summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErwin Hoffmann <feh@fehcom.de>2023-09-21 17:36:16 +0200
committerErwin Hoffmann <feh@fehcom.de>2023-09-21 17:36:16 +0200
commit44388ac49531af9e2565f76ef99ff7afb757b3fb (patch)
tree4eeb294db5bc3dbd075d0df5fea13c664cc331e2
parent889d69a87d51c8df531885cf1ac3d12d64a0cff7 (diff)
all sources
-rw-r--r--src/Makefile408
-rw-r--r--src/TARGETS76
-rw-r--r--src/address.cc65
-rw-r--r--src/argparser.cc322
-rw-r--r--src/authenticate.cc358
-rw-r--r--src/base64.cc121
-rw-r--r--src/bincimap-up.cc16
-rw-r--r--src/bincimap-updatecache.cc61
-rw-r--r--src/bincimapd.cc21
-rw-r--r--src/broker.cc190
-rw-r--r--src/compare.dirs662
-rw-r--r--src/convert.cc114
-rw-r--r--src/depot.cc717
-rw-r--r--src/greeting.cc47
-rw-r--r--src/imapparser.cc379
-rwxr-xr-xsrc/imapserver.cc221
-rw-r--r--src/include/address.h29
-rw-r--r--src/include/argparser.h69
-rw-r--r--src/include/authenticate.h18
-rw-r--r--src/include/base64.h18
-rw-r--r--src/include/broker.h84
-rw-r--r--src/include/convert.h298
-rw-r--r--src/include/depot.h154
-rw-r--r--src/include/globals.h25
-rw-r--r--src/include/imapparser.h171
-rw-r--r--src/include/imapserver.h37
-rw-r--r--src/include/iodevice.h382
-rw-r--r--src/include/iofactory.h53
-rw-r--r--src/include/mailbox.h136
-rw-r--r--src/include/maildir.h190
-rw-r--r--src/include/maildirmessage.h309
-rw-r--r--src/include/message.h151
-rw-r--r--src/include/mime-inputsource.h141
-rw-r--r--src/include/mime-utils.h27
-rw-r--r--src/include/mime.h121
-rw-r--r--src/include/multilogdevice.h29
-rw-r--r--src/include/operators.h476
-rw-r--r--src/include/pendingupdates.h107
-rw-r--r--src/include/recursivedescent.h64
-rw-r--r--src/include/regmatch.h17
-rw-r--r--src/include/session.h116
-rw-r--r--src/include/status.h50
-rw-r--r--src/include/stdiodevice.h31
-rw-r--r--src/include/syslogdevice.h41
-rw-r--r--src/include/tools.h23
-rw-r--r--src/iodevice.cc283
-rw-r--r--src/iofactory.cc66
-rw-r--r--src/mailbox.cc112
-rw-r--r--src/maildir-close.cc46
-rw-r--r--src/maildir-create.cc78
-rw-r--r--src/maildir-delete.cc88
-rw-r--r--src/maildir-expunge.cc61
-rw-r--r--src/maildir-readcache.cc151
-rw-r--r--src/maildir-scan.cc476
-rw-r--r--src/maildir-scanfilesnames.cc53
-rw-r--r--src/maildir-select.cc45
-rw-r--r--src/maildir-updateflags.cc115
-rw-r--r--src/maildir-writecache.cc74
-rw-r--r--src/maildir.cc839
-rw-r--r--src/maildirmessage.cc1153
-rw-r--r--src/mime-getpart.cc69
-rw-r--r--src/mime-parsefull.cc603
-rw-r--r--src/mime-parseonlyheader.cc146
-rw-r--r--src/mime-printbody.cc51
-rw-r--r--src/mime-printdoc.cc47
-rw-r--r--src/mime-printheader.cc172
-rw-r--r--src/mime.cc134
-rw-r--r--src/multilogdevice.cc86
-rw-r--r--src/operator-append.cc297
-rw-r--r--src/operator-authenticate.cc315
-rw-r--r--src/operator-capability.cc100
-rw-r--r--src/operator-check.cc71
-rw-r--r--src/operator-close.cc71
-rw-r--r--src/operator-copy.cc169
-rw-r--r--src/operator-create.cc84
-rw-r--r--src/operator-delete.cc84
-rw-r--r--src/operator-examine.cc26
-rw-r--r--src/operator-expunge.cc73
-rw-r--r--src/operator-fetch.cc641
-rw-r--r--src/operator-id.cc71
-rw-r--r--src/operator-id.obin0 -> 15952 bytes
-rw-r--r--src/operator-idle.cc239
-rw-r--r--src/operator-list.cc252
-rw-r--r--src/operator-login.cc134
-rw-r--r--src/operator-logout.cc86
-rw-r--r--src/operator-lsub.cc238
-rw-r--r--src/operator-namespace.cc79
-rw-r--r--src/operator-noop-pending.cc44
-rw-r--r--src/operator-noop.cc65
-rw-r--r--src/operator-rename.cc119
-rw-r--r--src/operator-search.cc898
-rw-r--r--src/operator-select.cc158
-rw-r--r--src/operator-starttls.cc115
-rw-r--r--src/operator-status.cc152
-rw-r--r--src/operator-store.cc194
-rw-r--r--src/operator-subscribe.cc84
-rw-r--r--src/operator-unsubscribe.cc91
-rw-r--r--src/pendingupdates.cc265
-rw-r--r--src/recursivedescent.cc1055
-rw-r--r--src/regmatch.cc31
-rw-r--r--src/session-initialize-bincimap-up.cc137
-rw-r--r--src/session-initialize-bincimapd.cc213
-rw-r--r--src/session.cc241
-rw-r--r--src/status.cc20
-rw-r--r--src/stdiodevice.cc111
-rw-r--r--src/syslogdevice.cc75
-rw-r--r--src/tools.cc44
107 files changed, 19235 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..de9d16f
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,408 @@
+# Don't edit Makefile! Use ../conf-* for configuration.
+
+SHELL=/bin/sh
+
+CXX = c++
+CXXFLAGS = -Og -Iinclude -Wall
+#LIBDL = -ldl
+
+bincimapd_OBJECTS = bincimapd.o address.o argparser.o authenticate.o base64.o \
+ broker.o convert.o depot.o iodevice.o iofactory.o \
+ imapparser.o imapserver.o mailbox.o maildir.o maildir-close.o \
+ maildir-create.o maildir-delete.o maildir-expunge.o maildir-readcache.o \
+ maildir-scan.o maildir-scanfilesnames.o maildir-select.o \
+ maildir-updateflags.o maildir-writecache.o maildirmessage.o mime.o \
+ mime-getpart.o mime-parsefull.o mime-parseonlyheader.o mime-printbody.o \
+ mime-printdoc.o mime-printheader.o multilogdevice.o \
+ operator-authenticate.o operator-capability.o operator-idle.o \
+ operator-id.o operator-noop.o operator-noop-pending.o operator-login.o \
+ operator-logout.o operator-append.o operator-examine.o \
+ operator-select.o operator-create.o operator-delete.o operator-list.o \
+ operator-lsub.o operator-namespace.o operator-rename.o \
+ operator-status.o operator-subscribe.o operator-unsubscribe.o \
+ operator-check.o operator-close.o operator-copy.o operator-expunge.o \
+ operator-fetch.o operator-search.o operator-store.o pendingupdates.o \
+ recursivedescent.o regmatch.o session.o session-initialize-bincimapd.o \
+ status.o stdiodevice.o syslogdevice.o tools.o
+
+bincimap_up_OBJECTS = bincimap-up.o argparser.o authenticate.o base64.o \
+ broker.o convert.o greeting.o imapparser.o imapserver.o iodevice.o \
+ iofactory.o multilogdevice.o operator-authenticate.o \
+ operator-capability.o operator-id.o operator-noop.o operator-login.o \
+ operator-logout.o operator-starttls.o recursivedescent.o \
+ session.o session-initialize-bincimap-up.o status.o stdiodevice.o \
+ syslogdevice.o tools.o
+
+bincimap_updatecache_OBJECTS = bincimap-updatecache.o address.o argparser.o \
+ convert.o depot.o imapparser.o iodevice.o \
+ iofactory.o mailbox.o maildir.o maildir-scan.o maildir-create.o \
+ maildir-close.o maildir-expunge.o maildir-readcache.o \
+ maildir-scanfilesnames.o maildir-select.o maildir-updateflags.o \
+ maildir-writecache.o maildir-delete.o maildirmessage.o mime.o \
+ mime-parsefull.o mime-parseonlyheader.o mime-printdoc.o \
+ mime-printbody.o mime-printheader.o mime-getpart.o pendingupdates.o \
+ session.o status.o
+
+.PHONY: default
+default: it-base
+it-base: bincimapd bincimap-updatecache bincimap-up
+
+bincimapd: $(bincimapd_OBJECTS)
+ $(CXX) $(CXXFLAGS) -o $@ $(bincimapd_OBJECTS)
+bincimap-up: $(bincimap_up_OBJECTS)
+ $(CXX) $(CXXFLAGS) -o $@ $(bincimap_up_OBJECTS)
+bincimap-updatecache: $(bincimap_updatecache_OBJECTS)
+ $(CXX) $(CXXFLAGS) -o $@ $(bincimap_updatecache_OBJECTS)
+
+.SUFFIXES:
+.SUFFIXES: .cc .o
+
+.cc.o:
+ $(CXX) $(CXXFLAGS) -o $@ -c $<
+
+.PHONY: clean
+clean:
+ rm -f `cat TARGETS`
+
+# Header depends
+address.o: address.cc include/address.h include/convert.h include/depot.h
+
+argparser.o: argparser.cc include/argparser.h include/convert.h \
+ include/address.h include/depot.h
+
+authenticate.o: authenticate.cc include/authenticate.h include/depot.h \
+ include/iodevice.h include/convert.h include/address.h \
+ include/iofactory.h include/session.h include/argparser.h \
+ include/globals.h
+
+base64.o: base64.cc include/base64.h
+
+bincimap-up.o: bincimap-up.cc include/imapserver.h
+
+bincimap-updatecache.o: bincimap-updatecache.cc include/depot.h \
+ include/mailbox.h include/imapparser.h include/maildir.h \
+ include/maildirmessage.h include/message.h include/address.h \
+ include/mime.h include/session.h include/argparser.h
+
+bincimapd.o: bincimapd.cc include/imapserver.h
+
+broker.o: broker.cc include/broker.h include/depot.h include/operators.h \
+ include/imapparser.h include/message.h include/convert.h \
+ include/address.h include/recursivedescent.h include/session.h \
+ include/argparser.h
+
+convert.o: convert.cc include/convert.h include/address.h include/depot.h
+
+depot.o: depot.cc include/depot.h include/mailbox.h include/imapparser.h \
+ include/status.h include/convert.h include/address.h \
+ include/iodevice.h include/iofactory.h
+
+greeting.o: greeting.cc include/iodevice.h include/convert.h \
+ include/address.h include/depot.h include/iofactory.h \
+ include/session.h include/argparser.h include/globals.h
+
+imapparser.o: imapparser.cc include/imapparser.h include/convert.h \
+ include/address.h include/depot.h
+
+imapserver.o: imapserver.cc include/broker.h include/depot.h \
+ include/operators.h include/imapparser.h include/message.h \
+ include/globals.h include/imapserver.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/session.h include/argparser.h
+
+iodevice.o: iodevice.cc include/iodevice.h include/convert.h \
+ include/address.h include/depot.h include/session.h \
+ include/argparser.h
+
+iofactory.o: iofactory.cc include/iofactory.h include/iodevice.h \
+ include/convert.h include/address.h include/depot.h
+
+mailbox.o: mailbox.cc include/mailbox.h include/imapparser.h \
+ include/message.h
+
+maildir-close.o: maildir-close.cc include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/address.h include/mime.h
+
+maildir-create.o: maildir-create.cc include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/address.h include/mime.h
+
+maildir-delete.o: maildir-delete.cc include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/address.h include/mime.h
+
+maildir-expunge.o: maildir-expunge.cc include/iodevice.h \
+ include/convert.h include/address.h include/depot.h \
+ include/iofactory.h include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/mime.h
+
+maildir-readcache.o: maildir-readcache.cc include/maildir.h \
+ include/mailbox.h include/imapparser.h include/maildirmessage.h \
+ include/message.h include/address.h include/mime.h include/convert.h \
+ include/depot.h include/globals.h
+
+maildir-scan.o: maildir-scan.cc include/iodevice.h include/convert.h \
+ include/address.h include/depot.h include/iofactory.h \
+ include/maildir.h include/mailbox.h include/imapparser.h \
+ include/maildirmessage.h include/message.h include/mime.h
+
+maildir-scanfilesnames.o: maildir-scanfilesnames.cc include/maildir.h \
+ include/mailbox.h include/imapparser.h include/maildirmessage.h \
+ include/message.h include/address.h include/mime.h include/iodevice.h \
+ include/convert.h include/depot.h include/iofactory.h
+
+maildir-select.o: maildir-select.cc include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/address.h include/mime.h
+
+maildir-updateflags.o: maildir-updateflags.cc include/maildir.h \
+ include/mailbox.h include/imapparser.h include/maildirmessage.h \
+ include/message.h include/address.h include/mime.h include/iodevice.h \
+ include/convert.h include/depot.h include/iofactory.h
+
+maildir-writecache.o: maildir-writecache.cc include/globals.h \
+ include/maildir.h include/mailbox.h include/imapparser.h \
+ include/maildirmessage.h include/message.h include/address.h \
+ include/mime.h
+
+maildir.o: maildir.cc include/convert.h include/address.h include/depot.h \
+ include/iodevice.h include/iofactory.h include/maildir.h \
+ include/mailbox.h include/imapparser.h include/maildirmessage.h \
+ include/message.h include/mime.h include/pendingupdates.h \
+ include/session.h include/argparser.h include/status.h \
+ include/globals.h
+
+maildirmessage.o: maildirmessage.cc include/maildir.h include/mailbox.h \
+ include/imapparser.h include/maildirmessage.h include/message.h \
+ include/address.h include/mime.h include/convert.h include/depot.h \
+ include/iodevice.h include/iofactory.h include/mime-utils.h \
+ include/mime-inputsource.h
+
+mime-getpart.o: mime-getpart.cc include/mime.h include/convert.h \
+ include/address.h include/depot.h
+
+mime-parsefull.o: mime-parsefull.cc include/mime.h include/mime-utils.h \
+ include/mime-inputsource.h include/convert.h include/address.h \
+ include/depot.h
+
+mime-parseonlyheader.o: mime-parseonlyheader.cc include/mime.h \
+ include/mime-utils.h include/mime-inputsource.h include/convert.h \
+ include/address.h include/depot.h
+
+mime-printbody.o: mime-printbody.cc include/mime.h include/mime-utils.h \
+ include/mime-inputsource.h include/convert.h include/address.h \
+ include/depot.h include/iodevice.h include/iofactory.h
+
+mime-printdoc.o: mime-printdoc.cc include/mime.h include/mime-utils.h \
+ include/mime-inputsource.h include/convert.h include/address.h \
+ include/depot.h include/iodevice.h include/iofactory.h
+
+mime-printheader.o: mime-printheader.cc include/mime.h \
+ include/mime-utils.h include/mime-inputsource.h include/convert.h \
+ include/address.h include/depot.h include/iodevice.h \
+ include/iofactory.h
+
+mime.o: mime.cc include/mime.h include/convert.h include/address.h \
+ include/depot.h
+
+multilogdevice.o: multilogdevice.cc include/multilogdevice.h \
+ include/iodevice.h include/convert.h include/address.h include/depot.h
+
+operator-append.o: operator-append.cc include/depot.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/recursivedescent.h include/pendingupdates.h \
+ include/session.h include/argparser.h
+
+operator-authenticate.o: operator-authenticate.cc include/authenticate.h \
+ include/depot.h include/base64.h include/convert.h include/address.h \
+ include/iodevice.h include/iofactory.h include/globals.h \
+ include/operators.h include/imapparser.h include/message.h \
+ include/recursivedescent.h include/session.h include/argparser.h
+
+operator-capability.o: operator-capability.cc include/depot.h \
+ include/iodevice.h include/convert.h include/address.h \
+ include/iofactory.h include/operators.h include/imapparser.h \
+ include/message.h include/recursivedescent.h include/session.h \
+ include/argparser.h include/globals.h
+
+operator-check.o: operator-check.cc include/depot.h include/mailbox.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/recursivedescent.h include/pendingupdates.h include/session.h \
+ include/argparser.h
+
+operator-close.o: operator-close.cc include/depot.h include/mailbox.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/recursivedescent.h include/session.h include/argparser.h
+
+operator-copy.o: operator-copy.cc include/depot.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/maildir.h include/mailbox.h include/imapparser.h \
+ include/maildirmessage.h include/message.h include/mime.h \
+ include/operators.h include/recursivedescent.h include/session.h \
+ include/argparser.h
+
+operator-create.o: operator-create.cc include/depot.h include/mailbox.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/recursivedescent.h include/session.h include/argparser.h \
+ include/convert.h include/address.h
+
+operator-delete.o: operator-delete.cc include/depot.h include/mailbox.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/recursivedescent.h include/session.h include/argparser.h \
+ include/convert.h include/address.h
+
+operator-examine.o: operator-examine.cc include/operators.h \
+ include/imapparser.h include/depot.h include/message.h
+
+operator-expunge.o: operator-expunge.cc include/depot.h include/mailbox.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/recursivedescent.h include/pendingupdates.h include/session.h \
+ include/argparser.h
+
+operator-fetch.o: operator-fetch.cc include/depot.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/pendingupdates.h include/recursivedescent.h \
+ include/session.h include/argparser.h
+
+operator-id.o: operator-id.cc include/depot.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/operators.h include/imapparser.h include/message.h \
+ include/recursivedescent.h include/session.h include/argparser.h \
+ include/globals.h
+
+operator-idle.o: operator-idle.cc include/iodevice.h include/convert.h \
+ include/address.h include/depot.h include/iofactory.h \
+ include/globals.h include/mailbox.h include/imapparser.h \
+ include/operators.h include/message.h include/pendingupdates.h \
+ include/recursivedescent.h include/session.h include/argparser.h
+
+operator-list.o: operator-list.cc include/convert.h include/address.h \
+ include/depot.h include/iodevice.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/recursivedescent.h include/regmatch.h \
+ include/session.h include/argparser.h
+
+operator-login.o: operator-login.cc include/authenticate.h \
+ include/depot.h include/iodevice.h include/convert.h include/address.h \
+ include/iofactory.h include/globals.h include/operators.h \
+ include/imapparser.h include/message.h include/recursivedescent.h \
+ include/session.h include/argparser.h
+
+operator-logout.o: operator-logout.cc include/iodevice.h \
+ include/convert.h include/address.h include/depot.h \
+ include/iofactory.h include/mailbox.h include/imapparser.h \
+ include/recursivedescent.h include/operators.h include/message.h \
+ include/session.h include/argparser.h
+
+operator-lsub.o: operator-lsub.cc include/convert.h include/address.h \
+ include/depot.h include/iodevice.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/recursivedescent.h include/regmatch.h \
+ include/session.h include/argparser.h
+
+operator-namespace.o: operator-namespace.cc include/depot.h \
+ include/iodevice.h include/convert.h include/address.h \
+ include/iofactory.h include/operators.h include/imapparser.h \
+ include/message.h include/recursivedescent.h include/session.h \
+ include/argparser.h
+
+operator-noop-pending.o: operator-noop-pending.cc include/mailbox.h \
+ include/imapparser.h include/pendingupdates.h \
+ include/recursivedescent.h include/operators.h include/depot.h \
+ include/message.h include/session.h include/argparser.h
+
+operator-noop.o: operator-noop.cc include/recursivedescent.h \
+ include/imapparser.h include/operators.h include/depot.h \
+ include/message.h include/session.h include/argparser.h
+
+operator-logout.o: operator-logout.cc include/iodevice.h \
+ include/depot.h include/mailbox.h include/imapparser.h \
+ include/operators.h include/message.h include/recursivedescent.h \
+ include/session.h include/argparser.h
+
+operator-search.o: operator-search.cc include/convert.h include/address.h \
+ include/depot.h include/imapparser.h include/iodevice.h \
+ include/iofactory.h include/mailbox.h include/mime.h \
+ include/operators.h include/message.h include/recursivedescent.h \
+ include/session.h include/argparser.h
+
+operator-select.o: operator-select.cc include/depot.h include/iodevice.h \
+ include/convert.h include/address.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/recursivedescent.h include/pendingupdates.h \
+ include/session.h include/argparser.h
+
+operator-starttls.o: operator-starttls.cc include/recursivedescent.h \
+ include/imapparser.h include/operators.h include/depot.h \
+ include/message.h include/iodevice.h include/convert.h \
+ include/address.h include/iofactory.h include/session.h \
+ include/argparser.h
+
+operator-status.o: operator-status.cc include/convert.h include/address.h \
+ include/depot.h include/iodevice.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/operators.h \
+ include/message.h include/recursivedescent.h include/session.h \
+ include/argparser.h include/status.h
+
+operator-store.o: operator-store.cc include/depot.h include/imapparser.h \
+ include/mailbox.h include/operators.h include/message.h \
+ include/pendingupdates.h include/recursivedescent.h include/session.h \
+ include/argparser.h
+
+operator-subscribe.o: operator-subscribe.cc include/convert.h \
+ include/address.h include/depot.h include/recursivedescent.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/session.h include/argparser.h
+
+operator-unsubscribe.o: operator-unsubscribe.cc include/convert.h \
+ include/address.h include/depot.h include/recursivedescent.h \
+ include/imapparser.h include/operators.h include/message.h \
+ include/session.h include/argparser.h
+
+pendingupdates.o: pendingupdates.cc include/iodevice.h include/convert.h \
+ include/address.h include/depot.h include/iofactory.h \
+ include/mailbox.h include/imapparser.h include/message.h \
+ include/pendingupdates.h include/session.h include/argparser.h
+
+recursivedescent.o: recursivedescent.cc include/imapparser.h \
+ include/recursivedescent.h include/operators.h include/depot.h \
+ include/message.h include/iodevice.h include/convert.h \
+ include/address.h include/iofactory.h include/session.h \
+ include/argparser.h
+
+regmatch.o: regmatch.cc include/regmatch.h
+
+session-initialize-bincimap-up.o: session-initialize-bincimap-up.cc \
+ include/broker.h include/depot.h include/operators.h \
+ include/imapparser.h include/message.h include/convert.h \
+ include/address.h include/globals.h include/iodevice.h \
+ include/iofactory.h include/multilogdevice.h include/syslogdevice.h \
+ include/stdiodevice.h include/session.h include/argparser.h \
+ include/tools.h
+
+session-initialize-bincimapd.o: session-initialize-bincimapd.cc \
+ include/broker.h include/depot.h include/operators.h \
+ include/imapparser.h include/message.h include/maildir.h \
+ include/mailbox.h include/maildirmessage.h include/address.h \
+ include/mime.h include/globals.h include/iodevice.h include/convert.h \
+ include/iofactory.h include/multilogdevice.h include/session.h \
+ include/argparser.h include/stdiodevice.h include/syslogdevice.h \
+ include/tools.h
+
+session.o: session.cc include/argparser.h include/convert.h \
+ include/address.h include/depot.h include/globals.h include/session.h \
+ include/tools.h
+
+status.o: status.cc include/status.h
+
+stdiodevice.o: stdiodevice.cc include/stdiodevice.h include/iodevice.h \
+ include/convert.h include/address.h include/depot.h
+
+syslogdevice.o: syslogdevice.cc include/syslogdevice.h include/iodevice.h \
+ include/convert.h include/address.h include/depot.h
+
+tools.o: tools.cc include/tools.h
diff --git a/src/TARGETS b/src/TARGETS
new file mode 100644
index 0000000..e286796
--- /dev/null
+++ b/src/TARGETS
@@ -0,0 +1,76 @@
+address.o
+argparser.o
+authenticate.o
+base64.o
+bincimap-up.o
+bincimap-up
+bincimapd.o
+bincimapd
+bincimap-updatecache.o
+bincimap-updatecache
+broker.o
+convert.o
+depot.o
+greeting.o
+imapparser.o
+imapserver.o
+iodevice.o
+iofactory.o
+mailbox.o
+maildir-close.o
+maildir-create.o
+maildir-delete.o
+maildir-expunge.o
+maildir-readcache.o
+maildir-scan.o
+maildir-scanfilesnames.o
+maildir-select.o
+maildir-updateflags.o
+maildir-writecache.o
+maildir.o
+maildirmessage.o
+mime-getpart.o
+mime-parsefull.o
+mime-parseonlyheader.o
+mime-printbody.o
+mime-printdoc.o
+mime-printheader.o
+mime.o
+multilogdevice.o
+operator-append.o
+operator-authenticate.o
+operator-capability.o
+operator-check.o
+operator-close.o
+operator-copy.o
+operator-create.o
+operator-delete.o
+operator-examine.o
+operator-expunge.o
+operator-fetch.o
+operator-idle.o
+operator-list.o
+operator-login.o
+operator-logout.o
+operator-lsub.o
+operator-namespace.o
+operator-noop-pending.o
+operator-noop.o
+operator-rename.o
+operator-search.o
+operator-select.o
+operator-starttls.o
+operator-status.o
+operator-store.o
+operator-subscribe.o
+operator-unsubscribe.o
+pendingupdates.o
+recursivedescent.o
+regmatch.o
+session-initialize-bincimap-up.o
+session-initialize-bincimapd.o
+session.o
+status.o
+stdiodevice.o
+syslogdevice.o
+tools.o
diff --git a/src/address.cc b/src/address.cc
new file mode 100644
index 0000000..7739500
--- /dev/null
+++ b/src/address.cc
@@ -0,0 +1,65 @@
+/** -------------------------------------------------------------------
+ * @file address.cc
+ * @brief Implementation of the Address class
+ * @author Andreas Aardal Hanssen
+ * @date 2005
+ * ---------------------------------------------------------------- **/
+#include "address.h"
+#include "convert.h"
+#include <string>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Address::Address(const string &name, const string &addr)
+{
+ string::size_type pos = addr.find('@');
+ this->name = name;
+ if (pos != string::npos) {
+ this->local = addr.substr(0, pos);
+ this->host = addr.substr(pos + 1);
+ } else this->local = addr;
+}
+
+//------------------------------------------------------------------------
+Address::Address(const string &wholeaddress)
+{
+ string::size_type start = wholeaddress.find('<');
+ string addr;
+ if (start != string::npos)
+ addr = wholeaddress.substr(start + 1);
+ else
+ addr = wholeaddress;
+
+ trim(addr, "<>");
+
+ if (start != string::npos)
+ name = wholeaddress.substr(0, start);
+ else
+ name = "";
+ trim(name);
+ trim(name, "\"");
+
+ start = addr.find('@');
+ local = addr.substr(0, start);
+ host = addr.substr(start + 1);
+
+ trim(local);
+ trim(host);
+ trim(name);
+}
+
+//------------------------------------------------------------------------
+string Address::toParenList(void) const
+{
+ string tmp = "(";
+ tmp += name == "" ? "NIL" : toImapString(name);
+ tmp += " NIL ";
+ tmp += local == "" ? "\"\"" : toImapString(local);
+ tmp += " ";
+ tmp += host == "" ? "\"\"" : toImapString(host);
+ tmp += ")";
+
+ return tmp;
+}
diff --git a/src/argparser.cc b/src/argparser.cc
new file mode 100644
index 0000000..c8b482c
--- /dev/null
+++ b/src/argparser.cc
@@ -0,0 +1,322 @@
+/** --------------------------------------------------------------------
+ * @file argparser.cc
+ * @brief Implementation of the command line argument parser
+ * @author Andreas Aardal Hanssen
+ * @date 2005
+ * ------------------------------------------------------------------ **/
+#include "argparser.h"
+#include "convert.h"
+
+#include <string>
+#include <map>
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CommandLineArgs::CommandLineArgs()
+{
+ errString = "Unknown error for args";
+ ac = 0;
+}
+
+//----------------------------------------------------------------------
+bool CommandLineArgs::parse(int argc, char *argv[])
+{
+ ac = -1;
+ head = argv[0];
+ head += " <options> --\n";
+
+ if (argc > 1) {
+ string lastKey;
+ bool lastIsBoolean = false;
+
+ for (int i = 1; i < argc; ++i) {
+ string s = argv[i];
+ if (s.length() < 2) {
+ unqualified.push_back(s);
+ continue;
+ }
+
+ if (s[0] != '-') {
+ // read value of last argument
+ if (lastKey == "") {
+ unqualified.push_back(s);
+ continue;
+ }
+
+ if (lastIsBoolean && (s != "yes" && s != "no")) {
+ errString = "syntax error: " + s;
+ errString += " (expected yes or no)";
+ return false;
+ }
+
+ args[lastKey] = s;
+ passedArgs[lastKey] = true;
+ lastKey = "";
+ lastIsBoolean = false;
+ } else if (s[1] == '-') {
+ if (lastKey != "") {
+ if (lastIsBoolean) {
+ args[lastKey] = "yes";
+ passedArgs[lastKey] = true;
+ lastKey = "";
+ lastIsBoolean = false;
+ } else {
+ errString = "expected value of ";
+ errString += lastKey;
+ return false;
+ }
+ }
+
+ // break if '--' is detected
+ if (s.length() == 2) {
+ ac = i + 1;
+ break;
+ }
+
+ // parse --argument
+ string arg = s.substr(2);
+ string val;
+ string::size_type epos = arg.find('=');
+ if (epos != string::npos) {
+ val = arg.substr(epos + 1);
+ arg = arg.substr(0, epos);
+ }
+
+ if (reg.find(arg) == reg.end()) {
+ errString = "unrecognized argument: --" + arg;
+ return false;
+ }
+
+ if (reg.find(arg)->second.b) {
+ if (val != "" && val != "yes" && val != "no") {
+ errString = "syntax error: " + val;
+ errString += " (expected yes or no)";
+ return false;
+ } else if (val == "") {
+ val = "yes";
+ }
+ }
+
+ if (val == "") {
+ errString = "syntax error: " + arg;
+ errString += " (expected --" + arg + "=<str>)";
+ return false;
+ }
+
+ args[arg] = val;
+ passedArgs[arg] = true;
+
+ lastKey = "";
+ lastIsBoolean = false;
+ } else {
+ if (lastKey != "") {
+ if (lastIsBoolean) {
+ args[lastKey] = "yes";
+ passedArgs[lastKey] = true;
+ lastKey = "";
+ lastIsBoolean = false;
+ } else {
+ errString = "expected value of ";
+ errString += lastKey;
+ return false;
+ }
+ }
+
+ // parse -argument
+ string arg = s.substr(1);
+ if (arg.length() == 1) {
+ map<string, ArgOpts>::const_iterator it = reg.begin();
+ bool match = false;
+ for (; it != reg.end(); ++it) {
+ if (it->second.c.find(arg[0]) != string::npos) {
+ lastKey = it->first;
+ if (it->second.b) lastIsBoolean = true;
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ errString = "unrecognized argument: -";
+ errString += arg[0];
+ return false;
+ }
+ } else {
+ string::const_iterator its = arg.begin();
+ for (; its != arg.end(); ++its) {
+ map<string, ArgOpts>::const_iterator it = reg.begin();
+ bool match = false;
+ for (; it != reg.end(); ++it) {
+ if (it->second.c.find(*its) != string::npos) {
+ if (!it->second.b) {
+ errString = "argument is not a boolean: ";
+ errString += "--" + it->first;
+ errString += " / -";
+ errString += it->second.c;
+ return false;
+ }
+
+ match = true;
+ args[it->first] = "yes";
+ passedArgs[it->first] = true;
+
+ lastKey = "";
+ lastIsBoolean = false;
+ break;
+ }
+ }
+
+ if (!match) {
+ errString = "unrecognized argument: ";
+ errString += s;
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if (lastKey != "") {
+ if (lastIsBoolean) {
+ args[lastKey] = "yes";
+ passedArgs[lastKey] = true;
+ } else {
+ errString = "expected value of ";
+ errString += lastKey;
+ return false;
+ }
+ }
+ }
+
+ // assign default "no" values for arguments that were not passed.
+ map<string, ArgOpts>::const_iterator it = reg.begin();
+ for (; it != reg.end(); ++it) {
+ if (args.find(it->first) == args.end()) {
+ if (!it->second.o) {
+ errString = "missing argument: ";
+ errString += it->first;
+ return false;
+ }
+ if (it->second.b) args[it->first] = "no";
+ }
+ }
+ if (ac == -1) ac = argc;
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+string CommandLineArgs::errorString(void) const
+{
+ return errString;
+}
+
+//----------------------------------------------------------------------
+const string CommandLineArgs::operator [](const string &arg) const
+{
+ if (args.find(arg) == args.end()) return "";
+ return args.find(arg)->second;
+}
+
+//----------------------------------------------------------------------
+void CommandLineArgs::addOptional(const string &arg, const string &desc,
+ bool boolean)
+{
+ registerArg(arg, desc, boolean, true);
+}
+
+//----------------------------------------------------------------------
+void CommandLineArgs::addRequired(const string &arg,
+ const string &desc, bool boolean)
+{
+ registerArg(arg, desc, boolean, false);
+}
+
+//----------------------------------------------------------------------
+void CommandLineArgs::registerArg(const string &arg, const string &desc,
+ bool boolean, bool optional)
+{
+ string name = arg;
+
+ string shorts;
+ while (name.size() > 1 && name[1] == '|') {
+ shorts += name[0];
+ name = name.substr(2);
+ }
+
+ reg.insert(make_pair(name, ArgOpts(shorts, boolean, optional, desc)));
+}
+
+//----------------------------------------------------------------------
+bool CommandLineArgs::hasArg(const std::string &arg) const
+{
+ string tmp = arg; lowercase(tmp);
+ return passedArgs.find(tmp) != passedArgs.end();
+}
+
+//----------------------------------------------------------------------
+string CommandLineArgs::usageString(void) const
+{
+ string tmp = head;
+ tmp += '\n';
+
+ map<string, ArgOpts>::const_iterator it = reg.begin();
+ for (; it != reg.end(); ++it) {
+ if (it->second.c != "") {
+ string::const_iterator sit = it->second.c.begin();
+ for (; sit != it->second.c.end(); ++sit) {
+ if (sit != it->second.c.begin()) tmp += '\n';
+ tmp += " -";
+ tmp += *sit;
+ }
+ tmp += ", ";
+ } else {
+ tmp += " ";
+ }
+
+ tmp += "--";
+ tmp += it->first;
+ if (!it->second.b) tmp += "=<str>";
+
+ if (!it->second.o) tmp += " (required)";
+
+ string::size_type lineStart = tmp.rfind('\n');
+ if (lineStart == string::npos) lineStart = 0;
+
+ int pad = 21 - (tmp.length() - lineStart);
+ if (pad < 0) {
+ tmp += '\n';
+ pad = 20;
+ }
+
+ tmp += string(pad, ' ');
+ tmp += it->second.desc;
+ tmp += '\n';
+ }
+
+ tmp += '\n';
+ tmp += tail;
+ tmp += '\n';
+
+ return tmp;
+}
+
+//----------------------------------------------------------------------
+int CommandLineArgs::argc(void) const
+{
+ return ac;
+}
+
+//----------------------------------------------------------------------
+void CommandLineArgs::setTail(const string &str)
+{
+ tail = str;
+}
+
+//----------------------------------------------------------------------
+const vector<string> &CommandLineArgs::getUnqualifiedArgs() const
+{
+ return unqualified;
+}
diff --git a/src/authenticate.cc b/src/authenticate.cc
new file mode 100644
index 0000000..a448238
--- /dev/null
+++ b/src/authenticate.cc
@@ -0,0 +1,358 @@
+/** --------------------------------------------------------------------
+ * @file authenticate.cc
+ * @brief Implementation of the common (C/R) authentication mechanism.
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <vector>
+
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+// #ifndef HAVE_SYS_WAIT_H
+// #include <wait.h>
+//#else
+#include <sys/wait.h>
+//#endif
+
+#include "authenticate.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "session.h"
+#include "convert.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+// 0 = ok
+// 1 = internal error
+// 2 = failed
+// 3 = timeout
+// -1 = abort
+//------------------------------------------------------------------------
+int Binc::authenticate(Depot &depot, const string &username,
+ const string &password, const string &challenge)
+{
+ Session &session = Session::getInstance();
+ session.setUserID(username);
+
+ // check if checkpassword is present
+ if (::access(session.unparsedArgs[0], X_OK) != 0) { // x is enough
+ bincError << "unable to start authenticator " << session.unparsedArgs[0]
+ << ": " << strerror(errno) << endl;
+ return 1;
+ }
+
+ // The information supplied on descriptor 3 is a login name
+ // terminated by \0, a password terminated by \0, a timestamp
+ // terminated by \0, and possibly more data. There are no other
+ // restrictions on the form of the login name, password, and
+ // timestamp.
+ int authintercom[2];
+ int intercomw[2];
+ int intercomr[2];
+ bool authenticated = false;
+
+ if (pipe(authintercom) == -1) {
+ session.setLastError("An error occurred when creating pipes: "
+ + string(strerror(errno)));
+ return -1;
+ }
+
+ if (pipe(intercomw) == -1) {
+ session.setLastError("An error occurred when creating pipes: "
+ + string(strerror(errno)));
+ close(authintercom[0]);
+ close(authintercom[1]);
+ return -1;
+ }
+
+ if (pipe(intercomr) == -1) {
+ session.setLastError("An error occurred when creating pipes: "
+ + string(strerror(errno)));
+ close(intercomw[0]);
+ close(intercomr[0]);
+ close(authintercom[0]);
+ close(authintercom[1]);
+ return -1;
+ }
+
+ string timestamp;
+ time_t t = time(0);
+ char *c;
+ if ((c = ctime(&t)) != 0) {
+ timestamp = c;
+ trim(timestamp);
+ } else
+ timestamp = "unknown timestamp";
+
+ string pid = to_string(session.getPid());
+
+ // execute authentication module
+ int result;
+ int childspid = fork();
+ if (childspid == -1) {
+ bincLog << "bincimap-up: pid " << pid
+ << " failed to start main server: "
+ << strerror(errno) << endl;
+ return 1;
+ }
+
+ if (childspid == 0) {
+ close(authintercom[1]);
+ close(intercomr[0]);
+ close(intercomw[1]);
+
+ if (dup2(intercomr[1], 1) == -1) {
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate(), [auth module] dup2 failed: "
+ << strerror(errno) << endl;
+ bincDebug.flush();
+ exit(111);
+ }
+
+ if (dup2(intercomw[0], 0) == -1) {
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate(), [auth module] dup2 failed: "
+ << strerror(errno) << endl;
+ bincDebug.flush();
+ exit(111);
+ }
+
+ if (dup2(authintercom[0], 3) == -1) {
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate(), [auth module] dup2 failed: "
+ << strerror(errno) << endl;
+ bincDebug.flush();
+ exit(111);
+ }
+
+ if (session.unparsedArgs[0] != 0) {
+ execvp(session.unparsedArgs[0], &session.unparsedArgs[0]);
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate(), [auth module] invocation of "
+ << session.unparsedArgs[0]
+ << " failed: " << strerror(errno) << endl;
+ bincDebug.flush();
+ exit(111);
+ }
+
+ bincLog << "bincimap-up: pid " << pid
+ << " missing mandatory -- in argument list,"
+ " after bincimap-up + arguments, before authenticator."
+ " Please check your run scripts and the man page bincimap(1) for"
+ " more on how to invoke Binc IMAP." << endl;
+ bincDebug.flush();
+ exit(111);
+ }
+
+ close(authintercom[0]);
+
+ // create the string of data to be passed to the checkpassword stub
+ int dataSize = username.length() + password.length() + challenge.length() + timestamp.length();
+ dataSize += 4;
+ char *checkpasswordData = new char[dataSize];
+ char *cpTmp = checkpasswordData;
+ strcpy(cpTmp, username.c_str());
+ cpTmp += username.length();
+ *cpTmp++ = '\0';
+ strcpy(cpTmp, password.c_str());
+ cpTmp += password.length();
+ *cpTmp++ = '\0';
+ // add challenge
+ strcpy(cpTmp, challenge.c_str());
+ cpTmp += challenge.length();
+ *cpTmp++ = '\0';
+ strcpy(cpTmp, timestamp.c_str());
+ cpTmp += timestamp.length();
+ *cpTmp++ = '\0';
+
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate(), writing username/password to "
+ << session.unparsedArgs[0] << endl;
+
+ // write the userid
+ signal(SIGPIPE, SIG_IGN);
+ int res = write(authintercom[1], checkpasswordData, dataSize);
+ delete[] checkpasswordData;
+ if (res != dataSize) {
+ bincWarning << "bincimap-up: pid " << pid
+ << " error writing to authenticator "
+ << session.unparsedArgs[0] << ": "
+ << strerror(errno) << endl;
+ return 1;
+ }
+
+ // close the write channel. this is necessary for the checkpassword
+ // module to see an EOF.
+ close(authintercom[1]);
+ close(intercomr[1]);
+ close(intercomw[0]);
+
+ fd_set rmask;
+ FD_ZERO(&rmask);
+ FD_SET(fileno(stdin), &rmask);
+ FD_SET(intercomr[0], &rmask);
+
+ int maxfd = intercomr[0];
+ bool disconnected = false;
+ bool timedout = false;
+ bincClient.clearFlags(IODevice::HasInputLimit);
+
+ bool eof = false;
+ while (!eof) {
+ fd_set rtmp = rmask;
+ struct timeval timeout;
+
+ // time out 5 minutes after the idle timeout. we expect the main
+ // server to time out at the right time, but will shut down at
+ // T+5m in case of a server lockup.
+ timeout.tv_sec = IDLE_TIMEOUT + AUTH_PENALTY * AUTH_TIMEOUT;
+ timeout.tv_usec = 0;
+
+ // select sometimes returns when we attach to the process with
+ // tracing tools such as ktrace and strace, setting errno to
+ // EINTR.
+ int n;
+ do {
+ n = select(maxfd + 1, &rtmp, 0, 0, &timeout);
+ } while (n < 0 && errno == EINTR);
+
+ if (n < 0) {
+ bincWarning << "bincimpa-up: pid " << pid
+ << " error: invalid exit from select, "
+ << strerror(errno) << endl;
+ break;
+ }
+
+ if (n == 0) {
+ bincLog << "bincimap-up: pid " << pid
+ << " server timed out after "
+ << IDLE_TIMEOUT << " seconds" << endl;
+ timedout = true;
+ break;
+ }
+
+ if (FD_ISSET(fileno(stdin), &rtmp)) {
+ authenticated = true;
+
+ do {
+ string data;
+ int ret = bincClient.readStr(&data);
+ if (ret == 0 || ret == -1) {
+ session.setLastError("client disconnected");
+ eof = true;
+ disconnected = true;
+ break;
+ }
+
+ // Fall through. Triggered when there was no data
+ // to read, even though no error has occurred
+ if (ret == -2) continue;
+
+ int w;
+ do {
+ w = write(intercomw[1], data.c_str(), data.length());
+ } while (w < 0 && errno == EINTR);
+
+ if (w > 0) Session::getInstance().addReadBytes(w);
+
+ if (w < 0) {
+ bincDebug << "bincimap-up: pid " << pid
+ << " error writing to server: "
+ << strerror(errno) << endl;
+ eof = true;
+ }
+ } while (bincClient.canRead());
+ }
+
+ if (FD_ISSET(intercomr[0], &rtmp)) {
+ char buf[8192];
+ int ret = read(intercomr[0], buf, sizeof(buf));
+ if (ret == 0) {
+ // Main server has shut down
+ eof = true;
+ break;
+ } else if (ret == -1) {
+ bincDebug << "bincimap-up: pid " << pid
+ << " error reading from server: "
+ << strerror(errno) << endl;
+ eof = true;
+ break;
+ } else {
+ // umask(0);
+ Session::getInstance().addWriteBytes(ret);
+
+ bincClient << string(buf, ret);
+ bincClient.flush();
+ }
+ }
+ }
+
+ close(intercomr[0]);
+ close(intercomw[1]);
+
+ // catch the dead baby
+ if (waitpid(childspid, &result, 0) != childspid) {
+ bincLog << "bincimap-up: pid " << pid
+ << " <" << username << "> authentication failed: "
+ << (authenticated ? "server " : session.unparsedArgs[0])
+ << " waitpid returned unexpected value" << endl;
+ string tmp = strerror(errno);
+
+ return -1;
+ }
+
+ // if the server died because we closed the sockets after a timeout,
+ // exit 3.
+ if (timedout) return 3;
+
+ if (disconnected) return 0;
+
+ if (WIFSIGNALED(result)) {
+ bincLog << "bincimap-up: pid " << pid
+ << " <" << username << "> authentication failed: "
+ << (authenticated ? "server" : session.unparsedArgs[0])
+ << " died by signal " << WTERMSIG(result) << endl;
+ sleep(AUTH_PENALTY);
+ session.setState(Session::LOGOUT);
+ return -1;
+ }
+
+ bincDebug << "bincimap-up: pid " << pid
+ << " authenticate() ,"
+ << (authenticated ? "authenticator" : "server")
+ << " exited with code " << WEXITSTATUS(result) << endl;
+
+ switch (WEXITSTATUS(result)) {
+ case 0: break;
+ case 1:
+ // authentication failed - sleep
+ bincLog << "bincimap-up: pid " << pid
+ << " <" << username << "> failed to log in" << endl;
+ sleep(AUTH_PENALTY);
+ return 2;
+ case 2: case 111: // wrong call or missing auth data
+ // abused
+ bincLog << "bincimap-up: pid " << pid
+ << " <" << username << "> authentication failed: "
+ << (authenticated ? "authenticator" : "server")
+ << " reports wrong usage" << endl;
+ return -1;
+ default:
+ // internal error -- or authenticator fooled us
+ bincLog << "bincimap-up: pid " << pid
+ << " <" << username << "> authentication failed: "
+ << (authenticated ? "authenticator" : "server")
+ << " returned " << WEXITSTATUS(result) << endl;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/base64.cc b/src/base64.cc
new file mode 100644
index 0000000..367a3e4
--- /dev/null
+++ b/src/base64.cc
@@ -0,0 +1,121 @@
+/** --------------------------------------------------------------------
+ * @file base64.cc
+ * @brief Implementation of base64 Utilities
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "base64.h"
+#include <string>
+#include <iostream>
+
+using namespace ::std;
+
+typedef unsigned char bbyte; /* Byte type */
+
+#define TRUE 1
+#define FALSE 0
+
+#define LINELEN 72 /* Encoded line length (max 76) */
+
+static bbyte dtable[256];
+
+string Binc::base64encode(const string &s_in)
+{
+ int i;
+ string result;
+
+ /* Fill dtable with character encodings. */
+
+ for (i = 0; i < 26; i++) {
+ dtable[i] = 'A' + i;
+ dtable[26 + i] = 'a' + i;
+ }
+ for (i = 0; i < 10; i++) {
+ dtable[52 + i] = '0' + i;
+ }
+ dtable[62] = '+';
+ dtable[63] = '/';
+
+ string::const_iterator s_i = s_in.begin();
+ while (s_i != s_in.end()) {
+
+ bbyte igroup[3], ogroup[4];
+ int c, n;
+
+ igroup[0] = igroup[1] = igroup[2] = 0;
+ for (n = 0; n < 3 && s_i != s_in.end(); n++) {
+ c = *s_i++;
+ igroup[n] = (bbyte) c;
+ }
+ if (n > 0) {
+ ogroup[0] = dtable[igroup[0] >> 2];
+ ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
+ ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
+ ogroup[3] = dtable[igroup[2] & 0x3F];
+
+ /* Replace characters in output stream with "=" pad
+ characters if fewer than three characters were
+ read from the end of the input stream. */
+
+ if (n < 3) {
+ ogroup[3] = '=';
+ if (n < 2) {
+ ogroup[2] = '=';
+ }
+ }
+
+ for (i = 0; i < 4; i++)
+ result += ogroup[i];
+ }
+ }
+
+ return result;
+}
+
+string Binc::base64decode(const string &s_in)
+{
+ string result;
+ int i;
+
+ for (i = 0; i < 255; i++) {
+ dtable[i] = 0x80;
+ }
+ for (i = 'A'; i <= 'Z'; i++) {
+ dtable[i] = 0 + (i - 'A');
+ }
+ for (i = 'a'; i <= 'z'; i++) {
+ dtable[i] = 26 + (i - 'a');
+ }
+ for (i = '0'; i <= '9'; i++) {
+ dtable[i] = 52 + (i - '0');
+ }
+ dtable[(int) '+'] = 62;
+ dtable[(int) '/'] = 63;
+ dtable[(int) '='] = 0;
+
+ /*CONSTANTCONDITION*/
+ string::const_iterator s_i = s_in.begin();
+ while (s_i != s_in.end()) {
+ bbyte a[4], b[4], o[3];
+
+ for (i = 0; i < 4 && s_i != s_in.end(); i++) {
+ int c = *s_i++;
+ if (dtable[c] & 0x80) return result;
+ a[i] = (bbyte) c;
+ b[i] = (bbyte) dtable[c];
+ }
+
+ o[0] = (b[0] << 2) | (b[1] >> 4);
+ o[1] = (b[1] << 4) | (b[2] >> 2);
+ o[2] = (b[2] << 6) | b[3];
+
+ i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+
+ for (int j = 0; j < i; ++j)
+ result += o[j];
+
+ if (i < 3) break;
+ }
+
+ return result;
+}
diff --git a/src/bincimap-up.cc b/src/bincimap-up.cc
new file mode 100644
index 0000000..77576de
--- /dev/null
+++ b/src/bincimap-up.cc
@@ -0,0 +1,16 @@
+/** --------------------------------------------------------------------
+ * @file bincimap-up.cc
+ * @brief Implementation of the preauthenticated bincimap stub
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include "imapserver.h"
+
+//------------------------------------------------------------------------
+int main(int argc, char *argv[])
+{
+ Binc::IMAPServer imapServer(argc, argv);
+ int initResult = imapServer.initialize();
+ if (initResult != 0) return initResult;
+ return imapServer.runStub();
+}
diff --git a/src/bincimap-updatecache.cc b/src/bincimap-updatecache.cc
new file mode 100644
index 0000000..4efe723
--- /dev/null
+++ b/src/bincimap-updatecache.cc
@@ -0,0 +1,61 @@
+/** --------------------------------------------------------------------
+ * @file bincimap-updatecache.cc
+ * @brief Implementation of the bincimap-updatecache tool.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "depot.h"
+#include "mailbox.h"
+#include "maildir.h"
+#include "session.h"
+
+using namespace ::Binc;
+using namespace ::std;
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <directory> ", argv[0]);
+ fprintf(stderr, "Updates the cache file in <directory>.\n");
+ fprintf(stderr, "Use export $DEPOT=\"IMAPdir\" to enable updates for this type.\n");
+ return 1;
+ }
+
+ Session &session = Session::getInstance();
+
+ DepotFactory &depotfactory = DepotFactory::getInstance();
+ depotfactory.assign(new IMAPdirDepot());
+ depotfactory.assign(new MaildirPPDepot());
+
+ string depottype = session.getEnv("DEPOT");
+ if (depottype == "") depottype = "Maildir++";
+
+ Depot *depot;
+ if ((depot = depotfactory.get(depottype)) == 0) {
+ fprintf(stderr, "Found no Depot for \"%s\". Please check " \
+ " your configurations file under the Mailbox section.\n",
+ depottype.c_str());
+ return 1;
+ }
+
+ depot->assign(new Maildir());
+ depot->setDefaultType("Maildir");
+
+ Mailbox *mailbox = depot->get(depot->filenameToMailbox(argv[1]));
+
+ if (!mailbox) {
+ fprintf(stderr, "selecting mailbox failed: %s\n",
+ depot->getLastError().c_str());
+ return 1;
+ }
+
+ if (!mailbox->selectMailbox(argv[1], argv[1])) {
+ fprintf(stderr, "selecting mailbox failed: %s\n",
+ mailbox->getLastError().c_str());
+ return 1;
+ }
+
+ mailbox->closeMailbox();
+
+ return 0;
+}
diff --git a/src/bincimapd.cc b/src/bincimapd.cc
new file mode 100644
index 0000000..d4d8508
--- /dev/null
+++ b/src/bincimapd.cc
@@ -0,0 +1,21 @@
+/** --------------------------------------------------------------------
+ * @file bincimapd.cc
+ * @brief Implementation of the main bincimapd service
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "imapserver.h"
+
+namespace Binc {
+ void showGreeting(void)
+ {
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ Binc::IMAPServer imapServer(argc, argv);
+ int initResult = imapServer.initialize();
+ if (initResult != 0) return initResult;
+ return imapServer.run();
+}
diff --git a/src/broker.cc b/src/broker.cc
new file mode 100644
index 0000000..9d7f728
--- /dev/null
+++ b/src/broker.cc
@@ -0,0 +1,190 @@
+/** ---------------------------------------------------------------------
+ * @file broker.cc
+ * @brief Implementation of the Broker class
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <map>
+#include <string>
+
+#include "broker.h"
+#include "convert.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+BrokerFactory::BrokerFactory(void)
+{
+ brokers[Session::NONAUTHENTICATED] = new Broker();
+ brokers[Session::AUTHENTICATED] = new Broker();
+ brokers[Session::SELECTED] = new Broker();
+}
+
+//----------------------------------------------------------------------
+BrokerFactory::~BrokerFactory(void)
+{
+ for (map<int, Broker *>::iterator i = brokers.begin();
+ i != brokers.end(); ++i)
+ delete i->second;
+}
+
+//----------------------------------------------------------------------
+BrokerFactory &BrokerFactory::getInstance(void)
+{
+ static BrokerFactory brokerfactory;
+ return brokerfactory;
+}
+
+//----------------------------------------------------------------------
+void BrokerFactory::addCapability(const std::string &c)
+{
+ for (map<int, Broker *>::iterator i = brokers.begin();
+ i != brokers.end(); ++i) {
+ CapabilityOperator * o;
+ o = dynamic_cast<CapabilityOperator*>(i->second->get("CAPABILITY"));
+ if (o != 0) {
+ o->addCapability(c);
+ break;
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+void BrokerFactory::assign(const string &fname, Operator *o)
+{
+ int deletable = true;
+ for (map<int, Broker *>::iterator i = brokers.begin();
+ i != brokers.end(); ++i)
+ if (i->first & o->getState()) {
+ i->second->assign(fname, o, deletable);
+ deletable = false;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator *BrokerFactory::getOperator(int state, const string &name) const
+{
+ if (brokers.find(state) == brokers.end())
+ return 0;
+ else
+ return brokers.find(state)->second->get(name);
+}
+
+//----------------------------------------------------------------------
+Broker *BrokerFactory::getBroker(int state)
+{
+ if (brokers.find(state) == brokers.end()) {
+ setLastError("No appropriate broker for state.");
+ return 0;
+ }
+
+ return brokers[state];
+}
+
+//----------------------------------------------------------------------
+Broker::Broker(void)
+{
+}
+
+//----------------------------------------------------------------------
+Broker::~Broker(void)
+{
+}
+
+//----------------------------------------------------------------------
+void Broker::assign(const string &fname, Operator *o, bool deletable)
+{
+ deletables[fname] = deletable;
+ operators[fname] = o;
+}
+
+//----------------------------------------------------------------------
+Operator *Broker::get(const string &name) const
+{
+ if (operators.find(name) == operators.end()) return 0;
+
+ return operators.find(name)->second;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Broker::parseStub(Request &command)
+{
+ Session &session = Session::getInstance();
+
+ string tag;
+ string cmd;
+
+ switch (expectTag(tag)) {
+ case Operator::ACCEPT:
+ break;
+ case Operator::REJECT:
+ session.setLastError("Syntax error; first token must be a tag");
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+
+ switch (expectSPACE()) {
+ case Operator::ACCEPT:
+ break;
+ case Operator::REJECT:
+ session.setLastError("Syntax error; second token must be a SPACE");
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+
+ switch (expectAstring(cmd)) {
+ case Operator::ACCEPT:
+ break;
+ case Operator::REJECT:
+ session.setLastError("Syntax error; third token must be a command");
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+
+ uppercase(cmd);
+
+ if (cmd == "UID") {
+ command.setUidMode();
+
+ switch (expectSPACE()) {
+ case Operator::ACCEPT:
+ break;
+ case Operator::REJECT:
+ session.setLastError("Syntax error; after UID there"
+ " must come a SPACE");
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+
+ switch (expectAstring(cmd)) {
+ case Operator::ACCEPT:
+ break;
+ case Operator::REJECT:
+ session.setLastError("Syntax error; after UID "
+ "SPACE there must come a command");
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+
+ uppercase(cmd);
+ }
+
+ command.setTag(tag);
+ command.setName(cmd);
+
+ return Operator::ACCEPT;
+}
diff --git a/src/compare.dirs b/src/compare.dirs
new file mode 100644
index 0000000..314b91a
--- /dev/null
+++ b/src/compare.dirs
@@ -0,0 +1,662 @@
+--- ./broker.cc 2023-08-22 22:04:58.667689000 +0200
++++ ../../bincimap-2.0.5/src//broker.cc 2023-08-14 12:04:05.654922000 +0200
+@@ -105,7 +105,8 @@
+ //----------------------------------------------------------------------
+ Operator *Broker::get(const string &name) const
+ {
+- if (operators.find(name) == operators.end()) return 0;
++ if (operators.find(name) == operators.end())
++ return 0;
+
+ return operators.find(name)->second;
+ }
+--- ./imapserver.cc 2023-08-22 11:30:32.929436000 +0200
++++ ../../bincimap-2.0.5/src//imapserver.cc 2023-08-19 15:30:18.579113000 +0200
+@@ -74,7 +74,6 @@
+ showGreeting();
+ } else {
+ bincInfo << "<" << session.getEnv("USER") << "> logged in" << "\n";
+- bincInfo << "<" << getenv("USER") << "> logged in" << "\n";
+ }
+ bincInfo.flush();
+
+--- ./iodevice.cc 2023-08-22 13:38:19.325053000 +0200
++++ ../../bincimap-2.0.5/src//iodevice.cc 2023-08-14 12:59:30.985424000 +0200
+@@ -40,11 +40,13 @@
+
+ static std::ostream &(*endl_funcptr)(ostream &) = endl;
+
+- if (source != endl_funcptr) return *this;
++ if (source != endl_funcptr)
++ return *this;
+
+ outputBuffer << "\r\n";
+
+- if (dumpfd) ::write(dumpfd, "\r\n", 2);
++ if (dumpfd)
++ ::write(dumpfd, "\r\n", 2);
+
+ if (flags & FlushesOnEndl)
+ flush();
+@@ -64,7 +66,8 @@
+ //------------------------------------------------------------------------
+ void IODevice::clear()
+ {
+- if (!(flags & IsEnabled)) return;
++ if (!(flags & IsEnabled))
++ return;
+
+ inputBuffer.clear();
+ outputBuffer.clear();
+@@ -73,20 +76,26 @@
+ //------------------------------------------------------------------------
+ bool IODevice::flush()
+ {
+- if (!(flags & IsEnabled)) return true;
++ if (!(flags & IsEnabled))
++ return true;
+
+ WriteResult writeResult = WriteWait;
+ do {
+ unsigned int s = outputBuffer.getSize();
+- if (s == 0) break;
+- if (!waitForWrite()) return false;
++ if (s == 0)
++ break;
++
++ if (!waitForWrite())
++ return false;
++
+ writeResult = write();
+- if (writeResult == WriteError) return false;
++ if (writeResult == WriteError)
++ return false;
++
+ writeCount += s - outputBuffer.getSize();
+ } while (outputBuffer.getSize() > 0 && writeResult == WriteWait);
+
+ outputBuffer.clear();
+-
+ return true;
+ }
+
+@@ -159,11 +168,13 @@
+ bool IODevice::readStr(string *dest, unsigned int max)
+ {
+ // If max is 0, fill the input buffer once only if it's empty.
+- if (!max && inputBuffer.getSize() == 0 && !fillInputBuffer()) return false;
++ if (!max && inputBuffer.getSize() == 0 && !fillInputBuffer())
++ return false;
+
+ // If max is != 0, wait until we have max.
+ while (max && inputBuffer.getSize() < max) {
+- if (!fillInputBuffer()) return false;
++ if (!fillInputBuffer())
++ return false;
+ }
+
+ unsigned int bytesToRead = max ? max : inputBuffer.getSize();
+@@ -174,21 +185,22 @@
+
+ inputBuffer.popString(bytesToRead);
+ readCount += bytesToRead;
+-
+ return true;
+ }
+
+ //------------------------------------------------------------------------
+ bool IODevice::readChar(char *dest)
+ {
+- if (inputBuffer.getSize() == 0 && !fillInputBuffer()) return false;
++ if (inputBuffer.getSize() == 0 && !fillInputBuffer())
++ return false;
+
+ char c = inputBuffer.popChar();
+- if (dest) *dest = c;
+- if (dumpfd) ::write(dumpfd, &c, 1);
++ if (dest)
++ *dest = c;
++ if (dumpfd)
++ ::write(dumpfd, &c, 1);
+
+ ++readCount;
+-
+ return true;
+ }
+
+@@ -209,8 +221,10 @@
+ {
+ char dest = '\0';
+ do {
+- if (!readChar(&dest)) return false;
+- if (dumpfd) ::write(dumpfd, &dest, 1);
++ if (!readChar(&dest))
++ return false;
++ if (dumpfd)
++ ::write(dumpfd, &dest, 1);
+ } while (c != dest);
+
+ return true;
+@@ -278,6 +292,7 @@
+ << Session::getInstance().getIP() << "-XXXXXX";
+ char *safename = strdup(ss.str().c_str());
+ dumpfd = mkstemp(safename);
+- if (dumpfd == -1) dumpfd = 0;
++ if (dumpfd == -1)
++ dumpfd = 0;
+ delete safename;
+ }
+--- ./operator-authenticate.cc 2023-08-24 10:14:07.627463000 +0200
++++ ../../bincimap-2.0.5/src//operator-authenticate.cc 2023-08-19 15:24:29.665376000 +0200
+@@ -166,11 +166,9 @@
+
+ putenv(strdup(("BINCIMAP_LOGIN=AUTHENTICATE+" + command.getTag()).c_str()));
+
+- // FEH: put the username in the environment for logging purpose
++ // put the username in the environment for logging purpose
+
+- // FIXME:
+- session.setEnv("USER", username.c_str());
+-// putenv(strdup(("USER=" + username).c_str()));
++ session.setEnv("USER", username.c_str());
+
+ // the authenticate function calls a stub which does the actual
+ // authentication. the function returns 0 (success), 1 (internal
+--- ./operator-capability.cc 2023-08-23 12:02:49.204839000 +0200
++++ ../../bincimap-2.0.5/src//operator-capability.cc 2023-08-21 15:12:32.915708000 +0200
+@@ -12,7 +12,6 @@
+ #include "operators.h"
+ #include "recursivedescent.h"
+ #include "session.h"
+-#include "globals.h"
+
+ using namespace ::std;
+ using namespace Binc;
+@@ -37,8 +36,8 @@
+ int CapabilityOperator::getState(void) const
+ {
+ return Session::NONAUTHENTICATED
+- | Session::AUTHENTICATED
+- | Session::SELECTED;
++ | Session::AUTHENTICATED
++ | Session::SELECTED;
+ }
+
+ //----------------------------------------------------------------------
+@@ -53,11 +52,10 @@
+ {
+ Session &session = Session::getInstance();
+
+- bincClient << "* CAPABILITY " << IMAP_VERSION ;
++ bincClient << "* CAPABILITY IMAP4rev1";
+
+ if (session.getState() == Session::NONAUTHENTICATED) {
+- if (getenv("UCSPITLS"))
+- if (!session.command.ssl) bincClient << " STARTTLS";
++ if (!session.command.ssl) bincClient << " STARTTLS";
+
+ if (session.command.ssl || session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS"))
+ bincClient << " AUTH=LOGIN AUTH=PLAIN";
+--- ./operator-fetch.cc 2023-08-22 11:28:00.260614000 +0200
++++ ../../bincimap-2.0.5/src//operator-fetch.cc 2023-08-21 11:54:04.569792000 +0200
+@@ -372,9 +372,9 @@
+
+ pendingUpdates(mailbox,
+ PendingUpdates::FLAGS
+- | PendingUpdates::EXISTS
+- | PendingUpdates::EXPUNGE
+- | PendingUpdates::RECENT, true);
++ | PendingUpdates::EXISTS
++ | PendingUpdates::EXPUNGE
++ | PendingUpdates::RECENT, true);
+
+ return OK;
+ }
+--- ./operator-id.cc 2023-08-23 22:43:11.362445000 +0200
++++ ../../bincimap-2.0.5/src//operator-id.cc 2023-08-21 15:05:56.720615000 +0200
+@@ -1,8 +1,8 @@
+ /** --------------------------------------------------------------------
+ * @file operator-id.cc
+- * @brief Operator for the ID extension. Described in RFC2971 Oct 2000.
++ * @brief Operator for the ID command.
+ * @author Erwin Hoffmann
+- * @date 22.09.2023
++ * @date 2023
+ * ------------------------------------------------------------------ **/
+ #include <string>
+ #include <iostream>
+@@ -46,10 +46,13 @@
+ Operator::ProcessResult IdOperator::process(Depot &depot,
+ Request &command)
+ {
+- bincClient << "* ID (\"name\" \"Binc IMAP\""
++ Session &session = Session::getInstance();
++
++ bincClient << " * ID (\"name\" \"Binc IMAP\""
+ << " \"version\" \"" << BINC_VERSION "\")" << endl;
++ bincClient.flush();
+
+- return NOTHING;
++ return OK;
+ }
+
+ //----------------------------------------------------------------------
+@@ -57,15 +60,13 @@
+ {
+ Session &session = Session::getInstance();
+
+- if (c_in.getUidMode()) return REJECT;
++ if (c_in.getUidMode()) return ACCEPT;
+
+-/* FIXME: We are not interested in the parsing result
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+-*/
+
+ c_in.setName("ID");
+
+--- ./operator-login.cc 2023-08-23 22:42:22.681346000 +0200
++++ ../../bincimap-2.0.5/src//operator-login.cc 2023-08-16 19:37:07.694225000 +0200
+@@ -48,7 +48,7 @@
+
+ //------------------------------------------------------------------------
+ Operator::ProcessResult LoginOperator::process(Depot &depot,
+- Request &command)
++ Request &command)
+ {
+ Session &session = Session::getInstance();
+
+@@ -79,7 +79,7 @@
+ break;
+ case -1:
+ bincClient << "* BYE The server died unexpectedly. Please contact "
+- "your system administrator for more information." << endl;
++ "your system administrator for more information." << endl;
+ break;
+ }
+
+--- ./operator-logout.cc 2023-08-22 13:41:49.546630000 +0200
++++ ../../bincimap-2.0.5/src//operator-logout.cc 2023-08-14 23:04:43.460303000 +0200
+@@ -41,8 +41,8 @@
+ int LogoutOperator::getState(void) const
+ {
+ return Session::NONAUTHENTICATED
+- | Session::AUTHENTICATED
+- | Session::SELECTED;
++ | Session::AUTHENTICATED
++ | Session::SELECTED;
+ }
+
+ //------------------------------------------------------------------------
+--- ./operator-noop.cc 2023-08-22 12:11:45.876657000 +0200
++++ ../../bincimap-2.0.5/src//operator-noop.cc 2023-08-15 15:05:11.493837000 +0200
+@@ -51,7 +51,8 @@
+ {
+ Session &session = Session::getInstance();
+
+- if (c_in.getUidMode()) return REJECT;
++ if (c_in.getUidMode())
++ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+--- ./operator-starttls.cc 2023-08-24 10:24:53.204617000 +0200
++++ ../../bincimap-2.0.5/src//operator-starttls.cc 2023-08-19 16:15:15.622679000 +0200
+@@ -1,13 +1,11 @@
+ /** --------------------------------------------------------------------
+ * @file operator-starttls.cc
+- * @brief Implementation of the STARTTLS command - based on sslserver
+- * @author Andreas Aardal Hanssen, Erwin Hoffmann
+- * @date 2002-2005, 2023
++ * @brief Implementation of the STARTTLS command.
++ * @author Andreas Aardal Hanssen
++ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+ #include <string>
+ #include <iostream>
+-#include <unistd.h>
+-#include <fcntl.h>
+
+ #include "recursivedescent.h"
+ #include "iodevice.h"
+@@ -43,37 +41,6 @@
+ | Session::SELECTED;
+ }
+
+-//----------------------------------------------------------------------
+-int StarttlsOperator::goStartTLS (void) const
+-{
+- Session &session = Session::getInstance();
+-
+- if (getenv("UCSPITLS")) {
+- string fdstr;
+- int fd;
+-
+- fdstr = session.getEnv("SSLCTLFD");
+- fd = std::stoi(fdstr);
+- if (write(fd,"Y",1) < 1) return NOTHING;
+-
+- fdstr = session.getEnv("SSLREADFD");
+- fd = std::stoi(fdstr);
+- if (fcntl(fd,F_GETFL,0) == -1) return NOTHING;
+- close (0);
+- if (fcntl(fd,F_DUPFD,0) == -1) return NOTHING;
+- close (fd);
+-
+- fdstr = session.getEnv("SSLWRITEFD");
+- fd = std::stoi(fdstr);
+- if (fcntl(fd,F_GETFL,0) == -1) return NOTHING;
+- close (1);
+- if (fcntl(fd,F_DUPFD,1) == -1) return NOTHING;
+- close (fd);
+- }
+-
+- return ACCEPT;
+-}
+-
+ //------------------------------------------------------------------------
+ Operator::ProcessResult StarttlsOperator::process(Depot &depot,
+ Request &command)
+@@ -84,13 +51,11 @@
+ return BAD;
+ }
+
+- bincClient << "OK STARTTLS completed, begin TLS session now" << endl;
++ bincClient << command.getTag()
++ << " OK STARTTLS completed, begin TLS negotiation now" << endl;
+ bincClient.flush();
+
+- if (goStartTLS() == ACCEPT)
+- session.command.ssl = true;
+- else
+- return NO;
++ session.command.ssl = true;
+
+ return NOTHING;
+ }
+@@ -100,7 +65,8 @@
+ {
+ Session &session = Session::getInstance();
+
+- if (c_in.getUidMode()) return REJECT;
++ if (c_in.getUidMode())
++ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+--- ./session-initialize-bincimap-up.cc 2023-08-24 10:30:35.277625000 +0200
++++ ../../bincimap-2.0.5/src//session-initialize-bincimap-up.cc 2023-08-21 15:50:02.457680000 +0200
+@@ -1,8 +1,8 @@
+ /** --------------------------------------------------------------------
+ * @file session-initialize-bincimap-up.cc
+- * @brief bincimap-up requires sslserver
+- * @author Andreas Aardal Hanssen, Erwin Hoffmann
+- * @date 2002-2005, 2023
++ * @brief <--->
++ * @author Andreas Aardal Hanssen
++ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+ #include <syslog.h>
+ #include <ctype.h>
+@@ -27,6 +27,15 @@
+
+ extern char **environ;
+
++namespace {
++ //------------------------------------------------------------------------
++ void usage(char *name)
++ {
++ printf("Please refer to the man pages for bincimap-up and bincimap\n");
++ printf("for more information about how to invoke Binc IMAP.\n");
++ }
++}
++
+ //----------------------------------------------------------------------
+ bool Session::initialize(int argc, char *argv[])
+ {
+@@ -51,26 +60,38 @@
+ return false;
+ }
+
+- // Show version if asked for it
++ // imaps (port 993) -- requires sslserver with option -e
++
++ int stls = 0;
++ string tlssession = session.getEnv("SSL_SESSION_ID");
++ trim(tlssession);
++ if (tlssession.size() > 2) {
++ session.command.ssl = true;
++ stls = -1 ;
++ }
++
++ // Show help if asked for it
+ if (session.command.version) {
+- printf("Binc IMAP v" BINC_VERSION"\n");
++ printf("Binc IMAP v" BINC_VERSION IMAP_VERSION "\n");
+ return false;
+ }
+
+ // Let the command line args override the global settings.
+ session.assignCommandLineArgs();
+
+- // for log input
++
++ // log settings
+ string ip = getenv("TCP6REMOTEIP") ? getenv("TCP6REMOTEIP") :
+ getenv("TCPREMOTEIP") ? getenv("TCPREMOTEIP") : "?";
+ session.setIP(ip);
+
++
+ string logtype = session.getEnv("LOG_TYPE");
+ lowercase(logtype);
+ trim(logtype);
+ if (logtype == "multilog" || logtype == "stderr") {
+- MultilogDevice *device = new MultilogDevice(IODevice::IsEnabled
+- | IODevice::FlushesOnEndl);
++ MultilogDevice *device = new MultilogDevice(IODevice::IsEnabled
++ | IODevice::FlushesOnEndl);
+ ioFactory.addDevice(device);
+ } else if (logtype == "" || logtype == "syslog") {
+ const string f = session.getEnv("SYSLOG_FACILITY");
+@@ -89,13 +110,13 @@
+ else if (f == "LOG_LOCAL6") facility = LOG_LOCAL6;
+ else if (f == "LOG_LOCAL7") facility = LOG_LOCAL7;
+ else facility = LOG_DAEMON;
+- }
++ }
+
+ SyslogDevice *device = new SyslogDevice(IODevice::IsEnabled
+- | IODevice::FlushesOnEndl,
+- "bincimap-up",
+- LOG_NDELAY | LOG_PID,
+- facility);
++ | IODevice::FlushesOnEndl,
++ "bincimap-up",
++ LOG_NDELAY | LOG_PID,
++ facility);
+ ioFactory.addDevice(device);
+ }
+
+@@ -105,25 +126,34 @@
+
+
+ MultilogDevice *device = new MultilogDevice(IODevice::IsEnabled
+- | IODevice::FlushesOnEndl);
+- ioFactory.addDevice(device);
++ | IODevice::FlushesOnEndl);
++ ioFactory.addDevice(device);
+
+ // Now that we know the log type, we can flush.
+ IOFactory::getLogger().setFlags(IODevice::FlushesOnEndl);
+ IOFactory::getLogger().setOutputLevelLimit(IODevice::InfoLevel);
+
+- // imaps (port 993) -- requires sslserver with option -e
++ string ucspitls = session.getEnv("UCSPITLS");
++ if (ucspitls == "+") stls = 1;
++ if (ucspitls == "-") stls = 0;
++ if (ucspitls == "!") stls = 2;
+
+- int stls = 0;
+- if (getenv("SSL_SESSION_ID")) {
+- session.command.ssl = true;
+- stls = -1;
+- // else we will do starttls - requires new FDs
+- } else if (getenv("UCSPITLS")) {
+- string ucspitls = session.getEnv("UCSPITLS");
+- if (ucspitls == "+") stls = 1;
+- if (ucspitls == "-") stls = 0;
+- if (ucspitls == "!") stls = 2;
++ if (stls > 0) {
++ string fdstr;
++ int fd;
++ fdstr = session.getEnv("SSLCTLFD");
++ fd = std::stoi(fdstr);
++ if (write(fd,"Y",1) < 1) return 0;
++
++ fdstr = session.getEnv("SSLREADFD");
++ fd = std::stoi(fdstr);
++ if (dup2(0,fd) == -1) return 0;
++ close(fd);
++
++ fdstr = session.getEnv("SSLWRITEFD");
++ fd = std::stoi(fdstr);
++ if (dup2(1,fd) == -1) return 0;
++ close(fd);
+ }
+
+ BrokerFactory &brokerfactory = BrokerFactory::getInstance();
+@@ -133,8 +163,7 @@
+ brokerfactory.assign("LOGIN", new LoginOperator());
+ brokerfactory.assign("LOGOUT", new LogoutOperator());
+ brokerfactory.assign("NOOP", new NoopOperator());
+- brokerfactory.assign("ID", new IdOperator());
+- if (stls > 0) brokerfactory.assign("STARTTLS", new StarttlsOperator());
++ if (stls) brokerfactory.assign("STARTTLS", new StarttlsOperator());
+
+ bincClient.setTimeout(60);
+
+--- ./session-initialize-bincimapd.cc 2023-08-24 10:37:13.007980000 +0200
++++ ../../bincimap-2.0.5/src//session-initialize-bincimapd.cc 2023-08-21 12:20:24.757336000 +0200
+@@ -1,9 +1,12 @@
+-/** --------------------------------------------------------------------
+- * @file session-initialize-bincimapd.cc
+- * @brief <--->
+- * @author Andreas Aardal Hanssen, Erwin Hoffmann
+- * @date 2002-2005, 2023
++/* --------------------------------------------------------------------
++ * Filename:
++ * session-initialize-bincimap-up.cc
++ *
++ * Description:
++ * <--->
+ * --------------------------------------------------------------------
++ * Copyright 2002-2005 Andreas Aardal Hanssen
++ * --------------------------------------------------------------------
+ */
+ #include <unistd.h>
+ #include <syslog.h>
+@@ -21,7 +24,6 @@
+ #include "syslogdevice.h"
+ #include "tools.h"
+ #include "convert.h"
+-
+ #include <string>
+ #include <map>
+ #include <signal.h>
+@@ -31,14 +33,24 @@
+
+ extern char **environ;
+
++namespace {
++ //------------------------------------------------------------------------
++ void usage(char *name)
++ {
++ bincInfo << "Please refer to the man pages for bincimap-up and bincimapd"
++ << endl;
++ bincInfo << "for more information about how to invoke Binc IMAP." << endl;
++ bincInfo.flush();
++ }
++}
++
+ //----------------------------------------------------------------------
+ bool Session::initialize(int argc, char *argv[])
+ {
+ IOFactory &ioFactory = IOFactory::getInstance();
+-
+ IODevice *stdioDevice = new StdIODevice(IODevice::IsEnabled
+- | IODevice::HasInputLimit
+- | IODevice::HasTimeout);
++ | IODevice::HasInputLimit
++ | IODevice::HasTimeout);
+ stdioDevice->setFlags(IODevice::HasOutputLimit);
+ stdioDevice->setMaxOutputBufferSize(TRANSFER_BUFFER_SIZE);
+ ioFactory.addDevice(stdioDevice);
+@@ -55,9 +67,9 @@
+ return false;
+ }
+
+- // Show version if asked for it
++ // Show help if asked for it
+ if (session.command.version) {
+- printf("Binc IMAP v" BINC_VERSION"\n");
++ printf("Binc IMAP v" BINC_VERSION IMAP_VERSION"\n");
+ return false;
+ }
+
+@@ -96,15 +108,27 @@
+ session.setEnv("SYSLOG_FACILITY", toString(facility));
+
+ ioFactory.addDevice(new SyslogDevice(IODevice::IsEnabled,
+- "bincimapd",
+- LOG_NDELAY | LOG_PID,
+- facility));
++ "bincimapd",
++ LOG_NDELAY | LOG_PID,
++ facility));
+ }
+
+ // Now that we know the log type, we can flush.
+ IOFactory::getLogger().flush();
+ IOFactory::getLogger().setFlags(IODevice::FlushesOnEndl);
+ IOFactory::getLogger().setOutputLevelLimit(IODevice::InfoLevel);
++
++ // Show help if asked for it
++ if (session.command.help) {
++ usage(argv[0]);
++ return false;
++ }
++
++ // Show help if asked for it
++ if (session.command.version) {
++ bincInfo << "Binc IMAP v" << BINC_VERSION IMAP_VERSION << endl;
++ return false;
++ }
+
+ char *logindetails = getenv("BINCIMAP_LOGIN");
+ if (logindetails == 0) {
+--- ./session.cc 2023-08-22 11:08:44.043704000 +0200
++++ ../../bincimap-2.0.5/src//session.cc 2023-08-20 13:56:00.410613000 +0200
+@@ -137,7 +137,7 @@
+ {
+ args.addOptional("h|?|help", "Display this help screen", true);
+ args.addOptional("version", "Display the version of Binc IMAP", true);
+- args.addOptional("a|allow-plain", "Allow authentication when not TLS protected", true);
++ args.addOptional("a|allow-plain", "Allow authentication when not in SSL", true);
+ args.addOptional("v|show-version", "Enable verbose IMAP greeting", false);
+ args.addOptional("l|log-type", "Sets the method used for logging", false);
+ args.addOptional("d|depot", "Sets the depot type", false);
+--- ./syslogdevice.cc 2023-08-22 13:36:36.473713000 +0200
++++ ../../bincimap-2.0.5/src//syslogdevice.cc 2023-08-19 21:24:47.622270000 +0200
+@@ -62,7 +62,8 @@
+ out += *i;
+ }
+
+- if (out != "") syslog(priority, out.c_str(), out.size());
++ if (out != "")
++ syslog(priority, out.c_str(), out.size());
+
+ outputBuffer.clear();
+ return WriteDone;
diff --git a/src/convert.cc b/src/convert.cc
new file mode 100644
index 0000000..5af4f3c
--- /dev/null
+++ b/src/convert.cc
@@ -0,0 +1,114 @@
+/** --------------------------------------------------------------------
+ * @file convert.cc
+ * @brief Implementation of miscellaneous convertion functions
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "convert.h"
+#include <string>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+BincStream::BincStream(void)
+{
+}
+
+//------------------------------------------------------------------------
+BincStream::~BincStream(void)
+{
+ clear();
+}
+
+//------------------------------------------------------------------------
+string BincStream::popString(unsigned int size)
+{
+ if (size > nstr.length())
+ size = nstr.length();
+ string tmp = nstr.substr(0, size);
+ nstr = nstr.substr(size);
+ return tmp;
+}
+
+//------------------------------------------------------------------------
+char BincStream::popChar(void)
+{
+ if (nstr.length() == 0)
+ return '\0';
+
+ char c = nstr[0];
+ nstr = nstr.substr(1);
+ return c;
+}
+
+//------------------------------------------------------------------------
+void BincStream::unpopChar(char c)
+{
+ nstr = c + nstr;
+}
+
+//------------------------------------------------------------------------
+void BincStream::unpopStr(const string &s)
+{
+ nstr = s + nstr;
+}
+
+//------------------------------------------------------------------------
+const string &BincStream::str(void) const
+{
+ return nstr;
+}
+
+//------------------------------------------------------------------------
+void BincStream::clear(void)
+{
+ nstr = "";
+}
+
+//------------------------------------------------------------------------
+unsigned int BincStream::getSize(void) const
+{
+ return (unsigned int) nstr.length();
+}
+
+//------------------------------------------------------------------------
+BincStream &BincStream::operator << (std::ostream&(*)(std::ostream&))
+{
+ nstr += "\r\n";
+ return *this;
+}
+
+//------------------------------------------------------------------------
+BincStream &BincStream::operator << (const string &t)
+{
+ nstr += t;
+ return *this;
+}
+
+//------------------------------------------------------------------------
+BincStream &BincStream::operator << (int t)
+{
+ nstr += toString(t);
+ return *this;
+}
+
+//------------------------------------------------------------------------
+BincStream &BincStream::operator << (unsigned long t)
+{
+ nstr += toString(t);
+ return *this;
+}
+
+BincStream &BincStream::operator << (unsigned int t)
+{
+ nstr += toString(t);
+ return *this;
+}
+
+//------------------------------------------------------------------------
+BincStream &BincStream::operator << (char t)
+{
+ nstr += t;
+ return *this;
+}
diff --git a/src/depot.cc b/src/depot.cc
new file mode 100644
index 0000000..599bf97
--- /dev/null
+++ b/src/depot.cc
@@ -0,0 +1,717 @@
+/** --------------------------------------------------------------------
+ * @file depot.cc
+ * @brief Implementation of the Depot class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <map>
+#include <string>
+#include <unistd.h>
+#include <errno.h>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "status.h"
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//--------------------------------------------------------------------
+DepotFactory::DepotFactory(void)
+{
+}
+
+//--------------------------------------------------------------------
+DepotFactory::~DepotFactory(void)
+{
+ for (vector<Depot *>::iterator i = depots.begin(); i != depots.end();
+ ++i)
+ delete *i;
+}
+
+//--------------------------------------------------------------------
+Depot *DepotFactory::get(const string &name) const
+{
+ for (vector<Depot *>::const_iterator i = depots.begin(); i != depots.end();
+ ++i)
+ if ((*i)->getName() == name)
+ return *i;
+
+ return 0;
+}
+
+//--------------------------------------------------------------------
+void DepotFactory::assign(Depot *depot)
+{
+ depots.push_back(depot);
+}
+
+//--------------------------------------------------------------------
+DepotFactory &DepotFactory::getInstance(void)
+{
+ static DepotFactory depotfactory;
+ return depotfactory;
+}
+
+//--------------------------------------------------------------------
+Depot::Depot(void) : enditerator(0, 0)
+{
+ defaultmailbox = 0;
+ selectedmailbox = 0;
+
+ delimiter = '/';
+}
+
+//--------------------------------------------------------------------
+Depot::Depot(const string &name) : enditerator(0, 0)
+{
+ defaultmailbox = 0;
+ selectedmailbox = 0;
+
+ delimiter = '/';
+
+ this->name = name;
+}
+
+//--------------------------------------------------------------------
+Depot::~Depot(void)
+{
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getLastError(void) const
+{
+ return lastError;
+}
+
+//--------------------------------------------------------------------
+void Depot::setLastError(const string &error) const
+{
+ lastError = error;
+}
+
+//--------------------------------------------------------------------
+void Depot::assign(Mailbox *m)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if (*i == m) break;
+
+ backends.push_back(m);
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::get(const string &s_in) const
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if ((*i)->isMailbox(mailboxToFilename(s_in)))
+ return *i;
+
+ setLastError("No such mailbox " + toImapString(s_in));
+ return 0;
+}
+
+//--------------------------------------------------------------------
+bool Depot::setSelected(Mailbox *m)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if (*i == m) {
+ selectedmailbox = m;
+ return true;
+ }
+
+ setLastError("Attempted to select unregistered Mailbox type in Depot");
+ return false;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getName(void) const
+{
+ return name;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getPersonalNamespace(void) const
+{
+ return personalNamespace;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getOthersNamespace(void) const
+{
+ return othersNamespace;
+}
+
+//--------------------------------------------------------------------
+const string &Depot::getSharedNamespace(void) const
+{
+ return sharedNamespace;
+}
+
+//--------------------------------------------------------------------
+void Depot::setDelimiter(char c)
+{
+ delimiter = c;
+}
+
+//--------------------------------------------------------------------
+const char Depot::getDelimiter(void) const
+{
+ return delimiter;
+}
+
+//--------------------------------------------------------------------
+bool Depot::setDefaultType(const string &name)
+{
+ for (vector<Mailbox *>::const_iterator i = backends.begin();
+ i != backends.end(); ++i)
+ if ((*i)->getTypeName() == name) {
+ defaultmailbox = *i;
+ return true;
+ }
+
+ setLastError("attempt to default to unregistered Mailbox type " + name);
+ return false;
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::getSelected(void) const
+{
+ return selectedmailbox;
+}
+
+//--------------------------------------------------------------------
+void Depot::resetSelected(void)
+{
+ selectedmailbox = 0;
+}
+
+//--------------------------------------------------------------------
+Mailbox *Depot::getDefault(void) const
+{
+ return defaultmailbox;
+}
+
+//--------------------------------------------------------------------
+bool Depot::createMailbox(const string &s_in) const
+{
+ const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
+ if (mailboxname == "") {
+ setLastError("invalid mailbox name");
+ return false;
+ }
+
+ Mailbox *mailbox = getDefault();
+ if (mailbox == 0) {
+ setLastError("no default mailbox defined");
+ return false;
+ }
+
+ bool result = mailbox->createMailbox(mailboxname, 0777);
+ if (result)
+ return true;
+ else {
+ setLastError(mailbox->getLastError());
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------
+bool Depot::deleteMailbox(const string &s_in) const
+{
+ const string &mailboxname = mailboxToFilename(toCanonMailbox(s_in));
+
+ Mailbox *mailbox = get(s_in);
+ if (mailbox == 0) {
+ setLastError(s_in + ": no such mailbox");
+ return false;
+ }
+
+ bool result = mailbox->deleteMailbox(mailboxname);
+ if (result)
+ return true;
+ else {
+ setLastError(mailbox->getLastError());
+ return false;
+ }
+}
+
+//--------------------------------------------------------------------
+bool Depot::renameMailbox(const string &s_in, const string &t_in) const
+{
+ const string &source = mailboxToFilename(s_in).c_str();
+ const string &dest = mailboxToFilename(t_in).c_str();
+
+ int nrenamed = 0;
+ const iterator e = end();
+ for (iterator i = begin("."); i != e; ++i) {
+ string entry = *i;
+
+ if (entry.substr(0, source.length()) == source) {
+ string sourcename, destname;
+
+ if (entry.length() == source.length()) {
+ sourcename = source;
+ destname = dest;
+
+ } else if (entry.length() > source.length()
+ && entry[source.length()] == '.') {
+ sourcename = entry;
+ destname = dest + entry.substr(source.length());
+ } else continue;
+
+ if (rename(sourcename.c_str(), destname.c_str()) != 0) {
+ bincWarning << "error renaming " << sourcename << " to "
+ << destname << ": " << strerror(errno) << endl;
+ } else
+ nrenamed++;
+
+ Mailbox *mailbox;
+ if ((mailbox = get(filenameToMailbox(sourcename))) != 0)
+ mailbox->bumpUidValidity(filenameToMailbox(sourcename));
+ if ((mailbox = get(filenameToMailbox(destname))) != 0)
+ mailbox->bumpUidValidity(filenameToMailbox(destname));
+ }
+ }
+
+ if (nrenamed == 0) {
+ setLastError("An error occurred when renaming "
+ + toImapString(s_in)
+ + " to " + toImapString(t_in)
+ + ". Try creating a new mailbox,"
+ " then copy over all messages."
+ " Finally, delete the original mailbox");
+ return false;
+ } else
+ return true;
+}
+
+//--------------------------------------------------------------------
+bool Depot::getStatus(const std::string &s_in, Status &dest) const
+{
+ const string mailbox = toCanonMailbox(s_in);
+ Mailbox *m = get(mailbox);
+ if (m == 0) {
+ setLastError("Unrecognized mailbox: " + toImapString(s_in));
+ return false;
+}
+
+ int statusid = m->getStatusID(mailboxToFilename(mailbox));
+ if (mailboxstatuses.find(mailbox) != mailboxstatuses.end()) {
+ dest = mailboxstatuses[mailbox];
+ if (dest.getStatusID() == statusid)
+ return true;
+ }
+
+ if (!m->getStatus(mailboxToFilename(mailbox), dest)) {
+ setLastError(m->getLastError());
+ return false;
+ }
+
+ dest.setStatusID(statusid);
+ mailboxstatuses[mailbox] = dest;
+ return true;
+}
+
+//----------------------------------------------------------------------
+vector<string> Depot::getSubscriptions(void) const
+{
+ return subscribed;
+}
+
+//----------------------------------------------------------------------
+void Depot::subscribeTo(const std::string mailbox)
+{
+ for (vector<string>::iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (*i == mailbox)
+ return;
+ }
+
+ subscribed.push_back(mailbox);
+}
+
+//----------------------------------------------------------------------
+bool Depot::unsubscribeTo(const std::string mailbox)
+{
+ for (vector<string>::iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (*i == mailbox) {
+ subscribed.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//----------------------------------------------------------------------
+void Depot::loadSubscribes(void)
+{
+ // drop all existing subscribed folders.
+ subscribed.clear();
+
+ // try loading the .subscribed file
+ bool ok = false;
+ FILE *fp = fopen(".subscribed", "r");
+ map<string, bool> addedEntries;
+ if (fp) {
+ int c;
+ string current;
+ while ((c = fgetc(fp)) != EOF) {
+ if (c == '\n') {
+ if (current != "") {
+ if (current == "INBOX")
+ current = ".";
+
+ if (current.substr(0, 5) == "INBOX")
+ current = current.substr(5);
+
+ if (addedEntries.find(current) == addedEntries.end()) {
+ subscribed.push_back(filenameToMailbox(current));
+ addedEntries[current] = true;
+ }
+ current = "";
+ }
+ } else
+ current += c;
+ }
+
+ fclose(fp);
+ ok = true;
+ }
+
+ if (!ok) {
+ subscribed.push_back("INBOX");
+ saveSubscribes();
+ }
+}
+
+//----------------------------------------------------------------------
+bool Depot::saveSubscribes(void) const
+{
+ // create a safe file name
+ string tpl = ".subscribed-tmp-XXXXXX";
+ char *ftemplate = new char[tpl.length() + 1];
+
+ strcpy(ftemplate, tpl.c_str());
+ int fd = mkstemp(ftemplate);
+ if (fd == -1) {
+ bincWarning << "unable to create temporary file \""
+ << tpl << "\"" << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ map<string, bool> addedEntries;
+ for (vector<string>::const_iterator i = subscribed.begin();
+ i != subscribed.end(); ++i) {
+ if (addedEntries.find(*i) == addedEntries.end()) {
+ addedEntries[*i] = true;
+ string w = mailboxToFilename(*i) + "\n";
+ if (write(fd, w.c_str(), w.length()) != (ssize_t) w.length()) {
+ bincWarning << "failed to write to " << tpl << ": "
+ << strerror(errno) << endl;
+ break;
+ }
+ }
+ }
+
+ if ((fsync(fd) && (errno != EROFS || errno != EINVAL)) || close(fd)) {
+ bincWarning << "failed to close " << ftemplate
+ << ": " << strerror(errno) << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ if (rename(ftemplate, ".subscribed") != 0) {
+ bincWarning << "failed to rename " << ftemplate
+ << " to .subscribed: "
+ << strerror(errno) << endl;
+ delete[] ftemplate;
+ return false;
+ }
+
+ delete[] ftemplate;
+ return true;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(void)
+{
+ dirp = 0;
+ ref = new int;
+ *ref = 1;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(DIR *dp, struct dirent *sp)
+{
+ dirp = dp;
+ direntp = sp;
+
+ ref = new int;
+ *ref = 1;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::iterator(const iterator &copy)
+{
+ if (*copy.ref != 0)
+ ++(*copy.ref);
+
+ ref = copy.ref;
+ dirp = copy.dirp;
+ direntp = copy.direntp;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator::~iterator(void)
+{
+ deref();
+}
+
+//--------------------------------------------------------------------
+Depot::iterator &Depot::iterator::operator =(const iterator &copy)
+{
+ if (*copy.ref != 0)
+ ++(*copy.ref);
+
+ deref();
+
+ ref = copy.ref;
+ dirp = copy.dirp;
+ direntp = copy.direntp;
+
+ return *this;
+}
+
+//--------------------------------------------------------------------
+void Depot::iterator::deref(void)
+{
+ // decrease existing copy ref if there is one
+ if (*ref != 0 && --(*ref) == 0) {
+ if (dirp) {
+ closedir(dirp);
+ dirp = 0;
+ }
+
+ delete ref;
+ ref = 0;
+ }
+}
+
+//--------------------------------------------------------------------
+string Depot::iterator::operator * (void) const
+{
+ if (direntp == 0)
+ return "";
+
+ return direntp->d_name;
+}
+
+//--------------------------------------------------------------------
+void Depot::iterator::operator ++ (void)
+{
+ direntp = readdir(dirp);
+}
+
+//--------------------------------------------------------------------
+bool Depot::iterator::operator == (Depot::iterator i) const
+{
+ return direntp == i.direntp;
+}
+
+//--------------------------------------------------------------------
+bool Depot::iterator::operator != (Depot::iterator i) const
+{
+ return direntp != i.direntp;
+}
+
+//--------------------------------------------------------------------
+Depot::iterator Depot::begin(const string &path) const
+{
+ Depot::iterator i;
+
+ if ((i.dirp = opendir(path.c_str())) == 0) {
+ bincWarning << "opendir on " << path << " failed" << endl;
+ setLastError("opendir on " + path + " failed");
+ return end();
+ }
+
+ ++i;
+ return i;
+}
+
+//--------------------------------------------------------------------
+const Depot::iterator &Depot::end(void) const
+{
+ return enditerator;
+}
+
+//--------------------------------------------------------------------
+MaildirPPDepot::MaildirPPDepot(void) : Depot("Maildir++")
+{
+ privateNamespace = "INBOX";
+ privateNamespace += getDelimiter();
+}
+
+//--------------------------------------------------------------------
+MaildirPPDepot::~MaildirPPDepot(void)
+{
+}
+
+//--------------------------------------------------------------------
+const string &MaildirPPDepot::getPersonalNamespace(void) const
+{
+ return privateNamespace;
+}
+
+//--------------------------------------------------------------------
+string MaildirPPDepot::mailboxToFilename(const string &m) const
+{
+ string prefix = "INBOX"; prefix += delimiter;
+
+ string mm = m;
+ trim(mm, string(&delimiter, 1));
+ string tmp = mm;
+ uppercase(tmp);
+ if (tmp != "INBOX" && tmp.substr(0, 6) != prefix) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX. Try creating"
+ " " + prefix + mm + " .");
+ return "";
+ }
+
+ string twodelim;
+ twodelim += delimiter;
+ twodelim += delimiter;
+
+ if (mm == "INBOX") return ".";
+ else if (mm.length() <= 6) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX.");
+ return "";
+ } else if (mm.substr(0, 6) != prefix) {
+ setLastError("With a Maildir++ depot, you must create all"
+ " mailboxes under INBOX.");
+ return "";
+ } else if (mm.find(twodelim) != string::npos) {
+ setLastError("Invalid character combination "
+ + twodelim + " in mailbox name");
+ return "";
+ } else if (mm != "" && delimiter != '.' && mm.substr(1).find('.') != string::npos) {
+ setLastError("Invalid character '.' in mailbox name");
+ return "";
+ } else {
+ string tmp = mm.substr(6);
+ for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
+ if (*i == delimiter) *i = '.';
+
+ return "." + tmp;
+ }
+}
+
+//--------------------------------------------------------------------
+string MaildirPPDepot::filenameToMailbox(const string &m) const
+{
+ if (m == ".") return "INBOX";
+ else if (delimiter != '.' && m.find(delimiter) != string::npos) return "";
+ else if (m != "" && m[0] == '.') {
+ string tmp = m;
+ for (string::iterator i = tmp.begin(); i != tmp.end(); ++i)
+ if (*i == '.') *i = delimiter;
+
+ return "INBOX" + tmp;
+ } else return "";
+}
+
+//--------------------------------------------------------------------
+IMAPdirDepot::IMAPdirDepot(void) : Depot("IMAPdir")
+{
+}
+
+//--------------------------------------------------------------------
+IMAPdirDepot::~IMAPdirDepot(void)
+{
+}
+
+//--------------------------------------------------------------------
+string IMAPdirDepot::mailboxToFilename(const string &m) const
+{
+ string tmp;
+ string mm = m;
+ trim(mm, string(&delimiter, 1));
+
+ string twodelim;
+ twodelim += delimiter;
+ twodelim += delimiter;
+
+ if (mm.find(twodelim) != string::npos) {
+ setLastError("Invalid character combination "
+ + twodelim + " in mailbox name");
+ return "";
+ }
+
+ string::const_iterator i = mm.begin();
+ while (i != mm.end()) {
+ if (*i == delimiter) {
+ tmp += '.';
+ } else if (*i == '\\') {
+ tmp += "\\\\";
+ } else if (*i == '.') {
+ if (i == mm.begin())
+ tmp += ".";
+ else
+ tmp += "\\.";
+ } else {
+ tmp += *i;
+ }
+
+ ++i;
+ }
+
+ return tmp;
+}
+
+//--------------------------------------------------------------------
+string IMAPdirDepot::filenameToMailbox(const string &m) const
+{
+ string tmp;
+ bool escape = false;
+
+ // hide the magic "." mailbox.
+ if (m == "." || m == "..")
+ return "";
+
+ string::const_iterator i = m.begin();
+ while (i != m.end()) {
+ if (*i == '.') {
+ if (i != m.begin() && !escape) tmp += delimiter;
+ else if (i == m.begin() || escape) tmp += '.';
+ escape = false;
+ } else if (*i == '\\') {
+ if (!escape) escape = true; else {
+ tmp += '\\';
+ escape = false;
+ }
+ } else if (*i == delimiter) {
+ return "";
+ } else {
+ if (escape) return "";
+ else {
+ tmp += *i;
+ escape = false;
+ }
+ }
+
+ ++i;
+ }
+
+ return tmp;
+}
diff --git a/src/greeting.cc b/src/greeting.cc
new file mode 100644
index 0000000..3a2a394
--- /dev/null
+++ b/src/greeting.cc
@@ -0,0 +1,47 @@
+/** --------------------------------------------------------------------
+ * @file greeting.cc
+ * @brief Implementation of the inital greeting.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <time.h>
+
+#include "iodevice.h"
+#include "iofactory.h"
+#include "session.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+static const unsigned int ISO8601SIZE = 32;
+
+namespace Binc {
+ void showGreeting(void);
+};
+
+
+//------------------------------------------------------------------------
+void Binc::showGreeting(void)
+{
+ Session &session = Session::getInstance();
+
+ time_t t = time(0);
+ struct tm *mytm = localtime(&t);
+
+ char mytime[ISO8601SIZE];
+ unsigned int size = strftime(mytime, sizeof(mytime), "%Y-%m-%d %H:%M:%S %z", mytm);
+ if (size >= sizeof(mytime) || size == 0)
+ mytime[0] = 0;
+
+ if (session.hasEnv("VERBOSE_GREETING")) {
+ bincClient << "* OK Welcome to Binc IMAP "
+ << BINC_VERSION
+ << " "
+ << IMAP_VERSION
+ << " by Andreas Aardal Hanssen & Erwin Hoffmann at "
+ << mytime << endl;
+ } else {
+ bincClient << "* OK Welcome to Binc IMAP at " << mytime << endl;
+ }
+}
diff --git a/src/imapparser.cc b/src/imapparser.cc
new file mode 100644
index 0000000..2e7d746
--- /dev/null
+++ b/src/imapparser.cc
@@ -0,0 +1,379 @@
+/** --------------------------------------------------------------------
+ * @file imapparser.cc
+ * @brief Implementation of the common items for parsing IMAP input
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "imapparser.h"
+#include "convert.h"
+
+#include <stdio.h>
+#include <map>
+#include <iostream>
+#include <vector>
+#include <string>
+#include <exception>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Request::Request(void)
+ : extra(0), flags(), statuses(), bset(), searchkey(), fatt()
+{
+ uidmode = false;
+}
+
+Request::~Request(void)
+{
+ if (extra != 0)
+ delete extra;
+}
+
+//------------------------------------------------------------------------
+void Request::setUidMode(void)
+{
+ uidmode = true;
+}
+
+//------------------------------------------------------------------------
+bool Request::getUidMode(void) const
+{
+ return uidmode;
+}
+
+//------------------------------------------------------------------------
+void Request::setTag(string &t_in)
+{
+ tag = t_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getTag(void) const
+{
+ return tag;
+}
+
+//------------------------------------------------------------------------
+void Request::setMode(const string &m_in)
+{
+ mode = m_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getMode(void) const
+{
+ return mode;
+}
+
+//------------------------------------------------------------------------
+void Request::setName(const string &s_in)
+{
+ name = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getName(void) const
+{
+ return name;
+}
+
+//------------------------------------------------------------------------
+void Request::setAuthType(const string &s_in)
+{
+ authtype = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getAuthType(void) const
+{
+ return authtype;
+}
+
+//------------------------------------------------------------------------
+void Request::setLiteral(const string &s_in)
+{
+ literal = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getLiteral(void) const
+{
+ return literal;
+}
+
+//------------------------------------------------------------------------
+void Request::setDate(const string &s_in)
+{
+ date = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getDate(void) const
+{
+ return date;
+}
+
+//------------------------------------------------------------------------
+void Request::setCharSet(const string &s_in)
+{
+ charset = s_in;
+ uppercase(charset);
+}
+
+//------------------------------------------------------------------------
+const string &Request::getCharSet(void) const
+{
+ return charset;
+}
+
+//------------------------------------------------------------------------
+void Request::setUserID(const string &s_in)
+{
+ userid = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getUserID(void) const
+{
+ return userid;
+}
+
+//------------------------------------------------------------------------
+void Request::setPassword(const string &s_in)
+{
+ password = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getPassword(void) const
+{
+ return password;
+}
+
+//------------------------------------------------------------------------
+void Request::setMailbox(const string &s_in)
+{
+ mailbox = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getMailbox(void) const
+{
+ return mailbox;
+}
+
+//------------------------------------------------------------------------
+void Request::setListMailbox(const string &s_in)
+{
+ listmailbox = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getListMailbox(void) const
+{
+ return listmailbox;
+}
+
+//------------------------------------------------------------------------
+void Request::setContextInfo(const string &s_in)
+{
+ contextInfo = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getContextInfo(void) const
+{
+ return contextInfo;
+}
+
+//------------------------------------------------------------------------
+void Request::setNewMailbox(const string &s_in)
+{
+ newmailbox = s_in;
+}
+
+//------------------------------------------------------------------------
+const string &Request::getNewMailbox(void) const
+{
+ return newmailbox;
+}
+
+//------------------------------------------------------------------------
+SequenceSet &Request::getSet(void)
+{
+ return bset;
+}
+
+//------------------------------------------------------------------------
+vector<string> &Request::getStatuses(void)
+{
+ return statuses;
+}
+
+//------------------------------------------------------------------------
+vector<string> &Request::getFlags(void)
+{
+ return flags;
+}
+
+//------------------------------------------------------------------------
+SequenceSet::SequenceSet(void) : limited(true), nullSet(false)
+{
+}
+
+//------------------------------------------------------------------------
+SequenceSet::SequenceSet(const SequenceSet &copy)
+ : limited(copy.limited), nullSet(copy.nullSet), internal(copy.internal)
+{
+}
+
+//------------------------------------------------------------------------
+SequenceSet &SequenceSet::operator = (const SequenceSet &copy)
+{
+ limited = copy.limited;
+ nullSet = copy.nullSet;
+ internal = copy.internal;
+
+ return *this;
+}
+
+//------------------------------------------------------------------------
+SequenceSet::~SequenceSet(void)
+{
+}
+
+//------------------------------------------------------------------------
+SequenceSet &SequenceSet::null(void)
+{
+ static SequenceSet nil;
+ nil.nullSet = true;
+ return nil;
+}
+
+//------------------------------------------------------------------------
+bool SequenceSet::isNull(void) const
+{
+ return nullSet;
+}
+
+//------------------------------------------------------------------------
+SequenceSet &SequenceSet::all(void)
+{
+ static bool initialized = false;
+ static SequenceSet all;
+
+ if (!initialized) {
+ all.addRange(1, (unsigned int)-1);
+ initialized = true;
+ }
+
+ return all;
+}
+
+//------------------------------------------------------------------------
+SequenceSet::Range::Range(unsigned int a, unsigned int b)
+{
+ if (a > b) {
+ from = b;
+ to = a;
+ } else {
+ from = a;
+ to = b;
+ }
+}
+
+//------------------------------------------------------------------------
+void SequenceSet::addRange(unsigned int a, unsigned int b)
+{
+ if (a == (unsigned int)-1 || b == (unsigned int)-1) limited = false;
+ internal.push_back(Range(a, b));
+}
+
+//------------------------------------------------------------------------
+void SequenceSet::addNumber(unsigned int a)
+{
+ if (a == (unsigned int)-1) limited = false;
+ internal.push_back(Range(a, a));
+}
+
+//------------------------------------------------------------------------
+bool SequenceSet::isInSet(unsigned int n) const
+{
+ unsigned int maxvalue = 0;
+ for (vector<Range>::const_iterator i = internal.begin();
+ i != internal.end(); ++i) {
+ const Range &r = *i;
+ if (r.from > maxvalue) maxvalue = r.from;
+ else if (r.to > maxvalue) maxvalue = r.to;
+
+ if (n >= (*i).from && n <= (*i).to) {
+ return true;
+ }
+ }
+
+ return (n > maxvalue && !limited);
+}
+
+//------------------------------------------------------------------------
+BincImapParserFetchAtt::BincImapParserFetchAtt(const std::string &typeName)
+ : type(typeName)
+{
+ offsetstart = 0;
+ offsetlength = (unsigned int) -1;
+ hassection = false;
+}
+
+//------------------------------------------------------------------------
+string BincImapParserFetchAtt::toString(void)
+{
+ string tmp;
+ if (type == "BODY.PEEK")
+ tmp = "BODY";
+ else
+ tmp = type;
+
+ if (type == "BODY" || type == "BODY.PEEK") {
+ if (hassection) {
+ tmp += "[";
+ tmp += section;
+ if (sectiontext != "") {
+ if (section != "")
+ tmp += ".";
+ tmp += sectiontext;
+
+ if (headerlist.size() > 0) {
+ tmp += " (";
+ for (vector<string>::iterator i = headerlist.begin();
+ i != headerlist.end(); ++i) {
+ if (i != headerlist.begin())
+ tmp += " ";
+ tmp += Binc::toImapString(*i);
+ }
+ tmp += ")";
+ }
+ }
+ tmp += "]";
+
+ if (offsetstart == 0 && offsetlength == (unsigned int) -1)
+ tmp += " ";
+ else
+ tmp += "<" + Binc::toString(offsetstart) + "> ";
+ }
+ }
+
+ return tmp;
+}
+
+//------------------------------------------------------------------------
+BincImapParserSearchKey::BincImapParserSearchKey(void)
+{
+ type = 0;
+ number = 0;
+}
+
+//------------------------------------------------------------------------
+const SequenceSet& BincImapParserSearchKey::getSet(void) const
+{
+ return bset;
+}
diff --git a/src/imapserver.cc b/src/imapserver.cc
new file mode 100755
index 0000000..003352b
--- /dev/null
+++ b/src/imapserver.cc
@@ -0,0 +1,221 @@
+/** --------------------------------------------------------------------
+ * @file imapserver.cc
+ * @brief Implementation of the IMAPServer class.
+ * @author Andreas Aardal Hanssen
+ * @date 2005
+ * --------------------------------------------------------------------
+ */
+#include "broker.h"
+#include "globals.h"
+#include "imapparser.h"
+#include "imapserver.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "session.h"
+
+using namespace ::Binc;
+using namespace ::std;
+
+namespace Binc {
+ void showGreeting(void);
+};
+
+IMAPServer::IMAPServer(int argc, char **argv)
+{
+ this->argc = argc;
+ this->argv = argv;
+ this->stubMode = false;
+ Session::getInstance().setState(Session::AUTHENTICATED);
+}
+
+IMAPServer::~IMAPServer(void)
+{
+}
+
+int IMAPServer::initialize(void)
+{
+ Session &session = Session::getInstance();
+ if (!session.initialize(argc, argv)) return 111;
+ return 0;
+}
+
+void IMAPServer::prepareForNextRequest(void)
+{
+ serverStatus = OK;
+
+ bincClient.setFlags(IODevice::HasInputLimit);
+ bincClient.flush();
+ bincClient.setMaxInputBufferSize(INPUT_BUFFER_LIMIT);
+
+ Session::getInstance().setLastError("");
+ Session::getInstance().clearResponseCode();
+}
+
+int IMAPServer::runStub(void)
+{
+ bincDebug << "IMAPServer::runStub(), running stub" << endl;
+ this->stubMode = true;
+ Session::getInstance().setState(Session::NONAUTHENTICATED);
+ return run();
+}
+
+int IMAPServer::run(void)
+{
+ Session &session = Session::getInstance();
+ bincLog.setOutputLevelLimit(IODevice::InfoLevel);
+ string pid = to_string(session.getPid());
+
+ bincDebug << "IMAPServer::run(), started server" << endl;
+
+ if (this->stubMode) {
+ if (session.hasEnv("PROTOCOLDUMP"))
+ bincClient.enableProtocolDumping();
+ bincLog << "bincimap-up: pid " << pid
+ << " Connected: " << session.getIP() << "\n";
+ showGreeting();
+ } else {
+ bincLog << "bincimapd: pid " << pid
+ << " Logged in: <" << session.getEnv("USER")
+ << "@" << session.getEnv("TCPREMOTEIP") << ">\n";
+ }
+ bincLog.flush();
+
+ do {
+ bincDebug << "IMAPServer::run(), preparing for next request" << endl;
+
+ prepareForNextRequest();
+
+ // Find the current state's broker.
+ BrokerFactory &brokerFactory = BrokerFactory::getInstance();
+ Broker *broker = brokerFactory.getBroker(session.getState());
+
+ bincDebug << "IMAPServer::run(), found broker " << (uintptr_t) broker
+ << " for state " << session.getState() << endl;
+
+ bool skipToNextRequest = false;
+
+ // Parse the stub of the IMAP request.
+ Request clientRequest;
+ int stubParseResult = broker->parseStub(clientRequest);
+ if (stubParseResult == Operator::TIMEOUT) {
+ serverStatus = Timeout;
+ break;
+ } else if (stubParseResult == Operator::REJECT) {
+ serverStatus = RequestRejected;
+ } else if (stubParseResult == Operator::ERROR) {
+ serverStatus = RequestError;
+ } else {
+ // Find an operator that recognizes the name of the request, and
+ // have it continue the parsing.
+ Operator *o = broker->get(clientRequest.getName());
+ if (!o) {
+ serverStatus = RequestRejected;
+ string err = "The command \"";
+ if (clientRequest.getUidMode()) err += "UID ";
+ err += clientRequest.getName();
+ err += "\" is unsupported in this state. ";
+ session.setLastError(err);
+ skipToNextRequest = true;
+ } else {
+ int parseResult = o->parse(clientRequest);
+ if (parseResult == Operator::TIMEOUT) {
+ serverStatus = Timeout;
+ } else if (parseResult == Operator::REJECT) {
+ serverStatus = RequestRejected;
+ } else if (parseResult == Operator::ERROR) {
+ serverStatus = RequestError;
+ } else {
+ session.addStatement();
+ Depot *dep = session.getDepot();
+
+ int processResult = o->process(*dep, clientRequest);
+ if (processResult == Operator::OK) {
+ } else if (processResult == Operator::NO) {
+ serverStatus = RequestRejected;
+ } else if (processResult == Operator::BAD) {
+ serverStatus = RequestError;
+ } else if (processResult == Operator::NOTHING) {
+ serverStatus = RequestIgnore; // answer given already
+ } else if (processResult == Operator::ABORT) {
+ session.setState(Session::LOGOUT);
+ }
+ }
+ }
+ }
+
+ // If a syntax error was detected, we skip all characters in the
+ // input stream up to and including '\n'.
+ if (serverStatus == RequestRejected) {
+ bincClient << clientRequest.getTag() << " NO "
+ << session.getResponseCode()
+ << clientRequest.getName() << " failed: "
+ << session.getLastError() << endl;
+ } else if (serverStatus == RequestError) {
+ bincClient << "* BAD "
+ << session.getLastError() << endl;
+ skipToNextRequest = true;
+ } else if (serverStatus == RequestIgnore) {
+ ;
+ } else if (serverStatus == OK && session.getState() != Session::LOGOUT) {
+ bincClient << clientRequest.getTag() << " OK";
+ if (clientRequest.getUidMode()) bincClient << " UID";
+ bincClient << " " << session.getResponseCode()
+ << clientRequest.getName() << " completed";
+ if (clientRequest.getContextInfo() != "")
+ bincClient << " (" << clientRequest.getContextInfo() << ")";
+
+ bincClient << endl;
+ } else {
+ // Timeout, ClientDisconnected
+ session.setState(Session::LOGOUT);
+ }
+
+ bincClient.flush();
+
+ if (skipToNextRequest) {
+ if (!bincClient.skipTo('\n')) {
+ if (bincClient.getLastError() == IODevice::Timeout) {
+ serverStatus = Timeout;
+ } else
+ serverStatus = ClientDisconnected;
+ break;
+ }
+ }
+ } while (session.getState() != Session::LOGOUT); // do line 81
+
+ // Session finished - write some log information
+
+ string userID = this->stubMode ? session.getIP() : session.getEnv("USER");
+
+ if (this->stubMode) {
+ bincLog << "bincimap-up: pid " << pid
+ << " (Read: " << session.getReadBytes()
+ << " Written: " << session.getWriteBytes() << ")\n";
+ } else {
+ bincLog << "bincimapd: pid " << pid
+ << " (Bodies: " << session.getBodies()
+ << " Statements: " << session.getStatements() << ")\n";
+ }
+
+ if (serverStatus == Timeout) {
+ bincClient << "* BYE Timeout after " << session.timeout()
+ << " seconds of inactivity\n";
+ bincClient.flush();
+ bincLog << "bincimapd: pid " << pid
+ << " Timed out: <" << userID << "> after " << IDLE_TIMEOUT << "s";
+ } else if (serverStatus == ClientDisconnected) {
+ bincLog << "bincimapd: pid " << pid
+ << "Disconnected: <" << userID << ">\n";
+ } else {
+ if (this->stubMode) {
+ bincLog << "bincimap-up: pid " << pid
+ << " Logged out: <" << userID << ">\n";
+ } else {
+ bincLog << "bincimapd: pid " << pid
+ << " Disconnected: <" << userID << ">\n";
+ }
+ }
+ bincLog.flush();
+
+ return 0;
+}
diff --git a/src/include/address.h b/src/include/address.h
new file mode 100644
index 0000000..a8aded9
--- /dev/null
+++ b/src/include/address.h
@@ -0,0 +1,29 @@
+/** --------------------------------------------------------------------
+ * @file address.h
+ * @brief Declaration of the Address class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef address_h_included
+#define address_h_included
+#include <string>
+
+namespace Binc {
+
+ //------------------------------------------------------------------------
+ class Address {
+ public:
+ std::string name;
+ std::string local;
+ std::string host;
+
+ //--
+ std::string toParenList(void) const;
+
+ //--
+ Address(const std::string &name, const std::string &addr);
+ Address(const std::string &wholeaddr);
+ };
+}
+
+#endif
diff --git a/src/include/argparser.h b/src/include/argparser.h
new file mode 100644
index 0000000..6106974
--- /dev/null
+++ b/src/include/argparser.h
@@ -0,0 +1,69 @@
+/** --------------------------------------------------------------------
+ * @file argparser.h
+ * @brief Declaration of the argument parser class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef ARGPARSER_H_INCLUDED
+#define ARGPARSER_H_INCLUDED
+#include <map>
+#include <string>
+#include <vector>
+
+namespace Binc {
+ class ArgOpts {
+ public:
+ std::string c;
+ bool b;
+ bool o;
+ std::string desc;
+
+ inline ArgOpts(const std::string &chr, bool boolean, bool optional,
+ const std::string &descr)
+ {
+ c = chr;
+ b = boolean;
+ o = optional;
+ desc = descr;
+ }
+ };
+
+ class CommandLineArgs {
+ public:
+ CommandLineArgs(void);
+
+ bool parse(int argc, char *argv[]);
+ std::string errorString(void) const;
+
+ int argc(void) const;
+
+ const std::string operator [](const std::string &arg) const;
+
+ void addOptional(const std::string &arg, const std::string &desc,
+ bool boolean);
+ void addRequired(const std::string &arg, const std::string &desc,
+ bool boolean);
+ bool hasArg(const std::string &arg) const;
+
+ std::string usageString(void) const;
+
+ void setTail(const std::string &str);
+
+ const std::vector<std::string> &getUnqualifiedArgs() const;
+
+ private:
+ void registerArg(const std::string &arg, const std::string &desc,
+ bool boolean, bool optional);
+
+ std::string errString;
+ std::map<std::string, ArgOpts> reg;
+ std::map<std::string, std::string> args;
+ std::map<std::string, bool> passedArgs;
+ std::vector<std::string> unqualified;
+ std::string tail;
+ std::string head;
+ int ac;
+ };
+}
+
+#endif
diff --git a/src/include/authenticate.h b/src/include/authenticate.h
new file mode 100644
index 0000000..0ef6796
--- /dev/null
+++ b/src/include/authenticate.h
@@ -0,0 +1,18 @@
+/** --------------------------------------------------------------------
+ * @file authenticate.h
+ * @brief Declaration of the common authentication mechanism.
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#ifndef authenticate_h_included
+#define authenticate_h_included
+#include <string>
+
+#include "depot.h"
+
+namespace Binc {
+ int authenticate(Depot &, const std::string &username,
+ const std::string &password, const std::string &challenge);
+}
+
+#endif
diff --git a/src/include/base64.h b/src/include/base64.h
new file mode 100644
index 0000000..25b0ff4
--- /dev/null
+++ b/src/include/base64.h
@@ -0,0 +1,18 @@
+/** --------------------------------------------------------------------
+ * @file base64.h
+ * @brief Declaration of base64 Utilities
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef base64_h_included
+#define base64_h_included
+#include <string>
+
+namespace Binc {
+
+ std::string base64decode(const std::string &s_in);
+ std::string base64encode(const std::string &s_in);
+
+}
+
+#endif
diff --git a/src/include/broker.h b/src/include/broker.h
new file mode 100644
index 0000000..6d148ae
--- /dev/null
+++ b/src/include/broker.h
@@ -0,0 +1,84 @@
+/** --------------------------------------------------------------------
+ * @file broker.h
+ * @brief Declaration of the Broker class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef broker_h_included
+#define broker_h_included
+#include "depot.h"
+#include "operators.h"
+
+#include <string>
+#include <map>
+
+namespace Binc {
+
+ class Request;
+ class Broker;
+
+ //------------------------------------------------------------------
+ class BrokerFactory {
+ private:
+ std::map<int, Broker *> brokers;
+
+ //--
+ BrokerFactory(void);
+
+ mutable std::string lastError;
+
+ public:
+ Broker *getBroker(int state);
+ void assign(const std::string &fname, Operator *o);
+ void addCapability(const std::string &c);
+ Operator *getOperator(int state, const std::string &name) const;
+
+ inline const std::string &getLastError(void) const;
+ inline void setLastError(const std::string &error) const;
+
+ //--
+ static BrokerFactory &getInstance(void);
+ ~BrokerFactory(void);
+ };
+
+ //------------------------------------------------------------------
+ inline const std::string &BrokerFactory::getLastError(void) const
+ {
+ return lastError;
+ }
+
+ //------------------------------------------------------------------
+ inline void BrokerFactory::setLastError(const std::string &error) const
+ {
+ lastError = error;
+ }
+
+ //------------------------------------------------------------------
+ class Broker {
+ private:
+ std::map<std::string, Operator *> operators;
+ std::map<std::string, bool> deletables;
+
+ public:
+ Operator * get(const std::string &name) const;
+ void assign(const std::string &fname, Operator *o, bool deletable = false);
+ Operator::ParseResult parseStub(Request &cmd);
+
+ //--
+ inline Broker(Broker &);
+ inline Broker(const Broker &);
+ Broker(void);
+ ~Broker(void);
+ };
+
+ inline Broker::Broker(Broker &)
+ {
+ }
+
+ inline Broker::Broker(const Broker &)
+ {
+ }
+
+}
+
+#endif
diff --git a/src/include/convert.h b/src/include/convert.h
new file mode 100644
index 0000000..cea2906
--- /dev/null
+++ b/src/include/convert.h
@@ -0,0 +1,298 @@
+/** --------------------------------------------------------------------
+ * @file convert.h
+ * @brief Declaration of miscellaneous convertion functions.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+
+#ifndef convert_h_included
+#define convert_h_included
+#include <cstring>
+#include <string>
+#include <vector>
+#include <iomanip>
+#include <iostream>
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "address.h"
+#include "depot.h"
+
+namespace Binc {
+
+ //----------------------------------------------------------------------
+ inline std::string toString(int i_in)
+ {
+ char intbuf[16];
+ snprintf(intbuf, sizeof(intbuf), "%d", i_in);
+ return std::string(intbuf);
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toString(unsigned int i_in)
+ {
+ char intbuf[16];
+ snprintf(intbuf, sizeof(intbuf), "%u", i_in);
+ return std::string(intbuf);
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toString(unsigned long i_in)
+ {
+ char longbuf[40];
+ snprintf(longbuf, sizeof(longbuf), "%lu", i_in);
+ return std::string(longbuf);
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toString(const char *i_in)
+ {
+ return std::string(i_in);
+ }
+
+ //----------------------------------------------------------------------
+ inline int atoi(const std::string &s_in)
+ {
+ return ::atoi(s_in.c_str());
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toHex(const std::string &s)
+ {
+ const char hexchars[] = "0123456789abcdef";
+ std::string tmp;
+ for (std::string::const_iterator i = s.begin(); i != s.end(); ++i) {
+ unsigned char c = (unsigned char)*i;
+ tmp += hexchars[((c & 0xf0) >> 4)];
+ tmp += hexchars[c & 0x0f];
+ }
+
+ return tmp;
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string fromHex(const std::string &s)
+ {
+ // const
+ char hexchars[] = "0123456789abcdef";
+ std::string tmp;
+ for (std::string::const_iterator i = s.begin();
+ i != s.end() && i + 1 != s.end(); i += 2) {
+ int n;
+ unsigned char c = *i;
+ unsigned char d = *(i + 1);
+
+ char *t;
+ if ((t = strchr(hexchars, c)) == 0) return "out of range";
+ n = (t - hexchars) << 4;
+
+ if ((t = strchr(hexchars, d)) == 0) return "out of range";
+ n += (t - hexchars);
+
+ if (n >= 0 && n <= 255)
+ tmp += (char) n;
+ else
+ return "out of range";
+ }
+
+ return tmp;
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toImapString(const std::string &s_in)
+ {
+ for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) {
+ unsigned char c = (unsigned char)*i;
+ if (c <= 31 || c >= 127 || c == '\"' || c == '\\')
+ return "{" + toString(s_in.length()) + "}\r\n" + s_in;
+ }
+
+ return "\"" + s_in + "\"";
+ }
+
+ //----------------------------------------------------------------------
+ inline void uppercase(std::string &input)
+ {
+ for (std::string::iterator i = input.begin(); i != input.end(); ++i)
+ *i = toupper(*i);
+ }
+
+ //----------------------------------------------------------------------
+ inline void lowercase(std::string &input)
+ {
+ for (std::string::iterator i = input.begin(); i != input.end(); ++i)
+ *i = tolower(*i);
+ }
+
+ //----------------------------------------------------------------------
+ inline void chomp(std::string &s_in, const std::string &chars = " \t\r\n")
+ {
+ int n = s_in.length();
+ while (n > 1 && chars.find(s_in[n - 1]) != std::string::npos)
+ s_in.resize(n-- - 1);
+ }
+
+ //----------------------------------------------------------------------
+ inline void trim(std::string &s_in, const std::string &chars = " \t\r\n")
+ {
+ while (s_in != "" && chars.find(s_in[0]) != std::string::npos)
+ s_in = s_in.substr(1);
+ chomp(s_in, chars);
+ }
+
+ //----------------------------------------------------------------------
+ inline const std::string unfold(const std::string &a,
+ bool removecomment = true)
+ {
+ std::string tmp;
+ bool incomment = false;
+ bool inquotes = false;
+ for (std::string::const_iterator i = a.begin(); i != a.end(); ++i) {
+ unsigned char c = (unsigned char)*i;
+ if (!inquotes && removecomment) {
+ if (c == '(') {
+ incomment = true;
+ tmp += " ";
+ } else if (c == ')') {
+ incomment = false;
+ } else if (c != 0x0a && c != 0x0d) {
+ tmp += *i;
+ }
+ } else if (c != 0x0a && c != 0x0d) {
+ tmp += *i;
+ }
+
+ if (!incomment) {
+ if (*i == '\"')
+ inquotes = !inquotes;
+ }
+ }
+
+ trim(tmp);
+ return tmp;
+ }
+
+ //----------------------------------------------------------------------
+ inline void split(const std::string &s_in, const std::string &delim,
+ std::vector<std::string> &dest, bool skipempty = true)
+ {
+ std::string token;
+ for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) {
+ if (delim.find(*i) != std::string::npos) {
+ if (!skipempty || token != "")
+ dest.push_back(token);
+ token = "";
+ } else
+ token += *i;
+ }
+
+ if (token != "")
+ dest.push_back(token);
+ }
+
+ //----------------------------------------------------------------------
+ inline void splitAddr(const std::string &s_in,
+ std::vector<std::string> &dest, bool skipempty = true)
+ {
+ static const std::string delim = ",";
+ std::string token;
+ bool inquote = false;
+ for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) {
+ if (inquote && *i == '\"') inquote = false;
+ else if (!inquote && *i == '\"') inquote = true;
+
+ if (!inquote && delim.find(*i) != std::string::npos) {
+ if (!skipempty || token != "") dest.push_back(token);
+ token = "";
+ } else
+ token += *i;
+ }
+ if (token != "")
+ dest.push_back(token);
+ }
+
+ //----------------------------------------------------------------------
+ inline std::string toCanonMailbox(const std::string &s_in)
+ {
+ if (s_in.find("..") != std::string::npos) return "";
+
+ if (s_in.length() >= 5) {
+ std::string a = s_in.substr(0, 5);
+ uppercase(a);
+ return a == "INBOX" ?
+ a + (s_in.length() > 5 ? s_in.substr(5) : "") : s_in;
+ }
+
+ return s_in;
+ }
+
+ //------------------------------------------------------------------------
+ inline std::string toRegex(const std::string &s_in, char delimiter)
+ {
+ std::string regex = "^";
+ for (std::string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) {
+ if (*i == '.' || *i == '[' || *i == ']' || *i == '{' || *i == '}' ||
+ *i == '(' || *i == ')' || *i == '^' || *i == '$' || *i == '?' ||
+ *i == '+' || *i == '\\') {
+ regex += "\\";
+ regex += *i;
+ } else if (*i == '*')
+ regex += ".*?";
+ else if (*i == '%') {
+ regex += "(\\";
+ regex += delimiter;
+ regex += "){0,1}";
+ regex += "[^\\";
+ regex += delimiter;
+ regex += "]*?";
+ } else
+ regex += *i;
+ }
+
+ if (regex[regex.length() - 1] == '?')
+ regex[regex.length() - 1] = '$';
+ else
+ regex += "$";
+
+ return regex;
+ }
+
+ //------------------------------------------------------------------------
+ class BincStream {
+ private:
+ std::string nstr;
+
+ public:
+ //--
+ BincStream &operator << (std::ostream&(*)(std::ostream&));
+ BincStream &operator << (const std::string &t);
+ BincStream &operator << (unsigned long t);
+ BincStream &operator << (unsigned int t);
+ BincStream &operator << (int t);
+ BincStream &operator << (char t);
+
+ //--
+ std::string popString(unsigned int size);
+
+ //--
+ char popChar(void);
+ void unpopChar(char c);
+ void unpopStr(const std::string &s);
+
+ //--
+ const std::string &str(void) const;
+
+ //--
+ unsigned int getSize(void) const;
+
+ //--
+ void clear(void);
+
+ //--
+ BincStream(void);
+ ~BincStream(void);
+ };
+}
+
+#endif
diff --git a/src/include/depot.h b/src/include/depot.h
new file mode 100644
index 0000000..844a987
--- /dev/null
+++ b/src/include/depot.h
@@ -0,0 +1,154 @@
+/** --------------------------------------------------------------------
+ * @filec depot.h
+ * @file Declaration of the Depot class (the mail storage)
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef depot_h_included
+#define depot_h_included
+#include <map>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+
+namespace Binc {
+
+ class Mailbox;
+ class Depot;
+ class Status;
+
+ //------------------------------------------------------------------
+ class DepotFactory {
+ private:
+ std::vector<Depot *> depots;
+ DepotFactory(void);
+
+ public:
+ void assign(Depot *);
+ Depot *get(const std::string &name) const;
+
+ static DepotFactory &getInstance(void);
+ ~DepotFactory(void);
+ };
+
+ //------------------------------------------------------------------
+ class Depot {
+ public:
+ //--
+ class iterator {
+ public:
+ std::string operator * (void) const;
+ void operator ++ (void);
+ bool operator != (iterator) const;
+ bool operator == (iterator) const;
+
+ iterator(void);
+ iterator(const iterator &copy);
+ iterator(DIR *, struct dirent *);
+ ~iterator(void);
+
+ void deref(void);
+
+ iterator &operator =(const iterator &copy);
+
+ friend class Depot;
+
+ private:
+ DIR *dirp;
+ struct dirent *direntp;
+ int *ref;
+ };
+
+ private:
+ iterator enditerator;
+ std::vector<Mailbox *> backends;
+ Mailbox *defaultmailbox;
+ Mailbox *selectedmailbox;
+ std::vector<std::string> subscribed;
+ std::string personalNamespace;
+ std::string othersNamespace;
+ std::string sharedNamespace;
+
+ protected:
+ mutable std::string lastError;
+ std::string name;
+ char delimiter;
+ mutable std::map<std::string, Status> mailboxstatuses;
+
+ public:
+ virtual iterator begin(const std::string &) const;
+ virtual const iterator &end(void) const;
+
+ void setDelimiter(char);
+ const char getDelimiter(void) const;
+
+ virtual void assign(Mailbox *);
+
+ bool setDefaultType(const std::string &n);
+ Mailbox *getDefault(void) const;
+ virtual Mailbox *get(const std::string &path) const;
+
+ virtual bool setSelected(Mailbox *);
+ virtual Mailbox *getSelected(void) const;
+ void resetSelected(void);
+
+ bool getStatus(const std::string &s_in, Status &dest) const;
+
+ const std::string &getName(void) const;
+
+ virtual const std::string &getPersonalNamespace(void) const;
+ virtual const std::string &getOthersNamespace(void) const;
+ virtual const std::string &getSharedNamespace(void) const;
+
+ virtual std::string mailboxToFilename(const std::string &m) const = 0;
+ virtual std::string filenameToMailbox(const std::string &m) const = 0;
+
+ virtual bool createMailbox(const std::string &m) const;
+ virtual bool deleteMailbox(const std::string &m) const;
+ virtual bool renameMailbox(const std::string &m, const std::string &n) const;
+
+ const std::string &getLastError(void) const;
+ void setLastError(const std::string &error) const;
+
+ virtual std::vector<std::string> getSubscriptions(void) const;
+ virtual void subscribeTo(const std::string mailbox);
+ virtual bool unsubscribeTo(const std::string mailbox);
+ virtual void loadSubscribes(void);
+ virtual bool saveSubscribes(void) const;
+
+ //--
+ Depot(void);
+ Depot(const std::string &name);
+ virtual ~Depot(void);
+ };
+
+ //------------------------------------------------------------------
+ class MaildirPPDepot : public Depot {
+ public:
+ std::string mailboxToFilename(const std::string &m) const;
+ std::string filenameToMailbox(const std::string &m) const;
+
+ const std::string &getPersonalNamespace(void) const;
+
+ //--
+ MaildirPPDepot();
+ ~MaildirPPDepot();
+ private:
+ std::string privateNamespace;
+ };
+
+ //------------------------------------------------------------------
+ class IMAPdirDepot : public Depot {
+ public:
+ std::string mailboxToFilename(const std::string &m) const;
+ std::string filenameToMailbox(const std::string &m) const;
+
+ //--
+ IMAPdirDepot();
+ ~IMAPdirDepot();
+ };
+
+}
+
+#endif
diff --git a/src/include/globals.h b/src/include/globals.h
new file mode 100644
index 0000000..dd63b5f
--- /dev/null
+++ b/src/include/globals.h
@@ -0,0 +1,25 @@
+/** --------------------------------------------------------------------
+ * @file globals.h
+ * @brief Global constants.
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#ifndef GLOBAL_H_INCLUDED
+#define GLOBAL_H_INCLUDED
+
+#define BINC_VERSION "2.0.14"
+#define IMAP_VERSION "IMAP4rev1"
+#define BINC_CACHE "BINC-CACHE-1.0"
+#define IMAP_PORT "143"
+#define IMAPS_PORT "993"
+
+namespace Binc {
+ static const int IDLE_TIMEOUT = 30*60;
+ static const int AUTH_TIMEOUT = 60;
+ static const int AUTH_PENALTY = 5;
+ static const int TRANSFER_TIMEOUT = 20*60;
+ static const int TRANSFER_BUFFER_SIZE = 1024;
+ static const int INPUT_BUFFER_LIMIT = 8192;
+
+};
+#endif
diff --git a/src/include/imapparser.h b/src/include/imapparser.h
new file mode 100644
index 0000000..4f77985
--- /dev/null
+++ b/src/include/imapparser.h
@@ -0,0 +1,171 @@
+/** --------------------------------------------------------------------
+ * @file imapparser.h
+ * @brief Declaration of the common items for parsing IMAP input
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef imapparser_h_included
+#define imapparser_h_included
+
+/* stl includes */
+#include <string>
+#include <map>
+#include <vector>
+
+namespace Binc {
+ //------------------------------------------------------------------------
+ class SequenceSet {
+ public:
+ void addRange(unsigned int a_in, unsigned int b_in);
+ bool isInSet(unsigned int n) const;
+ void addNumber(unsigned int a_in);
+ inline bool isLimited(void) const { return limited; }
+
+ static SequenceSet &all(void);
+
+ static SequenceSet &null(void);
+
+ SequenceSet &operator = (const SequenceSet &copy);
+
+ SequenceSet(void);
+ SequenceSet(const SequenceSet &copy);
+ ~SequenceSet(void);
+
+ protected:
+ bool isNull(void) const;
+
+ private:
+ bool limited;
+ bool nullSet;
+
+ class Range {
+ public:
+ unsigned int from;
+ unsigned int to;
+ Range(unsigned int from, unsigned int to);
+ };
+
+ std::vector<Range> internal;
+ };
+
+ //------------------------------------------------------------------------
+ class BincImapParserFetchAtt {
+ public:
+ std::string type;
+ std::string section;
+ std::string sectiontext;
+ std::vector<std::string> headerlist;
+ unsigned int offsetstart;
+ unsigned int offsetlength;
+ bool hassection;
+
+ BincImapParserFetchAtt(const std::string &typeName = "");
+
+ std::string toString(void);
+ };
+
+ //------------------------------------------------------------------------
+ class BincImapParserSearchKey {
+ public:
+ std::string name;
+ std::string date;
+ std::string astring;
+ std::string bstring;
+ int type;
+ unsigned int number;
+ SequenceSet bset;
+ enum {KEY_AND, KEY_OR, KEY_NOT, KEY_OTHER, KEY_SET};
+
+ std::vector<BincImapParserSearchKey> children;
+
+ const SequenceSet& getSet(void) const;
+
+ BincImapParserSearchKey(void);
+ };
+
+ //------------------------------------------------------------------------
+ class BincImapParserData {
+ public:
+ virtual ~BincImapParserData(void) {}
+ };
+
+ //------------------------------------------------------------------------
+ class Request {
+ private:
+ std::string tag;
+ std::string name;
+ std::string mode;
+ std::string date;
+ std::string userid;
+ std::string password;
+ std::string mailbox;
+ std::string newmailbox;
+ std::string authtype;
+ std::string listmailbox;
+ std::string charset;
+ std::string literal;
+ std::string contextInfo;
+ bool uidmode;
+
+ public:
+ BincImapParserData * extra;
+ std::vector<std::string> flags;
+ std::vector<std::string> statuses;
+
+ SequenceSet bset;
+ BincImapParserSearchKey searchkey;
+ std::vector<BincImapParserFetchAtt> fatt;
+
+ void setUidMode(void);
+ bool getUidMode(void) const;
+
+ void setTag(std::string &t_in);
+ const std::string &getTag(void) const;
+
+ void setMode(const std::string &m_in);
+ const std::string &getMode(void) const;
+
+ void setName(const std::string &s_in);
+ const std::string &getName(void) const;
+
+ void setLiteral(const std::string &s_in);
+ const std::string &getLiteral(void) const;
+
+ void setDate(const std::string &s_in);
+ const std::string &getDate(void) const;
+
+ void setCharSet(const std::string &s_in);
+ const std::string &getCharSet(void) const;
+
+ void setUserID(const std::string &s_in);
+ const std::string &getUserID(void) const;
+
+ void setPassword(const std::string &s_in);
+ const std::string &getPassword(void) const;
+
+ void setMailbox(const std::string &s_in);
+ const std::string &getMailbox(void) const;
+
+ void setAuthType(const std::string &s_in);
+ const std::string &getAuthType(void) const;
+
+ void setNewMailbox(const std::string &s_in);
+ const std::string &getNewMailbox(void) const;
+
+ void setListMailbox(const std::string &s_in);
+ const std::string &getListMailbox(void) const;
+
+ void setContextInfo(const std::string &s_in);
+ const std::string &getContextInfo(void) const;
+
+ SequenceSet &getSet(void);
+
+ std::vector<std::string> &getFlags(void);
+ std::vector<std::string> &getStatuses(void);
+
+ Request(void);
+ ~Request(void);
+ };
+}
+
+#endif
diff --git a/src/include/imapserver.h b/src/include/imapserver.h
new file mode 100644
index 0000000..940234e
--- /dev/null
+++ b/src/include/imapserver.h
@@ -0,0 +1,37 @@
+/** --------------------------------------------------------------------
+ * @file imapserver.h
+ * @brief Declaration of the IMAPServer class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+
+namespace Binc {
+
+ class IMAPServer {
+ public:
+ IMAPServer(int argc, char *argv[]);
+ ~IMAPServer(void);
+
+ int initialize(void);
+ int runStub(void);
+ int run(void);
+
+ enum ServerStatus {
+ OK,
+ RequestError,
+ RequestIgnore, // required for StartTLS, premature answer
+ RequestRejected,
+ ClientDisconnected,
+ Timeout
+ };
+
+ private:
+ void prepareForNextRequest(void);
+
+ int argc;
+ char **argv;
+ bool stubMode;
+
+ ServerStatus serverStatus;
+ };
+}
diff --git a/src/include/iodevice.h b/src/include/iodevice.h
new file mode 100644
index 0000000..070ac3f
--- /dev/null
+++ b/src/include/iodevice.h
@@ -0,0 +1,382 @@
+/** --------------------------------------------------------------------
+ * @file iodevice.h
+ * @brief Declaration of the IODevice class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#ifndef iodevice_h_included
+#define iodevice_h_included
+
+#include "convert.h" // BincStream
+//#include <iostream>
+#include <string>
+#include <unistd.h> // ::write
+
+const char CMS_END_OF_LINE[4] = { 0x0d, '\n', 0x00, 0x00 };
+
+namespace Binc {
+ /*!
+ \class IODevice
+ \brief The IODevice class provides a framework for reading and
+ writing to device.
+
+ Implement new devices by inheriting this class and overloading all
+ virtual methods.
+
+ service() returns the service that the specific device is used
+ for. Two values are "log" and "client".
+
+ \sa IOFactory, MultilogDevice, SyslogDevice, StdIODevice, SSLDevice
+ */
+ class IODevice {
+ public:
+ /*!
+ Standard options for an IODevice.
+ */
+ enum Flags {
+ None = 0,
+ FlushesOnEndl = 1 << 0,
+ HasInputLimit = 1 << 1,
+ HasOutputLimit = 1 << 2,
+ IsEnabled = 1 << 3,
+ HasTimeout = 1 << 4
+ };
+
+ /*!
+ Errors from when an operation returned false.
+ */
+ enum Error {
+ Unknown,
+ Timeout
+ };
+
+ /*!
+ Constructs an invalid IODevice.
+
+ Instances of IODevice perform no operations, and all boolean
+ functions always return false. This constructor is only useful
+ if called from a subclass that reimplements all virtual methods.
+ */
+ IODevice(int f = 0);
+
+ /*!
+ Destructs an IODevice; does nothing.
+ */
+ virtual ~IODevice(void);
+
+ /*!
+ Clears all data in the input and output buffers.
+ */
+ void clear(void);
+
+ /*!
+ Sets one or more flags.
+ \param f A bitwise OR of flags from the Flags enum.
+ */
+ void setFlags(unsigned int f);
+
+ /*!
+ Clears one or more flags.
+ \param f A bitwise OR of flags from the Flags enum.
+ */
+ void clearFlags(unsigned int f);
+
+ /*!
+ Sets the maximum allowed input buffer size. If this size is
+ non-zero and exceeded, reading from the device will fail. This
+ functionality is used to prevent clients from forcing this class
+ to consume so much memory that the program crashes.
+
+ Setting the max input buffer size to 0 disables the input size
+ limit.
+
+ \param max The maximum input buffer size in bytes.
+ */
+ void setMaxInputBufferSize(unsigned int max);
+
+ /*!
+ Sets the maximum allowed output buffer size. If this size is
+ non-zero and exceeded, flush() is called implicitly.
+
+ Setting the max output buffer size to 0 disables the output size
+ limit. This is generally discouraged.
+
+ As a contrast to setMaxInputBufferSize(), this function is used
+ to bundle up consequent write calls, allowing more efficient use
+ of the underlying device as larger blocks of data are written at
+ a time.
+
+ \param max The maximum output buffer size in bytes.
+ */
+ void setMaxOutputBufferSize(unsigned int max);
+
+ /*!
+ Sets the device's internal timeout in seconds. This timeout is
+ used both when waiting for data to read and for waiting for the
+ ability to write.
+
+ If this timeout is exceeded, the read or write function that
+ triggered the timeout will fail.
+
+ Setting the timeout to 0 disables the timeout.
+
+ \param t The timeout in seconds.
+ \sa getTimeout()
+ */
+ void setTimeout(unsigned int t);
+
+ /*!
+ Returns the timeout in seconds, or 0 if there is no timeout.
+
+ \sa setTimeout()
+ */
+ unsigned int getTimeout(void) const;
+
+ enum LogLevel {
+ ErrorLevel,
+ InfoLevel,
+ WarningLevel,
+ DebugLevel
+ };
+
+ /*!
+ Sets the output level for the following write operations on this
+ device.
+
+ The output level is a number which gives the following write
+ operations a priority. You can use setOutputLevelLimit() to
+ filter the write operations valid for different operating modes.
+ This enables you to have certain write operations ignored.
+
+ For instance, if the output level is set to 0, then "Hello" is
+ written, and the output level is set to 1, followed by writing
+ "Daisy", the output level limit value will decive wether only
+ "Hello" is written, or if also "Daisy" is written.
+
+ A low value of the level gives higher priority, and a high level
+ will give low priority. The default value is 0, and write
+ operations that are done with output level 0 are never ignored.
+
+ \param level The output level
+ \sa getOutputLevel(), setOutputLevelLimit()
+ */
+ void setOutputLevel(LogLevel level);
+
+ /*!
+ Returns the current output level.
+
+ \sa setOutputLevel()
+ */
+ LogLevel getOutputLevel(void) const;
+
+ /*!
+ Sets the current output level limit. Write operations with a
+ level higher than the output level limit are ignored.
+
+ \param level The output level limit
+ \sa setOutputLevel()
+ */
+ void setOutputLevelLimit(LogLevel level);
+
+ /*!
+ Returns the current output level limit.
+
+ \sa setOutputLevelLimit()
+ */
+ LogLevel getOutputLevelLimit(void) const;
+
+ /*!
+ Returns the number of bytes that have been read from this device
+ since it was created.
+ */
+ unsigned int getReadCount(void) const;
+
+ /*!
+ Returns the number of bytes that have been written to this
+ device since it was created.
+ */
+ unsigned int getWriteCount(void) const;
+
+ /*!
+ Calling this function enables the built-in protocol dumping feature in
+ the device. All input and output to this device will be dumped to a file
+ in /tmp.
+ */
+ void enableProtocolDumping(void);
+
+ /*!
+ Writes data to the device. Depending on the value of the max
+ output buffer size, the data may not be written immediately.
+
+ \sa setMaxOutputBufferSize()
+ */
+ template <class T> IODevice &operator << (const T &source);
+
+ /*!
+ Writes data to the device. This function specializes on standard
+ ostream derivates, such as std::endl.
+ */
+ IODevice &operator << (std::ostream &(*source)(std::ostream &));
+
+ /*!
+ Returns true if data can be read from the device; otherwise
+ returns false.
+ */
+ virtual bool canRead(void) const;
+
+ /*!
+ Reads data from the device, and stores this in a string. Returns
+ true on success; otherwise returns false.
+
+ \param dest The incoming data is stored in this string.
+ \param max No more than this number of bytes is read from the
+ device.
+ */
+ bool readStr(std::string *dest, unsigned int max = 0);
+
+ /*!
+ Reads exactly one byte from the device and stores this in a
+ char. Returns true on success; otherwise returns false.
+
+ \param dest The incoming byte is stored in this char.
+ */
+ bool readChar(char *dest = 0);
+
+ /*!
+ FIXME: add docs
+ */
+ void unreadChar(char c);
+
+ /*!
+ FIXME: add docs
+ */
+ void unreadStr(const std::string &s);
+
+ /*!
+ Reads characters from the device, until and including one
+ certain character is found. All read characters are discarded.
+
+ This function can be used to skip to the beginning of a line,
+ with the terminating character being '\n'.
+
+ \param The certain character.
+ */
+ bool skipTo(char c);
+
+ /*!
+ Flushes the output buffer. Writes all data in the output buffer
+ to the device.
+ */
+ bool flush(void);
+
+ /*!
+ Returns the type of error that most recently occurred.
+ */
+ Error getLastError(void) const;
+
+ /*!
+ Returns a human readable description of the error that most
+ recently occurred. If no known error has occurred, this method
+ returns "Unknown error".
+ */
+ std::string getLastErrorString(void) const;
+
+ /*!
+ Returns the type of service provided by this device. Two valid
+ return values are "client" and "log".
+ */
+ virtual std::string service(void) const;
+
+ protected:
+ /*!
+ Waits until data can be written to the device. If the timeout is
+ 0, this function waits indefinitely. Otherwise, it waits until
+ the timeout has expired.
+
+ If this function returns true, data can be written to the
+ device; otherwise, getLastError() must be checked to determine
+ whether a timeout occurred or whether an error with the device
+ prevents further writing.
+ */
+ virtual bool waitForWrite(void) const;
+
+ /*!
+ Waits until data can be read from the device.
+
+ \sa waitForWrite()
+ */
+ virtual bool waitForRead(void) const;
+
+ /*!
+ Types of results from a write.
+ */
+ enum WriteResult {
+ WriteWait = 0,
+ WriteDone = 1 << 0,
+ WriteError = 1 << 1
+ };
+
+ /*!
+ Writes as much data as possible to the device. If some but not
+ all data was written, returns WriteWait. If all data was
+ written, returns WriteDone. If an error occurred, returns
+ WriteError.
+ */
+ virtual WriteResult write(void);
+
+ /*!
+ Reads data from the device, and stores it in the input buffer.
+ Returns true on success; otherwise returns false.
+
+ This method will fail if there is no more data available, if a
+ timeout occurred or if an error with the device prevents more
+ data from being read.
+
+ The number of bytes read from the device is undefined.
+ */
+ virtual bool fillInputBuffer(void);
+
+ BincStream inputBuffer;
+ BincStream outputBuffer;
+
+ protected:
+ unsigned int flags;
+ unsigned int maxInputBufferSize;
+ unsigned int maxOutputBufferSize;
+
+ unsigned int timeout;
+
+ unsigned int readCount;
+ unsigned int writeCount;
+
+ LogLevel outputLevel;
+ LogLevel outputLevelLimit;
+
+ mutable Error error;
+ mutable std::string errorString;
+
+ int dumpfd;
+ };
+
+ //----------------------------------------------------------------------
+ template <class T> IODevice &IODevice::operator << (const T &source)
+ {
+ if ((flags & IsEnabled) && outputLevel <= outputLevelLimit) {
+ outputBuffer << source;
+
+ if (dumpfd) {
+ BincStream ss;
+ ss << source;
+ ::write(dumpfd, ss.str().c_str(), ss.getSize());
+ }
+
+ if (flags & HasInputLimit)
+ if (outputBuffer.getSize() > maxOutputBufferSize)
+ flush();
+ }
+
+ return *this;
+ }
+}
+
+#endif
diff --git a/src/include/iofactory.h b/src/include/iofactory.h
new file mode 100644
index 0000000..6ebccaa
--- /dev/null
+++ b/src/include/iofactory.h
@@ -0,0 +1,53 @@
+/** --------------------------------------------------------------------
+ * @file iofactory.h
+ * @brief Declaration of the IOFactory class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#ifndef IOFACTORY_H_INCLUDED
+#define IOFACTORY_H_INCLUDED
+#include <map>
+#include <string>
+
+#include "iodevice.h"
+
+namespace Binc {
+ class IOFactory {
+ public:
+ ~IOFactory(void);
+
+ static void addDevice(IODevice *dev);
+ static IOFactory &getInstance(void);
+ static IODevice &getClient(void);
+ static IODevice &getLogger(void);
+
+ private:
+ IOFactory(void);
+
+ std::map<std::string, IODevice *> devices;
+ };
+}
+
+#define bincClient \
+ IOFactory::getClient()
+
+#if defined (DEBUG)
+//#define bincError if (false) std::cout
+#define bincError std::cerr
+// #define bincWarning if (false) std::cout
+#define bincWarning std::cerr
+#define bincDebug std::cerr
+//#define bincDebug if (false) std::cout
+#else
+#define bincError \
+ IOFactory::getLogger().setOutputLevel(IODevice::ErrorLevel);IOFactory::getLogger()
+#define bincWarning \
+ IOFactory::getLogger().setOutputLevel(IODevice::WarningLevel);IOFactory::getLogger()
+#define bincDebug \
+ IOFactory::getLogger().setOutputLevel(IODevice::DebugLevel);IOFactory::getLogger()
+#endif
+
+#define bincLog \
+ IOFactory::getLogger().setOutputLevel(IODevice::InfoLevel);IOFactory::getLogger()
+
+#endif
diff --git a/src/include/mailbox.h b/src/include/mailbox.h
new file mode 100644
index 0000000..db98cc0
--- /dev/null
+++ b/src/include/mailbox.h
@@ -0,0 +1,136 @@
+/** --------------------------------------------------------------------
+ * @file mailbox.h
+ * @brief Declaration of the Mailbox class (Mailbox is logical container)
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef mailbox_h_included
+#define mailbox_h_included
+
+#include <map>
+#include <string>
+#include <queue>
+#include <vector>
+
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "imapparser.h"
+
+namespace Binc {
+
+ class Message;
+ class Status;
+ class PendingUpdates;
+ class File;
+
+ //------------------------------------------------------------------------
+ class Mailbox {
+ public:
+
+ //----------------------------------------------------------------------
+ class BaseIterator {
+ public:
+ BaseIterator(int sqn = 0);
+ virtual ~BaseIterator(void);
+
+ virtual Message &operator *(void) = 0;
+ virtual void operator ++(void) = 0;
+ virtual bool operator !=(const BaseIterator &d) const = 0;
+ virtual bool operator ==(const BaseIterator &d) const = 0;
+
+ virtual void erase(void) = 0;
+
+ unsigned int sqnr;
+ };
+
+ //----------------------------------------------------------------------
+ class iterator {
+ public:
+ iterator(BaseIterator &i);
+
+ Message &operator *(void);
+ void operator ++(void);
+ bool operator ==(const iterator &) const;
+ bool operator !=(const iterator &) const;
+
+ unsigned int getSqnr() const;
+
+ void erase(void);
+
+ protected:
+ BaseIterator &realIterator;
+ };
+
+ enum Iterator {
+ INCLUDE_EXPUNGED = 1,
+ SKIP_EXPUNGED = 2
+ };
+
+ enum Mode {
+ UID_MODE = 4,
+ SQNR_MODE = 8
+ };
+
+ virtual iterator begin(const SequenceSet &bset, unsigned int mod = INCLUDE_EXPUNGED | SQNR_MODE) const = 0;
+ virtual iterator end(void) const = 0;
+
+ //-- Generic for one mailbox type
+ virtual bool getStatus(const std::string &, Status &) const = 0;
+ virtual bool isMailbox(const std::string &) const = 0;
+ virtual bool isMarked(const std::string &) const = 0;
+ virtual unsigned int getStatusID(const std::string &) const = 0;
+ virtual void bumpUidValidity(const std::string &) const = 0;
+
+ //-- Specific for one mailbox
+ void setReadOnly(bool readOnly);
+ bool isReadOnly(void) const;
+
+ virtual const std::string getTypeName(void) const = 0;
+ const std::string getName(void) const;
+ void setName(const std::string &name);
+
+ virtual unsigned int getMaxUid(void) const = 0;
+ virtual unsigned int getMaxSqnr(void) const = 0;
+ virtual unsigned int getUidNext(void) const = 0;
+ virtual unsigned int getUidValidity(void) const = 0;
+
+ virtual bool getUpdates(bool scan, unsigned int type,
+ PendingUpdates &updates, bool forceScan) = 0;
+
+ virtual void updateFlags(void) = 0;
+ virtual void expungeMailbox(void) = 0;
+ virtual bool selectMailbox(const std::string &name, const std::string &s_in) = 0;
+ virtual bool createMailbox(const std::string &s, mode_t mode, uid_t owner = 0, gid_t group = 0, bool root = false) = 0;
+ virtual bool deleteMailbox(const std::string &s) = 0;
+ virtual void closeMailbox(void) = 0;
+
+ virtual Message *createMessage(const std::string &mbox, time_t idate = 0) = 0;
+ virtual bool commitNewMessages(const std::string &mbox) = 0;
+ virtual bool rollBackNewMessages(void) = 0;
+ virtual bool fastCopy(Message &source, Mailbox &desttype, const std::string &destname) = 0;
+
+ const std::string &getLastError(void) const;
+ void setLastError(const std::string &error) const;
+
+ //--
+ Mailbox(void);
+ virtual ~Mailbox(void);
+
+ friend class Mailbox::iterator;
+
+ protected:
+ bool readOnly;
+
+ private:
+ Mailbox(const Mailbox &copy);
+
+ mutable std::string lastError;
+
+ std::string name;
+ };
+}
+
+#endif
diff --git a/src/include/maildir.h b/src/include/maildir.h
new file mode 100644
index 0000000..4a262b5
--- /dev/null
+++ b/src/include/maildir.h
@@ -0,0 +1,190 @@
+/** --------------------------------------------------------------------
+ * @file maildir.h
+ * @brief Declaration of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef maildir_h_included
+#define maildir_h_included
+#include <string>
+#include <vector>
+#include <map>
+
+#include "mailbox.h"
+#include "maildirmessage.h"
+
+namespace Binc {
+ static const std::string CACHEFILEVERSION = "1.0.5";
+ static const std::string UIDVALFILEVERSION = "1.0.5";
+
+ //----------------------------------------------------------------------
+ class Lock {
+ public:
+ Lock(const std::string &path);
+ ~Lock();
+
+ private:
+ std::string lock;
+ };
+
+ //------------------------------------------------------------------------
+ class MaildirIndexItem {
+ public:
+ unsigned int uid;
+ std::string fileName;
+ };
+
+ //------------------------------------------------------------------------
+ class MaildirIndex
+ {
+ private:
+ std::map<std::string, MaildirIndexItem> idx;
+
+ public:
+ void insert(const std::string &unique, unsigned int uid,
+ const std::string &fileName = "");
+ void remove(const std::string &unique);
+ void clear(void);
+ void clearFileNames(void);
+ void clearUids(void);
+ unsigned int getSize(void) const;
+ MaildirIndexItem *find(const std::string &unique);
+ };
+
+ //------------------------------------------------------------------------
+ class Maildir : public Mailbox {
+ public:
+ typedef std::map<unsigned int, MaildirMessage> MessageMap;
+
+ class iterator : public BaseIterator {
+ public:
+ iterator(void);
+ iterator(Maildir *home, MessageMap::iterator i,
+ const SequenceSet &bset,
+ unsigned int mod = INCLUDE_EXPUNGED | SQNR_MODE);
+ iterator(const iterator &copy);
+ ~iterator(void);
+
+ Message &operator *(void);
+ void operator ++(void);
+ bool operator ==(const BaseIterator &) const;
+ bool operator !=(const BaseIterator &) const;
+
+ iterator &operator =(const iterator &copy);
+
+ void erase(void);
+
+ friend class Maildir;
+
+ protected:
+ void reposition(void);
+ MaildirMessage &curMessage(void);
+
+ private:
+ Maildir *mailbox;
+ SequenceSet bset;
+ int mod;
+
+ MessageMap::iterator i;
+ unsigned int uidmax;
+ unsigned int sqnrmax;
+
+ iterator(iterator &external);
+ };
+
+ const std::string getTypeName(void) const;
+
+ Mailbox::iterator begin(const SequenceSet &bset, unsigned int mod = INCLUDE_EXPUNGED | SQNR_MODE) const;
+ Mailbox::iterator end(void) const;
+
+ unsigned int getMaxUid(void) const;
+ unsigned int getMaxSqnr(void) const;
+ unsigned int getUidValidity(void) const;
+ unsigned int getUidNext(void) const;
+
+ bool getUpdates(bool doscan, unsigned int type,
+ PendingUpdates &updates, bool forceScan);
+
+ const std::string &getPath(void) const;
+ void setPath(const std::string &path_in);
+
+ void bumpUidValidity(const std::string &) const;
+
+ unsigned int getStatusID(const std::string &) const;
+ bool getStatus(const std::string &, Status &) const;
+ void updateFlags(void);
+
+ bool isMailbox(const std::string &) const;
+ bool isMarked(const std::string &) const;
+ bool selectMailbox(const std::string &name, const std::string &s_in);
+ void closeMailbox(void);
+ void expungeMailbox(void);
+ bool createMailbox(const std::string &s, mode_t mode, uid_t owner = 0, gid_t group = 0, bool root = false);
+ bool deleteMailbox(const std::string &s);
+
+ Message *createMessage(const std::string &mbox, time_t idate = 0);
+ bool commitNewMessages(const std::string &mbox);
+ bool rollBackNewMessages(void);
+
+ bool fastCopy(Message &source, Mailbox &desttype, const std::string &destname);
+
+ //--
+ Maildir(void);
+ ~Maildir(void);
+
+ friend class Maildir::iterator;
+ friend class MaildirMessage;
+
+ protected:
+ enum ReadCacheResult {
+ Ok,
+ NoCache,
+ Error
+ };
+
+ ReadCacheResult readCache(void);
+ bool writeCache(void);
+ bool scanFileNames(void) const;
+
+ enum ScanResult {
+ Success = 0,
+ TemporaryError = 1,
+ PermanentError = 2
+ };
+
+ ScanResult scan(bool forceScan = false);
+
+ MaildirMessage *get(const std::string &id);
+ void add(MaildirMessage &m);
+
+ private:
+ std::vector<MaildirMessage> newMessages;
+
+ unsigned int uidvalidity;
+ unsigned int uidnext;
+ bool selected;
+ std::string path;
+
+ mutable iterator beginIterator;
+ mutable iterator endIterator;
+
+ mutable bool firstscan;
+ mutable bool cacheRead;
+ mutable MaildirIndex index;
+ mutable MessageMap messages;
+
+ mutable unsigned int oldrecent;
+ mutable unsigned int oldexists;
+
+ mutable time_t old_bincimap_cache_st_mtime;
+ mutable time_t old_bincimap_cache_st_ctime;
+ mutable time_t old_cur_st_mtime;
+ mutable time_t old_cur_st_ctime;
+ mutable time_t old_new_st_mtime;
+ mutable time_t old_new_st_ctime;
+
+ mutable bool mailboxchanged;
+ };
+}
+
+#endif
diff --git a/src/include/maildirmessage.h b/src/include/maildirmessage.h
new file mode 100644
index 0000000..9e9c717
--- /dev/null
+++ b/src/include/maildirmessage.h
@@ -0,0 +1,309 @@
+/** --------------------------------------------------------------------
+ * @file maildirmessage.h
+ * @brief Declaration of the MaildirMessage class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef maildirmessage_h_included
+#define maildirmessage_h_included
+#include <string>
+#include <map>
+#include <vector>
+#include <exception>
+#include <iostream>
+#include <time.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "message.h"
+#include "address.h"
+#include "mime.h"
+
+namespace Binc {
+
+ class Maildir;
+
+ /*!
+ \class MaildirMessage
+ \brief The MaildirMessage class provides an interface for
+ IMAP messages.
+
+ Mailbox independent operations and properties are available
+ through this interface.
+
+ \sa Message
+ */
+ class MaildirMessage : public Message {
+ public:
+ /*!
+ Sets the UID of a message.
+ \param uid The UID that will be set.
+ */
+ void setUID(unsigned int uid);
+
+ /*!
+ Returns the UID of a message.
+ */
+ unsigned int getUID(void) const;
+
+ /*!
+ Sets the size of the message. This size must be consistent with
+ the size reported when fetching the full message.
+
+ \param size The size of the message in characters, after
+ any conversion to CRLF.
+ */
+ void setSize(unsigned int size);
+
+ /*!
+ Returns the size of the message, optionally determining the size
+ if it is not yet known.
+
+ \param determine If render is true and the size is unknown, the
+ size will be calculated and stored implicitly. Otherwise if the
+ size is unknown, 0 is returned.
+ */
+ unsigned int getSize(bool determine = false) const;
+
+ /*!
+ Adds one or more flags to a message.
+
+ \param flags This is a bitmask of flags from the Flags enum.
+ */
+ void setStdFlag(unsigned char flags);
+
+ /*!
+ Resets all flags on a message.
+ */
+ void resetStdFlags(void);
+
+ /*!
+ Returns the flags that are set on a message.
+ */
+ unsigned char getStdFlags(void) const;
+
+ /*
+ */
+ void setCustomFlag(const std::string &flag);
+ void removeCustomFlag(const std::string &flag);
+ void resetCustomFlags(void);
+ std::vector<std::string> getCustomFlags(void) const;
+
+ /*!
+ Sets the internal flags.
+
+ \param flags a bitmask of the Flags enum.
+ */
+ void setInternalFlag(unsigned char flags);
+
+ /*!
+ Removes the internal flags.
+
+ \param flags a bitmask of the Flags enum.
+ */
+ void clearInternalFlag(unsigned char flags);
+
+ /*!
+ Returns the internal flags.
+ */
+ unsigned char getInternalFlags(void) const;
+
+ /*!
+ Sets a state in a message that indicates that no flags have been
+ changed. Used together with hasFlagsChanged() to check if the
+ flags in this message have been changed.
+ */
+ void setFlagsUnchanged(void);
+
+ /*!
+ Returns true if flags have been added or reset since the last
+ call to setFlagsUnchanged(), otherwise returns false.
+ */
+ bool hasFlagsChanged(void) const;
+
+ /*!
+ Sets the internal date of a message. This is usually the date in
+ which the message arrived in the mailbox.
+
+ \param internaldate The internal date of the message in seconds
+ since the epoch.
+ */
+ void setInternalDate(time_t internaldate);
+
+ /*!
+ Returns the internal date of the message in seconds since the
+ epoch.
+ */
+ time_t getInternalDate(void) const;
+
+ /*!
+ Reads a chunk of up to 4096 bytes from a message. Call close()
+ before readChunk() to read the first chunk from a message.
+
+ readChunk() is used for copying or appending a message to a
+ mailbox.
+
+ \param chunk The characters are stored in this string.
+ */
+ int readChunk(std::string &chunk);
+
+ /*!
+ Appends a chunk of bytes to a message. appendChunk() is used for
+ copying or appending a message to a mailbox.
+
+ \param chunk The content of this string is appended to the
+ message.
+ */
+ bool appendChunk(const std::string &chunk);
+
+ /*!
+ Resets a message and frees all allocated resources.
+ */
+ void close(void);
+
+ /*!
+ Marks the message as expunged. Equivalent to calling
+ setStdFlag() with F_EXPUNGED.
+ */
+ void setExpunged(void);
+
+ /*!
+ Removes the F_EXPUNGED flag from the message.
+ */
+ void setUnExpunged(void);
+
+ /*!
+ Returns true if the message is marked as expunged, otherwise
+ returns false.
+ */
+ bool isExpunged(void) const;
+
+ /*!
+ Returns the first occurrance of a MIME header in a message,
+ counting from the top of the message and downwards, or "" if no
+ such header is found.
+ \param header The name of the header to be fetched.
+ */
+ const std::string &getHeader(const std::string &header);
+
+ bool headerContains(const std::string &header,
+ const std::string &text);
+
+ bool bodyContains(const std::string &text);
+ bool textContains(const std::string &text);
+
+ bool printBodyStructure(bool extended = true) const;
+
+ bool printEnvelope(void) const;
+
+ bool printHeader(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders = false,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool mime = false) const;
+
+ unsigned int getHeaderSize(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders = false,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool mime = false) const;
+
+ bool printBody(const std::string &section = "",
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX) const;
+ unsigned int getBodySize(const std::string &section,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX) const;
+
+ bool printDoc(unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool onlyText = false) const;
+
+ unsigned int getDocSize(unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool onlyText = false) const;
+
+ void setUnique(const std::string &s_in);
+ const std::string &getUnique(void) const;
+
+ //--
+ MaildirMessage(Maildir &home);
+ ~MaildirMessage(void);
+
+ friend class Maildir;
+
+ bool operator < (const MaildirMessage &a) const;
+
+ MaildirMessage(const MaildirMessage &copy);
+ MaildirMessage &operator = (const MaildirMessage &copy);
+
+ enum Flags {
+ None = 0x00,
+ Expunged = 0x01,
+ FlagsChanged = 0x02,
+ JustArrived = 0x04,
+ WasWrittenTo = 0x08,
+ Committed = 0x10,
+ CustomFlagsChanged = 0x20
+ };
+
+ protected:
+ bool parseFull(void) const;
+ bool parseHeaders(void) const;
+
+ std::string getFixedFilename(void) const;
+ std::string getFileName(void) const;
+
+ void setFile(int fd);
+ int getFile(void) const;
+
+ void setSafeName(const std::string &name);
+ const std::string &getSafeName(void) const;
+
+ private:
+ mutable int fd;
+ mutable MimeDocument *doc;
+ mutable unsigned char internalFlags;
+ mutable unsigned char stdflags;
+ mutable unsigned int uid;
+ mutable unsigned int size;
+ mutable std::string unique;
+ mutable std::string safeName;
+ time_t internaldate;
+
+ Maildir &home;
+ static std::string storage;
+ std::vector<std::string> *customFlags;
+ };
+
+ //------------------------------------------------------------------------
+ class MaildirMessageCache
+ {
+ public:
+ ~MaildirMessageCache();
+
+ enum ParseStatus {
+ NotParsed,
+ HeaderParsed,
+ AllParsed
+ };
+
+ static MaildirMessageCache &getInstance(void);
+
+ void removeStatus(const MaildirMessage *);
+ void addStatus(const MaildirMessage *, ParseStatus pstat);
+ ParseStatus getStatus(const MaildirMessage *) const;
+ void clear(void);
+
+ private:
+ MaildirMessageCache();
+
+ mutable std::map<const MaildirMessage *, ParseStatus> statuses;
+ mutable std::deque<const MaildirMessage *> parsed;
+ };
+}
+
+#endif
diff --git a/src/include/message.h b/src/include/message.h
new file mode 100644
index 0000000..76607e5
--- /dev/null
+++ b/src/include/message.h
@@ -0,0 +1,151 @@
+/** --------------------------------------------------------------------
+ * @file message.h
+ * @brief Declaration of the Message class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef message_h_included
+#define message_h_included
+#include <vector>
+#include <string>
+#include <time.h>
+
+#ifndef UINTMAX
+#define UINTMAX ((unsigned int)-1)
+#endif
+
+namespace Binc {
+
+ /*!
+ \class Message
+ \brief The Message class provides an interface for
+ IMAP messages.
+
+ Mailbox independent operations and properties are available
+ through this interface.
+
+ This class is an abstract, and has no implementation.
+
+ \sa MaildirMessage
+ */
+ class Message {
+ public:
+
+ /*!
+ Standard IMAP message flags.
+
+ */
+ enum Flags {
+ F_NONE = 0x00, /*!< No flag is set */
+ F_SEEN = 0x01, /*!< The message has been seen */
+ F_ANSWERED = 0x02, /*!< The message has been answered */
+ F_DELETED = 0x04, /*!< The message is marked as deleted */
+ F_DRAFT = 0x08, /*!< The message is a draft */
+ F_RECENT = 0x10, /*!< The message arrived recently */
+ F_FLAGGED = 0x20, /*!< The message is flagged / important */
+ F_EXPUNGED = 0x40, /*!< The message has been expunged */
+ F_PASSED = 0x80 /*!< The message has been bounced */
+ };
+
+ virtual void setUID(unsigned int) = 0;
+ virtual unsigned int getUID(void) const = 0;
+
+ virtual void setSize(unsigned int) = 0;
+ virtual unsigned int getSize(bool render = false) const = 0;
+
+ virtual void setStdFlag(unsigned char) = 0;
+ virtual void resetStdFlags(void) = 0;
+ virtual unsigned char getStdFlags(void) const = 0;
+
+ virtual void setCustomFlag(const std::string &flag) = 0;
+ virtual void removeCustomFlag(const std::string &flag) = 0;
+ virtual void resetCustomFlags(void) = 0;
+ virtual std::vector<std::string> getCustomFlags(void) const = 0;
+
+ virtual void setFlagsUnchanged(void) = 0;
+ virtual bool hasFlagsChanged(void) const = 0;
+
+ virtual void setInternalDate(time_t) = 0;
+ virtual time_t getInternalDate(void) const = 0;
+
+ // virtual void rewind(void) = 0;
+ virtual int readChunk(std::string &) = 0;
+ virtual bool appendChunk(const std::string &) = 0;
+ virtual void close(void) = 0;
+
+ virtual void setExpunged(void) = 0;
+ virtual void setUnExpunged(void) = 0;
+ virtual bool isExpunged(void) const = 0;
+
+ virtual const std::string &getHeader(const std::string &header) = 0;
+
+ virtual bool headerContains(const std::string &header,
+ const std::string &text) = 0;
+
+ virtual bool bodyContains(const std::string &text) = 0;
+ virtual bool textContains(const std::string &text) = 0;
+
+ virtual bool printBodyStructure(bool extended = true) const = 0;
+
+ virtual bool printEnvelope(void) const = 0;
+
+ virtual bool printHeader(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders = false,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool mime = false) const = 0;
+
+ virtual unsigned int getHeaderSize(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders = false,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool mime = false) const = 0;
+
+ virtual bool printBody(const std::string &section,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX) const = 0;
+
+ virtual unsigned int getBodySize(const std::string &section,
+ unsigned int startOffset = 0,
+ unsigned int length = UINTMAX) const = 0;
+
+ virtual bool printDoc(unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool onlyText = false) const = 0;
+
+ virtual unsigned int getDocSize(unsigned int startOffset = 0,
+ unsigned int length = UINTMAX,
+ bool onlyText = false) const = 0;
+
+ Message(void);
+ virtual ~Message(void);
+
+ void setLastError(const std::string &) const;
+ const std::string &getLastError(void) const;
+
+ private:
+ static std::string lastError;
+ };
+
+ inline Message::Message(void)
+ {
+ }
+
+ inline Message::~Message(void)
+ {
+ }
+
+ inline void Message::setLastError(const std::string &error) const
+ {
+ lastError = error;
+ }
+
+ inline const std::string &Message::getLastError(void) const
+ {
+ return lastError;
+ }
+}
+
+#endif
diff --git a/src/include/mime-inputsource.h b/src/include/mime-inputsource.h
new file mode 100644
index 0000000..e37d508
--- /dev/null
+++ b/src/include/mime-inputsource.h
@@ -0,0 +1,141 @@
+/** --------------------------------------------------------------------
+ * @file mime-inputsource.h
+ * @brief The base class of the MIME input source
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef mime_inputsource_h_included
+#define mime_inputsource_h_included
+
+#include <string.h>
+#include <unistd.h>
+
+namespace Binc {
+
+ class MimeInputSource {
+ public:
+ inline MimeInputSource(int fd, unsigned int start = 0);
+ virtual inline ~MimeInputSource(void);
+
+ virtual inline bool fillInputBuffer(void);
+ virtual inline void reset(void);
+
+ inline void seek(unsigned int offset);
+ inline bool getChar(char *c);
+ inline void ungetChar(void);
+ inline int getFileDescriptor(void) const;
+
+ inline unsigned int getOffset(void) const;
+
+ private:
+ int fd;
+ char data[16384];
+ unsigned int offset;
+ unsigned int tail;
+ unsigned int head;
+ unsigned int start;
+ char lastChar;
+ };
+
+ inline MimeInputSource::MimeInputSource(int fd, unsigned int start)
+ {
+ this->fd = fd;
+ this->start = start;
+ offset = 0;
+ tail = 0;
+ head = 0;
+ lastChar = '\0';
+ memset(data, '\0', sizeof(data));
+
+ seek(start);
+ }
+
+ inline MimeInputSource::~MimeInputSource(void)
+ {
+ }
+
+ inline bool MimeInputSource::fillInputBuffer(void)
+ {
+ char raw[4096];
+ ssize_t nbytes = read(fd, raw, sizeof(raw));
+ if (nbytes <= 0) {
+ // FIXME: If ferror(crlffile) we should log this.
+ return false;
+ }
+
+ for (ssize_t i = 0; i < nbytes; ++i) {
+ const char c = raw[i];
+ if (c == '\r') {
+ if (lastChar == '\r') {
+ data[tail++ & (0x4000-1)] = '\r';
+ data[tail++ & (0x4000-1)] = '\n';
+ }
+ } else if (c == '\n') {
+ data[tail++ & (0x4000-1)] = '\r';
+ data[tail++ & (0x4000-1)] = '\n';
+ } else {
+ if (lastChar == '\r') {
+ data[tail++ & (0x4000-1)] = '\r';
+ data[tail++ & (0x4000-1)] = '\n';
+ }
+ data[tail++ & (0x4000-1)] = c;
+ }
+
+ lastChar = c;
+ }
+
+ return true;
+ }
+
+ inline void MimeInputSource::reset(void)
+ {
+ offset = head = tail = 0;
+ lastChar = '\0';
+
+ if (fd != -1)
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ inline void MimeInputSource::seek(unsigned int seekToOffset)
+ {
+ if (offset > seekToOffset)
+ reset();
+
+ char c;
+ int n = 0;
+ while (seekToOffset > offset) {
+ if (!getChar(&c)) break;
+ ++n;
+ }
+ }
+
+ inline bool MimeInputSource::getChar(char *c)
+ {
+ if (head == tail && !fillInputBuffer())
+ return false;
+
+ *c = data[head++ & (0x4000-1)];
+ ++offset;
+ return true;
+ }
+
+ inline void MimeInputSource::ungetChar()
+ {
+ --head;
+ --offset;
+ }
+
+ inline int MimeInputSource::getFileDescriptor(void) const
+ {
+ return fd;
+ }
+
+ inline unsigned int MimeInputSource::getOffset(void) const
+ {
+ return offset;
+ }
+}
+
+extern Binc::MimeInputSource *mimeSource;
+
+#endif
diff --git a/src/include/mime-utils.h b/src/include/mime-utils.h
new file mode 100644
index 0000000..732234a
--- /dev/null
+++ b/src/include/mime-utils.h
@@ -0,0 +1,27 @@
+/** --------------------------------------------------------------------
+ * @file mime.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef mime_utils_h_included
+#define mime_utils_h_included
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+inline bool compareStringToQueue(const char *s_in, char *bqueue,
+ int pos, int size)
+{
+ for (int i = 0; i < size; ++i)
+ if (s_in[i] != bqueue[(pos + i) % size])
+ return false;
+
+ return true;
+}
+
+#endif
diff --git a/src/include/mime.h b/src/include/mime.h
new file mode 100644
index 0000000..2033442
--- /dev/null
+++ b/src/include/mime.h
@@ -0,0 +1,121 @@
+/** --------------------------------------------------------------------
+ * @file mime.h
+ * @brief Declaration of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date Andreas Aardal Hanssen
+ * ----------------------------------------------------------------- **/
+#ifndef mime_h_included
+#define mime_h_included
+#include <string>
+#include <vector>
+#include <map>
+#include <stdio.h>
+
+namespace Binc {
+ //----------------------------------------------------------------------
+ class HeaderItem {
+ private:
+ mutable std::string key;
+ mutable std::string value;
+
+ public:
+ inline const std::string &getKey(void) const { return key; }
+ inline const std::string &getValue(void) const { return value; }
+
+ //--
+ HeaderItem(void);
+ HeaderItem(const std::string &key, const std::string &value);
+ };
+
+ //----------------------------------------------------------------------
+ class Header {
+ private:
+ mutable std::vector<HeaderItem> content;
+
+ public:
+ bool getFirstHeader(const std::string &key, HeaderItem &dest) const;
+ bool getAllHeaders(const std::string &key, std::vector<HeaderItem> &dest) const;
+ void add(const std::string &name, const std::string &content);
+ void clear(void) const;
+
+ //--
+ Header(void);
+ ~Header(void);
+ };
+
+ //----------------------------------------------------------------------
+ class IODevice;
+ class MimeDocument;
+ class MimePart {
+ protected:
+ public:
+ mutable bool multipart;
+ mutable bool messagerfc822;
+ mutable std::string subtype;
+ mutable std::string boundary;
+
+ mutable unsigned int headerstartoffsetcrlf;
+ mutable unsigned int headerlength;
+
+ mutable unsigned int bodystartoffsetcrlf;
+ mutable unsigned int bodylength;
+ mutable unsigned int nlines;
+ mutable unsigned int nbodylines;
+ mutable unsigned int size;
+
+ public:
+ enum FetchType {
+ FetchBody,
+ FetchHeader,
+ FetchMime
+ };
+
+ mutable Header h;
+
+ mutable std::vector<MimePart> members;
+
+ inline const std::string &getSubType(void) const { return subtype; }
+ inline bool isMultipart(void) const { return multipart; }
+ inline bool isMessageRFC822(void) const { return messagerfc822; }
+ inline unsigned int getSize(void) const { return bodylength; }
+ inline unsigned int getNofLines(void) const { return nlines; }
+ inline unsigned int getNofBodyLines(void) const { return nbodylines; }
+ inline unsigned int getBodyLength(void) const { return bodylength; }
+ inline unsigned int getBodyStartOffset(void) const { return bodystartoffsetcrlf; }
+
+ void printBody(int fd, Binc::IODevice &output, unsigned int startoffset, unsigned int length) const;
+ void printHeader(int fd, Binc::IODevice &output, std::vector<std::string> headers,
+ bool includeheaders, unsigned int startoffset, unsigned int length, std::string &storage) const;
+ void printDoc(int fd, Binc::IODevice &output, unsigned int startoffset, unsigned int length) const;
+ virtual void clear(void) const;
+
+ const MimePart *getPart(const std::string &findpart, std::string genpart, FetchType fetchType = FetchBody) const;
+ virtual int parseOnlyHeader(const std::string &toboundary) const;
+ virtual int parseFull(const std::string &toboundary, int &boundarysize) const;
+
+ MimePart(void);
+ virtual ~MimePart(void);
+ };
+
+ //----------------------------------------------------------------------
+ class MimeDocument : public MimePart {
+ private:
+ mutable bool headerIsParsed;
+ mutable bool allIsParsed;
+
+ public:
+ void parseOnlyHeader(int fd) const;
+ void parseFull(int fd) const;
+ void clear(void) const;
+
+ inline bool isHeaderParsed(void) { return headerIsParsed; }
+ inline bool isAllParsed(void) { return allIsParsed; }
+
+ //--
+ MimeDocument(void);
+ ~MimeDocument(void);
+ };
+
+};
+
+#endif
diff --git a/src/include/multilogdevice.h b/src/include/multilogdevice.h
new file mode 100644
index 0000000..a6f29f2
--- /dev/null
+++ b/src/include/multilogdevice.h
@@ -0,0 +1,29 @@
+/** --------------------------------------------------------------------
+ * @file multilogdevice.h
+ * @brief Declaration of the MultilogDevice class.
+ * @author Andreas Aardal Hanssen
+ * @date Andreas Aardal Hanssen
+ * ----------------------------------------------------------------- **/
+#ifndef multilogdevice_h_included
+#define multilogdevice_h_included
+
+#include "iodevice.h"
+
+namespace Binc {
+ class MultilogDevice : public IODevice {
+ public:
+ MultilogDevice(int flags);
+ ~MultilogDevice();
+
+ std::string service(void) const;
+
+ protected:
+ bool waitForWrite(void) const;
+ bool waitForRead(void) const;
+
+ WriteResult write(void);
+ bool fillInputBuffer(void);
+ };
+}
+
+#endif
diff --git a/src/include/operators.h b/src/include/operators.h
new file mode 100644
index 0000000..c030918
--- /dev/null
+++ b/src/include/operators.h
@@ -0,0 +1,476 @@
+/** --------------------------------------------------------------------
+ * @file operators.h
+ * @brief Declaration of all operators.
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#ifndef operators_h_included
+#define operators_h_included
+#include <string>
+#include <vector>
+
+#include "imapparser.h"
+#include "depot.h"
+#include "message.h"
+
+namespace Binc {
+
+ //--------------------------------------------------------------------
+ class Operator {
+ public:
+ enum ProcessResult {OK, BAD, NO, NOTHING, ABORT};
+ enum ParseResult {ACCEPT, REJECT, ERROR, TIMEOUT};
+
+ virtual ProcessResult process(Depot &, Request &) = 0;
+ virtual ParseResult parse(Request &) const = 0;
+ virtual int getState(void) const = 0;
+ virtual const std::string getName(void) const = 0;
+
+ //--
+ virtual ~Operator(void) {};
+ };
+
+ //--------------------------------------------------------------------
+ class AppendOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ AppendOperator(void);
+ ~AppendOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class AuthenticateOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ ProcessResult Login(std::string& username, std::string& password);
+ ProcessResult Plain(std::string& username, std::string& password);
+ ProcessResult Cram(std::string& username, std::string& password,
+ std::string& challenge);
+
+ AuthenticateOperator(void);
+ ~AuthenticateOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class CapabilityOperator : public Operator {
+ std::vector<std::string> capabilities;
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ void addCapability(const std::string &cap);
+
+ CapabilityOperator(void);
+ ~CapabilityOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class CheckOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ CheckOperator(void);
+ ~CheckOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class CreateOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ CreateOperator(void);
+ ~CreateOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class CloseOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ CloseOperator(void);
+ ~CloseOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class CopyOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ CopyOperator(void);
+ ~CopyOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class DeleteOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ DeleteOperator(void);
+ ~DeleteOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class ExpungeOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ ExpungeOperator(void);
+ ~ExpungeOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class FetchOperator : public Operator {
+ protected:
+ ParseResult expectSectionText(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectSection(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectFetchAtt(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectOffset(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectHeaderList(BincImapParserFetchAtt &f_in) const;
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ FetchOperator(void);
+ ~FetchOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class IdOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ IdOperator(void);
+ ~IdOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class IdleOperator : public Operator {
+ protected:
+ ParseResult expectSectionText(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectSection(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectFetchAtt(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectOffset(BincImapParserFetchAtt &f_in) const;
+ ParseResult expectHeaderList(BincImapParserFetchAtt &f_in) const;
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ IdleOperator(void);
+ ~IdleOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class ListOperator : public Operator {
+ protected:
+ enum MailboxFlags {
+ DIR_SELECT = 0x01,
+ DIR_MARKED = 0x02,
+ DIR_NOINFERIORS = 0x04,
+ DIR_LEAF = 0x08
+ };
+
+ std::map<std::string, unsigned int> cache;
+ time_t cacheTimeout;
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ ListOperator(void);
+ ~ListOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class LoginOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ LoginOperator(void);
+ ~LoginOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class LogoutOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ LogoutOperator(void);
+ ~LogoutOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class LsubOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ LsubOperator(void);
+ ~LsubOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class NamespaceOperator : public Operator {
+ public:
+ virtual ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ NamespaceOperator(void);
+ ~NamespaceOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class NoopOperator : public Operator {
+ public:
+ virtual ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ NoopOperator(void);
+ ~NoopOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class NoopPendingOperator : public NoopOperator {
+ public:
+ ProcessResult process(Depot &, Request &);
+
+ NoopPendingOperator(void);
+ ~NoopPendingOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class RenameOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ RenameOperator(void);
+ ~RenameOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class SearchOperator : public Operator {
+ protected:
+ ParseResult expectSearchKey(BincImapParserSearchKey &s_in) const;
+
+ //------------------------------------------------------------------
+ class SearchNode {
+
+ std::string date;
+ std::string astring;
+ std::string bstring;
+ unsigned int number;
+
+ int type;
+ mutable int weight;
+ const SequenceSet *bset;
+
+ std::vector<SearchNode> children;
+
+ public:
+ enum {
+ S_ALL, S_ANSWERED, S_BCC, S_BEFORE, S_BODY, S_CC, S_DELETED,
+ S_FLAGGED, S_FROM, S_KEYWORD, S_NEW, S_OLD, S_ON, S_RECENT,
+ S_SEEN, S_SINCE, S_SUBJECT, S_TEXT, S_TO, S_UNANSWERED,
+ S_UNDELETED, S_UNFLAGGED, S_UNKEYWORD, S_UNSEEN, S_DRAFT,
+ S_HEADER, S_LARGER, S_NOT, S_OR, S_SENTBEFORE, S_SENTON,
+ S_SENTSINCE, S_SMALLER, S_UID, S_UNDRAFT, S_SET, S_AND
+ };
+
+ static bool convertDate(const std::string &date, time_t &t, const std::string &delim = "-");
+ static bool convertDateHeader(const std::string &d_in, time_t &t);
+
+ void order(void);
+
+ bool match(Mailbox *, Message *,
+ unsigned seqnr, unsigned int lastmessage,
+ unsigned int lastuid) const;
+
+ int getType(void) const;
+ int getWeight(void) const;
+ void setWeight(int i);
+
+ void init(const BincImapParserSearchKey &a);
+
+ //-
+ static bool compareNodes(const SearchNode &a,
+ const SearchNode &b)
+ {
+ return a.getWeight() < b.getWeight();
+ }
+
+ SearchNode(void);
+ SearchNode(const BincImapParserSearchKey &a);
+ };
+
+ public:
+
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ SearchOperator(void);
+ ~SearchOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class SelectOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ SelectOperator(void);
+ ~SelectOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class ExamineOperator : public SelectOperator {
+ public:
+ const std::string getName(void) const;
+ ExamineOperator(void);
+ ~ExamineOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class StarttlsOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+ int goStartTLS(void) const;
+
+ StarttlsOperator(void);
+ ~StarttlsOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class StatusOperator : public Operator {
+
+ std::map<int, Status> statuses;
+
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ StatusOperator(void);
+ ~StatusOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class StoreOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ StoreOperator(void);
+ ~StoreOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class SubscribeOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ SubscribeOperator(void);
+ ~SubscribeOperator(void);
+ };
+
+ //--------------------------------------------------------------------
+ class UnsubscribeOperator : public Operator {
+ public:
+ ProcessResult process(Depot &, Request &);
+ virtual ParseResult parse(Request &) const;
+
+ const std::string getName(void) const;
+ int getState(void) const;
+
+ UnsubscribeOperator(void);
+ ~UnsubscribeOperator(void);
+ };
+}
+
+#endif
diff --git a/src/include/pendingupdates.h b/src/include/pendingupdates.h
new file mode 100644
index 0000000..ea192de
--- /dev/null
+++ b/src/include/pendingupdates.h
@@ -0,0 +1,107 @@
+/** --------------------------------------------------------------------
+ * @file pendingupdates.h
+ * @brief <--->
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <map>
+#include <vector>
+
+#ifndef pendingupdates_h_included
+#define pendingupdates_h_included
+
+namespace Binc {
+ class Mailbox;
+
+ //------------------------------------------------------------------------
+ class PendingUpdates {
+ public:
+ enum {
+ EXPUNGE = 0x01,
+ FLAGS = 0x02,
+ EXISTS = 0x04,
+ RECENT = 0x08
+ };
+
+ //----------------------------------------------------------------------
+ class expunged_const_iterator {
+ private:
+ std::vector<unsigned int>::iterator internal;
+
+ public:
+ unsigned int operator * (void) const;
+ void operator ++ (void);
+ bool operator != (expunged_const_iterator) const;
+ bool operator == (expunged_const_iterator) const;
+
+ //--
+ expunged_const_iterator(void);
+ expunged_const_iterator(std::vector<unsigned int>::iterator i);
+ };
+
+ //--
+ expunged_const_iterator beginExpunged(void);
+ expunged_const_iterator endExpunged(void);
+
+ //----------------------------------------------------------------------
+ class flagupdates_const_iterator {
+ private:
+ std::map<unsigned int, unsigned int>::iterator internal;
+ std::map<unsigned int, unsigned int> *sqnrtouid;
+ std::map<unsigned int, std::vector<std::string> > *sqnrtocflags;
+
+ public:
+ unsigned int first(void) const;
+ unsigned int second(void) const;
+ std::vector<std::string> getCustomFlags(void) const;
+ unsigned int getUID(void) const;
+
+ void operator ++ (void);
+ bool operator != (flagupdates_const_iterator) const;
+
+ //--
+ flagupdates_const_iterator(void);
+ flagupdates_const_iterator(std::map<unsigned int, unsigned int>::iterator i,
+ std::map<unsigned int, std::vector<std::string> > *,
+ std::map<unsigned int, unsigned int> *);
+ };
+
+ //--
+ flagupdates_const_iterator beginFlagUpdates(void);
+ flagupdates_const_iterator endFlagUpdates(void);
+
+ //--
+ void addExpunged(unsigned int uid);
+ void addFlagUpdates(unsigned int sqnr, unsigned int uid,
+ unsigned int flags,
+ const std::vector<std::string> &cflags);
+ void setExists(unsigned int n);
+ void setRecent(unsigned int n);
+ unsigned int getExists(void) const;
+ unsigned int getRecent(void) const;
+ bool newExists(void) const;
+ bool newRecent(void) const;
+
+ //--
+ PendingUpdates(void);
+ ~PendingUpdates(void);
+
+ private:
+ std::vector<unsigned int> expunges;
+ std::map<unsigned int, unsigned int> flagupdates;
+ std::map<unsigned int, unsigned int> sqnrtouid;
+ std::map<unsigned int, std::vector<std::string> > sqnrtocflags;
+
+ unsigned int exists;
+ unsigned int recent;
+ bool newexists;
+ bool newrecent;
+ };
+
+ bool pendingUpdates(Mailbox *, int type, bool rescan,
+ bool showAll = false,
+ bool forceScan = false,
+ bool uidfetchflags = false);
+}
+
+#endif
diff --git a/src/include/recursivedescent.h b/src/include/recursivedescent.h
new file mode 100644
index 0000000..ea8fe33
--- /dev/null
+++ b/src/include/recursivedescent.h
@@ -0,0 +1,64 @@
+/** --------------------------------------------------------------------
+ * @file recursivedescent.h
+ * @brief Declaration of a recursive descent IMAP command parser.
+ * @author Andreas Aardal Hanssen
+ * @date Andreas Aardal Hanssen
+ * ----------------------------------------------------------------- **/
+#ifndef expectcommand_h_inluded
+#define expectcommand_h_inluded
+#include <stack>
+#include <string>
+
+#include "imapparser.h"
+#include "operators.h"
+
+namespace Binc {
+
+ extern std::stack<int> inputBuffer;
+ extern int charnr;
+
+ int readChar(void);
+ void unReadChar(int c_in);
+ void unReadChar(const std::string &s_in);
+
+ Operator::ParseResult expectTag(std::string &s_in);
+ Operator::ParseResult expectTagChar(int &c_in);
+ Operator::ParseResult expectSPACE(void);
+
+ Operator::ParseResult expectFlag(std::vector<std::string> &v_in);
+
+ Operator::ParseResult expectListMailbox(std::string &s_in);
+ Operator::ParseResult expectListWildcards(int &c_in);
+
+ Operator::ParseResult expectDateTime(std::string &s_in);
+ Operator::ParseResult expectTime(std::string &s_in);
+ Operator::ParseResult expectZone(std::string &s_in);
+
+ Operator::ParseResult expectMailbox(std::string &s_in);
+ Operator::ParseResult expectAstring(std::string &s_in);
+ Operator::ParseResult expectAtom(std::string &s_in);
+ Operator::ParseResult expectAtomChar(int &i_in);
+ Operator::ParseResult expectString(std::string &s_in);
+
+ Operator::ParseResult expectDate(std::string &s_in);
+
+ Operator::ParseResult expectNumber(unsigned int &i_in);
+ Operator::ParseResult expectDigit(unsigned int &i_in);
+ Operator::ParseResult expectDigitNZ(unsigned int &i_in);
+
+ Operator::ParseResult expectLiteral(std::string &s_in);
+ Operator::ParseResult expectQuoted(std::string &s_in);
+ Operator::ParseResult expectQuotedChar(int &c_in);
+
+ Operator::ParseResult expectSet(SequenceSet &s_in);
+ Operator::ParseResult expectSequenceNum(unsigned int &i_in);
+ Operator::ParseResult expectNZNumber(unsigned int &i_in);
+
+ Operator::ParseResult expectCRLF(void);
+ Operator::ParseResult expectCR(void);
+ Operator::ParseResult expectLF(void);
+
+ Operator::ParseResult expectThisString(const std::string &s_in);
+}
+
+#endif
diff --git a/src/include/regmatch.h b/src/include/regmatch.h
new file mode 100644
index 0000000..1471e90
--- /dev/null
+++ b/src/include/regmatch.h
@@ -0,0 +1,17 @@
+/** --------------------------------------------------------------------
+ * @file regex.h
+ * @brief Declaration of miscellaneous regexp functions.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef regex_h_included
+#define regex_h_included
+
+#include <string>
+
+namespace Binc {
+
+ int regexMatch(const std::string &data_in, const std::string &p_in);
+}
+
+#endif
diff --git a/src/include/session.h b/src/include/session.h
new file mode 100644
index 0000000..94825cd
--- /dev/null
+++ b/src/include/session.h
@@ -0,0 +1,116 @@
+/** --------------------------------------------------------------------
+ * @file session.h
+ * @brief <--->
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef session_h_included
+#define session_h_included
+#include <string>
+#include <vector>
+#include <map>
+#include <sys/types.h>
+
+#include "argparser.h"
+
+namespace Binc {
+
+ class Depot;
+
+ //--------------------------------------------------------------------
+ class Session {
+ public:
+ std::map<std::string, std::string> attrs;
+
+ char **unparsedArgs;
+
+ struct {
+ bool help;
+ bool version;
+ bool ssl;
+ } command;
+
+ bool mailboxchanges;
+
+ enum State {
+ NONAUTHENTICATED = 0x01,
+ AUTHENTICATED = 0x02,
+ SELECTED = 0x04,
+ LOGOUT = 0x00
+ };
+
+ CommandLineArgs args;
+
+ int timeout() const;
+
+ bool hasEnv(const std::string &key) const;
+ std::string getEnv(const std::string &key);
+ void setEnv(const std::string &key, const std::string &value);
+
+ const int getState(void) const;
+ void setState(int n);
+ bool parseCommandLine(int argc, char * argv[]);
+ void assignCommandLineArgs(void);
+ int getWriteBytes(void) const;
+ int getReadBytes(void) const;
+ void addWriteBytes(int);
+ void addReadBytes(int);
+ int getBodies(void) const;
+ int getStatements(void) const;
+ void addBody(void);
+ void addStatement(void);
+ void setLogFacility(int facility);
+ int getLogFacility(void) const;
+
+ const std::string &getLastError(void) const;
+ const std::string &getResponseCode(void) const;
+ const std::string &getIP(void) const;
+ const std::string &getUserID() const;
+ pid_t getPid(void);
+ const std::string &getHostname(void);
+ void setLastError(const std::string &error) const;
+ void setResponseCode(const std::string &error) const;
+ void clearResponseCode(void) const;
+ void setIP(const std::string &ip);
+ void setUserID(const std::string &s);
+
+ inline Depot *getDepot(void) const;
+
+ //--
+ static Session &getInstance(void);
+
+ bool initialize(int argc, char *argv[]);
+
+ private:
+ //--
+ int state;
+ std::string userid;
+ std::string ip;
+ char **argv;
+ int argc;
+
+ int logfacility;
+
+ int readbytes;
+ int writebytes;
+ int statements;
+ int bodies;
+
+ Depot *depot;
+
+ mutable std::string lastError;
+ mutable std::string responseCode;
+
+ pid_t pid;
+ std::string hostname;
+
+ Session(void);
+ };
+
+ inline Depot *Session::getDepot(void) const
+ {
+ return depot;
+ }
+}
+
+#endif
diff --git a/src/include/status.h b/src/include/status.h
new file mode 100644
index 0000000..f7f9e1f
--- /dev/null
+++ b/src/include/status.h
@@ -0,0 +1,50 @@
+/** --------------------------------------------------------------------
+ * @file status.h
+ * @brief Declaration of the Status class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#ifndef status_h_included
+#define status_h_included
+
+namespace Binc {
+
+ //------------------------------------------------------------------------
+ class Status {
+
+ //--
+ int recent;
+ int messages;
+ int unseen;
+ int uidvalidity;
+ int uidnext;
+
+ //--
+ int statusid;
+
+ public:
+
+ //--
+ inline void setMessages(int i) { messages = i; }
+ inline void setRecent(int i) { recent = i; }
+ inline void setStatusID(int i) { statusid = i; }
+ inline void setUnseen(int i) { unseen = i; }
+ inline void setUidValidity(int i) { uidvalidity = i; }
+ inline void setUidNext(int i) { uidnext = i; }
+
+ //--
+ inline int getMessages(void) const { return messages; }
+ inline int getRecent(void) const { return recent; }
+ inline int getStatusID(void) const { return statusid; }
+ inline int getUnseen(void) const { return unseen; }
+ inline int getUidValidity(void) const { return uidvalidity; }
+ inline int getUidNext(void) const { return uidnext; }
+
+
+ //--
+ Status(void);
+ ~Status(void);
+ };
+}
+
+#endif
diff --git a/src/include/stdiodevice.h b/src/include/stdiodevice.h
new file mode 100644
index 0000000..026386c
--- /dev/null
+++ b/src/include/stdiodevice.h
@@ -0,0 +1,31 @@
+/** --------------------------------------------------------------------
+ * @file stdiodevice.h
+ * @brief Declaration of the StdIODevice class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#ifndef stdiodevice_h_included
+#define stdiodevice_h_included
+
+#include "iodevice.h"
+
+namespace Binc {
+ class StdIODevice : public IODevice {
+ public:
+ StdIODevice(int flags);
+ ~StdIODevice();
+
+ std::string service(void) const;
+
+ bool canRead(void) const;
+
+ protected:
+ bool waitForWrite(void) const;
+ bool waitForRead(void) const;
+
+ WriteResult write(void);
+ bool fillInputBuffer(void);
+ };
+}
+
+#endif
diff --git a/src/include/syslogdevice.h b/src/include/syslogdevice.h
new file mode 100644
index 0000000..2269fb2
--- /dev/null
+++ b/src/include/syslogdevice.h
@@ -0,0 +1,41 @@
+/** --------------------------------------------------------------------
+ * @file Syslogdevice.h
+ * @brief Declaration of the SyslogDevice class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#ifndef syslogdevice_h_included
+#define syslogdevice_h_included
+
+#include "iodevice.h"
+#include <syslog.h>
+
+namespace Binc {
+ class SyslogDevice : public IODevice {
+ public:
+ SyslogDevice(int flags, const char *ident = "bincimap",
+ int option = LOG_NDELAY | LOG_PID,
+ int facility = LOG_USER);
+ ~SyslogDevice();
+
+ void setPriority(int p);
+
+ std::string service(void) const;
+
+ protected:
+ bool waitForWrite(void) const;
+ bool waitForRead(void) const;
+
+ WriteResult write(void);
+ bool fillInputBuffer(void);
+
+ private:
+ static std::string ident;
+
+ int option;
+ int facility;
+ int priority;
+ };
+}
+
+#endif
diff --git a/src/include/tools.h b/src/include/tools.h
new file mode 100644
index 0000000..67ed91e
--- /dev/null
+++ b/src/include/tools.h
@@ -0,0 +1,23 @@
+/** --------------------------------------------------------------------
+ * @file tools.h
+ * @brief Declaration of miscellaneous tools.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+namespace Binc {
+
+ class Tools {
+ private:
+ Tools(void);
+
+ public:
+ void setenv(const std::string &key, const std::string &value) const;
+ std::string getenv(const std::string &key) const;
+
+ //--
+ static Tools &getInstance(void);
+ };
+
+}
diff --git a/src/iodevice.cc b/src/iodevice.cc
new file mode 100644
index 0000000..7197354
--- /dev/null
+++ b/src/iodevice.cc
@@ -0,0 +1,283 @@
+/** --------------------------------------------------------------------
+ * @file iodevice.cc
+ * @brief Implementation of the IODevice class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#include "iodevice.h"
+#include "convert.h" // BincStream
+#include "session.h" // getEnv/hasEnv
+
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace ::std;
+using namespace ::Binc;
+
+//------------------------------------------------------------------------
+IODevice::IODevice(int f) : flags(f | IsEnabled),
+ maxInputBufferSize(0),
+ maxOutputBufferSize(0),
+ timeout(0),
+ readCount(0), writeCount(0),
+ outputLevel(ErrorLevel),
+ outputLevelLimit(ErrorLevel),
+ error(Unknown), errorString("Unknown device error"),
+ dumpfd(0)
+{
+}
+
+//------------------------------------------------------------------------
+IODevice::~IODevice(void)
+{
+}
+
+//------------------------------------------------------------------------
+IODevice &IODevice::operator <<(ostream &(*source)(ostream &))
+{
+ if (!(flags & IsEnabled) || outputLevel > outputLevelLimit)
+ return *this;
+
+ static std::ostream &(*endl_funcptr)(ostream &) = endl;
+
+ if (source != endl_funcptr) return *this;
+
+ outputBuffer << "\r\n";
+
+ if (dumpfd) ::write(dumpfd, "\r\n", 2);
+
+ if (flags & FlushesOnEndl)
+ flush();
+ else if (flags & HasOutputLimit)
+ if (outputBuffer.getSize() > maxOutputBufferSize)
+ flush();
+
+ return *this;
+}
+
+//------------------------------------------------------------------------
+bool IODevice::canRead(void) const
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+void IODevice::clear()
+{
+ if (!(flags & IsEnabled)) return;
+
+ inputBuffer.clear();
+ outputBuffer.clear();
+}
+
+//------------------------------------------------------------------------
+bool IODevice::flush()
+{
+ if (!(flags & IsEnabled)) return true;
+
+ WriteResult writeResult = WriteWait;
+ do {
+ unsigned int s = outputBuffer.getSize();
+ if (s == 0) break;
+ if (!waitForWrite()) return false;
+ writeResult = write();
+ if (writeResult == WriteError) return false;
+ writeCount += s - outputBuffer.getSize();
+ } while (outputBuffer.getSize() > 0 && writeResult == WriteWait);
+
+ outputBuffer.clear();
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setFlags(unsigned int f)
+{
+ flags |= f;
+}
+
+//------------------------------------------------------------------------
+void IODevice::clearFlags(unsigned int f)
+{
+ flags &= ~f;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setMaxInputBufferSize(unsigned int max)
+{
+ maxInputBufferSize = max;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setMaxOutputBufferSize(unsigned int max)
+{
+ maxOutputBufferSize = max;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setTimeout(unsigned int t)
+{
+ timeout = t;
+
+ if (t)
+ flags |= HasTimeout;
+ else
+ flags &= ~HasTimeout;
+}
+
+//------------------------------------------------------------------------
+unsigned int IODevice::getTimeout(void) const
+{
+ return timeout;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setOutputLevel(LogLevel level)
+{
+ outputLevel = level;
+}
+
+//------------------------------------------------------------------------
+IODevice::LogLevel IODevice::getOutputLevel(void) const
+{
+ return outputLevel;
+}
+
+//------------------------------------------------------------------------
+void IODevice::setOutputLevelLimit(LogLevel level)
+{
+ outputLevelLimit = level;
+}
+
+//------------------------------------------------------------------------
+IODevice::LogLevel IODevice::getOutputLevelLimit(void) const
+{
+ return outputLevelLimit;
+}
+
+//------------------------------------------------------------------------
+bool IODevice::readStr(string *dest, unsigned int max)
+{
+ // If max is 0, fill the input buffer once only if it's empty.
+ if (!max && inputBuffer.getSize() == 0 && !fillInputBuffer()) return false;
+
+ // If max is != 0, wait until we have max.
+ while (max && inputBuffer.getSize() < max) {
+ if (!fillInputBuffer()) return false;
+ }
+
+ unsigned int bytesToRead = max ? max : inputBuffer.getSize();
+ *dest += inputBuffer.str().substr(0, bytesToRead);
+ if (dumpfd) {
+ ::write(dumpfd, inputBuffer.str().substr(0, bytesToRead).c_str(), bytesToRead);
+ }
+
+ inputBuffer.popString(bytesToRead);
+ readCount += bytesToRead;
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool IODevice::readChar(char *dest)
+{
+ if (inputBuffer.getSize() == 0 && !fillInputBuffer()) return false;
+
+ char c = inputBuffer.popChar();
+ if (dest) *dest = c;
+ if (dumpfd) ::write(dumpfd, &c, 1);
+
+ ++readCount;
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+void IODevice::unreadChar(char c)
+{
+ inputBuffer.unpopChar(c);
+}
+
+//------------------------------------------------------------------------
+void IODevice::unreadStr(const string &s)
+{
+ inputBuffer.unpopStr(s);
+}
+
+//------------------------------------------------------------------------
+bool IODevice::skipTo(char c)
+{
+ char dest = '\0';
+ do {
+ if (!readChar(&dest)) return false;
+ if (dumpfd) ::write(dumpfd, &dest, 1);
+ } while (c != dest);
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+string IODevice::service(void) const
+{
+ return "nul";
+}
+
+//------------------------------------------------------------------------
+bool IODevice::waitForWrite(void) const
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+bool IODevice::waitForRead(void) const
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+IODevice::WriteResult IODevice::write(void)
+{
+ return WriteError;
+}
+
+//------------------------------------------------------------------------
+bool IODevice::fillInputBuffer(void)
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+IODevice::Error IODevice::getLastError(void) const
+{
+ return error;
+}
+
+//------------------------------------------------------------------------
+string IODevice::getLastErrorString(void) const
+{
+ return errorString;
+}
+
+//------------------------------------------------------------------------
+unsigned int IODevice::getReadCount(void) const
+{
+ return readCount;
+}
+
+//------------------------------------------------------------------------
+unsigned int IODevice::getWriteCount(void) const
+{
+ return writeCount;
+}
+
+//------------------------------------------------------------------------
+void IODevice::enableProtocolDumping(void)
+{
+ BincStream ss;
+ ss << "/tmp/bincimap-dump-" << (int) time(0) << "-"
+ << Session::getInstance().getIP() << "-XXXXXX";
+ char *safename = strdup(ss.str().c_str());
+ dumpfd = mkstemp(safename);
+ if (dumpfd == -1) dumpfd = 0;
+ delete[] safename;
+}
diff --git a/src/iofactory.cc b/src/iofactory.cc
new file mode 100644
index 0000000..ced087c
--- /dev/null
+++ b/src/iofactory.cc
@@ -0,0 +1,66 @@
+/* --------------------------------------------------------------------
+ * @file iofactory.cc
+ * @brief Implementation of the IOFactory class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#include "iofactory.h"
+#include "iodevice.h"
+
+using namespace ::Binc;
+using namespace ::std;
+
+//------------------------------------------------------------------------
+IOFactory::IOFactory(void)
+{
+}
+
+//------------------------------------------------------------------------
+IOFactory::~IOFactory(void)
+{
+}
+
+//------------------------------------------------------------------------
+IOFactory &IOFactory::getInstance(void)
+{
+ static IOFactory ioFactory;
+ return ioFactory;
+}
+
+//------------------------------------------------------------------------
+void IOFactory::addDevice(IODevice *dev)
+{
+ IODevice *ioDevice = IOFactory::getInstance().devices[dev->service()];
+
+ // FIXME: Delete correct object. Now, only IODevice's destructor is
+ // called, and only IODevice's memory is freed.
+ if (ioDevice)
+ delete ioDevice;
+
+ IOFactory::getInstance().devices[dev->service()] = dev;
+}
+
+//------------------------------------------------------------------------
+IODevice &IOFactory::getClient(void)
+{
+ static IODevice nulDevice;
+
+ IOFactory &ioFactory = IOFactory::getInstance();
+
+ if (ioFactory.devices.find("client") != ioFactory.devices.end())
+ return *ioFactory.devices["client"];
+
+ return nulDevice;
+}
+
+//------------------------------------------------------------------------
+IODevice &IOFactory::getLogger(void)
+{
+ static IODevice nulDevice;
+
+ IOFactory &ioFactory = IOFactory::getInstance();
+
+ if (ioFactory.devices.find("log") != ioFactory.devices.end())
+ return *ioFactory.devices["log"];
+ return nulDevice;
+}
diff --git a/src/mailbox.cc b/src/mailbox.cc
new file mode 100644
index 0000000..53c8606
--- /dev/null
+++ b/src/mailbox.cc
@@ -0,0 +1,112 @@
+/** --------------------------------------------------------------------
+ * @file mailbox.cc
+ * @brief Implementation of the Mailbox class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "mailbox.h"
+#include "message.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Mailbox::BaseIterator::BaseIterator(int sqn)
+{
+ sqnr = sqn;
+}
+
+//------------------------------------------------------------------------
+Mailbox::BaseIterator::~BaseIterator(void)
+{
+}
+
+//------------------------------------------------------------------------
+Mailbox::Mailbox(void) : readOnly(false)
+{
+}
+
+//------------------------------------------------------------------------
+Mailbox::~Mailbox(void)
+{
+}
+
+//------------------------------------------------------------------------
+bool Mailbox::isReadOnly(void) const
+{
+ return readOnly;
+}
+
+//------------------------------------------------------------------------
+void Mailbox::setReadOnly(bool readOnly)
+{
+ this->readOnly = readOnly;
+}
+
+//------------------------------------------------------------------------
+Mailbox::iterator::iterator(BaseIterator &i)
+ : realIterator(i)
+{
+}
+
+//------------------------------------------------------------------------
+Message &Mailbox::iterator::operator *(void)
+{
+ return *realIterator;
+}
+
+//------------------------------------------------------------------------
+void Mailbox::iterator::operator ++(void)
+{
+ ++realIterator;
+}
+
+//------------------------------------------------------------------------
+bool Mailbox::iterator::operator ==(const iterator &i) const
+{
+ return realIterator == i.realIterator;
+}
+
+//------------------------------------------------------------------------
+bool Mailbox::iterator::operator !=(const iterator &i) const
+{
+ return realIterator != i.realIterator;
+}
+
+//------------------------------------------------------------------------
+void Mailbox::iterator::erase(void)
+{
+ realIterator.erase();
+}
+
+//------------------------------------------------------------------------
+unsigned int Mailbox::iterator::getSqnr(void) const
+{
+ return realIterator.sqnr;
+}
+
+//------------------------------------------------------------------------
+void Mailbox::setName(const string &name)
+{
+ this->name = name;
+}
+
+//------------------------------------------------------------------------
+const string Mailbox::getName(void) const
+{
+ return name;
+}
+
+//------------------------------------------------------------------------
+const string &Mailbox::getLastError(void) const
+{
+ return lastError;
+}
+
+//------------------------------------------------------------------------
+void Mailbox::setLastError(const string &error) const
+{
+ lastError = error;
+}
diff --git a/src/maildir-close.cc b/src/maildir-close.cc
new file mode 100644
index 0000000..a886d97
--- /dev/null
+++ b/src/maildir-close.cc
@@ -0,0 +1,46 @@
+/** --------------------------------------------------------------------
+ * @file maildir-close.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+void Binc::Maildir::closeMailbox(void)
+{
+ if (!selected)
+ return;
+
+ if (mailboxchanged && !readOnly)
+ writeCache();
+
+ mailboxchanged = false;
+
+ MaildirMessageCache::getInstance().clear();
+
+ messages.clear();
+ index.clear();
+ newMessages.clear();
+ oldrecent = 0;
+ oldexists = 0;
+ firstscan = true;
+ cacheRead = false;
+ uidvalidity = 0;
+ uidnext = 1;
+ selected = false;
+ path = "";
+
+ old_bincimap_cache_st_mtime = 0;
+ old_bincimap_cache_st_ctime = 0;
+ old_cur_st_mtime = 0;
+ old_cur_st_ctime = 0;
+ old_new_st_mtime = 0;
+ old_new_st_ctime = 0;
+}
diff --git a/src/maildir-create.cc b/src/maildir-create.cc
new file mode 100644
index 0000000..6bbd7d7
--- /dev/null
+++ b/src/maildir-create.cc
@@ -0,0 +1,78 @@
+/** --------------------------------------------------------------------
+ * @file maildir-create.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+bool Binc::Maildir::createMailbox(const string &s_in, mode_t mode,
+ uid_t owner, gid_t group, bool root)
+{
+ if (s_in != "." && mkdir(s_in.c_str(), mode) == -1) {
+ setLastError("unable to create " + s_in + ": "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ // Allow uidvalidity, which is generated from time(0), to
+ // increase with one second to avoid race conditions.
+ sleep(1);
+
+ if (mkdir((s_in + "/cur").c_str(), mode) == -1) {
+ setLastError("unable to create " + s_in + "/cur: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (mkdir((s_in + "/new").c_str(), mode) == -1) {
+ setLastError("unable to create " + s_in + "/new: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (mkdir((s_in + "/tmp").c_str(), mode) == -1) {
+ setLastError("unable to create " + s_in + "/tmp: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (owner == 0 && group == 0)
+ return true;
+
+ if (chown(s_in.c_str(), owner, group) == -1) {
+ setLastError("unable to chown " + s_in + ": "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (chown((s_in + "/cur").c_str(), owner, group) == -1) {
+ setLastError("unable to chown " + s_in + "/cur: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (chown((s_in + "/new").c_str(), owner, group) == -1) {
+ setLastError("unable to chown " + s_in + "/new: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ if (chown((s_in + "/tmp").c_str(), owner, group) == -1) {
+ setLastError("unable to chown " + s_in + "/tmp: "
+ + string(strerror(errno)));
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/maildir-delete.cc b/src/maildir-delete.cc
new file mode 100644
index 0000000..9ed162b
--- /dev/null
+++ b/src/maildir-delete.cc
@@ -0,0 +1,88 @@
+/** --------------------------------------------------------------------
+ * @file maildir-delete.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <unistd.h>
+
+using namespace ::std;
+using namespace Binc;
+
+namespace {
+
+ bool recursiveDelete(const string &path)
+ {
+ DIR *mydir = opendir(path.c_str());
+ if (mydir == 0)
+ return false;
+
+ struct dirent *mydirent;
+ while ((mydirent = readdir(mydir)) != 0) {
+ string d = mydirent->d_name;
+ if (d == "." || d == "..")
+ continue;
+
+ string f = path + "/" + d;
+
+ struct stat mystat;
+ if (lstat(f.c_str(), &mystat) != 0) {
+ if (errno == ENOENT)
+ continue;
+ return false;
+ }
+
+ if (S_ISDIR(mystat.st_mode)) {
+ if (!recursiveDelete(f)) {
+ closedir(mydir);
+ return false;
+ }
+ if (rmdir(f.c_str()) != 0 && errno != ENOENT) {
+ closedir(mydir);
+ return false;
+ }
+ } else {
+ if (unlink(f.c_str()) != 0 && errno != ENOENT) {
+ closedir(mydir);
+ return false;
+ }
+ }
+ }
+
+ closedir(mydir);
+ return true;
+ }
+}
+
+//------------------------------------------------------------------------
+bool Binc::Maildir::deleteMailbox(const string &s_in)
+{
+ if (s_in == ".") {
+ setLastError("disallowed by rule");
+ return false;
+ }
+
+ if (!recursiveDelete(s_in)) {
+ setLastError("error deleting Maildir - status is undefined");
+ return false;
+ }
+
+ if (rmdir(s_in.c_str()) != 0) {
+ setLastError("error deleting Maildir: "
+ + string(strerror(errno))
+ + " - status is undefined");
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/maildir-expunge.cc b/src/maildir-expunge.cc
new file mode 100644
index 0000000..3091d7f
--- /dev/null
+++ b/src/maildir-expunge.cc
@@ -0,0 +1,61 @@
+/** --------------------------------------------------------------------
+ * @file maildir-expunge.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <unistd.h>
+#include <errno.h>
+
+#include "iodevice.h"
+#include "iofactory.h"
+#include "maildir.h"
+#include "maildirmessage.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+void Maildir::expungeMailbox(void)
+{
+ if (readOnly) return;
+
+ Mailbox::iterator i = begin(SequenceSet::all(), SQNR_MODE|INCLUDE_EXPUNGED);
+
+ bool success = true;
+ for (; success && i != end(); ++i) {
+ MaildirMessage &message = reinterpret_cast<MaildirMessage &>(*i);
+
+ if ((message.getStdFlags() & Message::F_DELETED) == 0)
+ continue;
+
+ message.setExpunged();
+
+ const string &id = message.getUnique();
+
+ // The message might be gone already
+ MaildirIndexItem *item = index.find(id);
+ if (!item)
+ continue;
+
+ string fpath = path + "/cur/" + item->fileName;
+
+ while (unlink(fpath.c_str()) != 0) {
+ if (errno != ENOENT) {
+ bincWarning << "unable to remove " << fpath << ": "
+ << strerror(errno) << endl;
+ break;
+ }
+
+ if (!scanFileNames()) {
+ success = false;
+ break;
+ }
+
+ if ((item = index.find(id)))
+ break;
+ else
+ fpath = path + "/cur/" + item->fileName;
+ }
+ }
+}
diff --git a/src/maildir-readcache.cc b/src/maildir-readcache.cc
new file mode 100644
index 0000000..a108242
--- /dev/null
+++ b/src/maildir-readcache.cc
@@ -0,0 +1,151 @@
+/** --------------------------------------------------------------------
+ * @file maildir-readcache.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <algorithm>
+#include "maildir.h"
+#include "convert.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Maildir::ReadCacheResult Maildir::readCache(void)
+{
+ index.clearUids();
+
+ const string cachefilename = path + "/bincimap-cache";
+ FILE *fp = fopen(cachefilename.c_str(), "r");
+ if (!fp) {
+ uidvalidity = time(0);
+ uidnext = 1;
+ messages.clear();
+ index.clear();
+ newMessages.clear();
+ return NoCache;
+ }
+
+ char inputBuffer[512];
+ if (!fgets(inputBuffer, sizeof(inputBuffer), fp)) {
+ fclose(fp);
+ uidvalidity = time(0);
+ uidnext = 1;
+ messages.clear();
+ index.clear();
+ newMessages.clear();
+ return NoCache;
+ }
+
+ // terminate the buffer
+ inputBuffer[sizeof(inputBuffer) - 1] = '\0';
+
+ char cacheFileVersionBuffer[512];
+ unsigned int readUidValidity;
+ unsigned int readUidNext;
+
+ if (sscanf(inputBuffer, "%s %u %u", cacheFileVersionBuffer, &readUidValidity, &readUidNext) != 3
+ || strcmp(cacheFileVersionBuffer, BINC_CACHE) != 0) {
+ // bump cache
+ fclose(fp);
+ uidvalidity = time(0);
+ uidnext = 1;
+ messages.clear();
+ index.clear();
+ newMessages.clear();
+ return NoCache;
+ }
+
+ uidnext = readUidNext;
+ uidvalidity = readUidValidity;
+
+ unsigned int readUID;
+ unsigned int readSize;
+ unsigned int readInternalDate;
+ char readUnique[512];
+
+ while (fgets(inputBuffer, sizeof(inputBuffer), fp)) {
+ inputBuffer[sizeof(inputBuffer) - 1] = '\0';
+ if (sscanf(inputBuffer, "%u %u %u %s", &readUID,
+ &readInternalDate, &readSize, readUnique) != 4) {
+ // error in input
+ fclose(fp);
+ uidvalidity = time(0);
+ uidnext = 1;
+ messages.clear();
+ index.clear();
+ newMessages.clear();
+ return NoCache;
+ }
+
+ vector<string> customFlags;
+
+ char *flagStart = inputBuffer;
+ for (int i = 0; flagStart != 0 && *flagStart != '\0' && i < 4; ++i) {
+ flagStart = strchr(flagStart, ' ');
+
+ // skip consecutive white space
+ while (flagStart && *flagStart == ' ')
+ ++flagStart;
+ }
+
+ // get flags
+ while (flagStart) {
+ char *lastOffset = flagStart;
+ flagStart = strchr(flagStart, ' ');
+ if (flagStart) {
+ *flagStart = '\0';
+ ++flagStart;
+ }
+
+ customFlags.push_back(lastOffset);
+
+ // skip consecutive white space
+ while (flagStart && *flagStart != '\0' && *flagStart == ' ')
+ ++flagStart;
+ }
+
+ MaildirMessage m(*this);
+ m.setUnique(readUnique);
+ m.setInternalDate(readInternalDate);
+
+ if (index.find(readUnique) == 0) {
+ for (vector<string>::const_iterator it = customFlags.begin();
+ it != customFlags.end(); ++it) {
+ string tmpFlag = *it;
+ trim(tmpFlag, " \n");
+ m.setCustomFlag(tmpFlag);
+ }
+
+ m.setUID(readUID);
+ m.setInternalFlag(MaildirMessage::JustArrived);
+ m.setSize(readSize);
+ add(m);
+ } else {
+ // Remember to insert the uid of the message again - we reset this
+ // at the top of this function.
+ index.insert(readUnique, readUID);
+ MaildirMessage &existingMessage = messages.find(readUID)->second;
+
+ vector<string> oldCustomFlags = existingMessage.getCustomFlags();
+ sort(oldCustomFlags.begin(), oldCustomFlags.end());
+ sort(customFlags.begin(), customFlags.end());
+ if (oldCustomFlags != customFlags) {
+ existingMessage.resetCustomFlags();
+ for (vector<string>::const_iterator it = customFlags.begin();
+ it != customFlags.end(); ++it) {
+ string tmpFlag = *it;
+ trim(tmpFlag, " \n");
+ existingMessage.setCustomFlag(tmpFlag);
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+
+ return Ok;
+}
+
diff --git a/src/maildir-scan.cc b/src/maildir-scan.cc
new file mode 100644
index 0000000..4628d5b
--- /dev/null
+++ b/src/maildir-scan.cc
@@ -0,0 +1,476 @@
+/** --------------------------------------------------------------------
+ * @file maildir-scan.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "iodevice.h"
+#include "iofactory.h"
+#include "maildir.h"
+
+using namespace Binc;
+using namespace ::std;
+
+Lock::Lock(const string &path)
+{
+ lock = (path == "" ? "." : path) + "/bincimap-scan-lock";
+
+ int lockfd = -1;
+ while ((lockfd = ::open(lock.c_str(),
+ O_CREAT | O_WRONLY | O_EXCL, 0666)) == -1) {
+ if (errno != EEXIST) {
+ bincWarning << "unable to lock mailbox: " << lock
+ << ", " << string(strerror(errno)) << endl;
+ return;
+ }
+
+ struct stat mystat;
+ bincWarning << "waiting for mailbox lock " << lock << "." << endl;
+ if (lstat(lock.c_str(), &mystat) == 0) {
+ if ((time(0) - mystat.st_ctime) > 300) {
+ if (unlink(lock.c_str()) == 0) continue;
+ else bincWarning << "failed to force mailbox lock: " << lock
+ << ", " << string(strerror(errno)) << endl;
+ }
+ } else {
+ if (errno != ENOENT) {
+ string err = "invalid lock " + lock + ": "
+ + strerror(errno);
+ bincWarning << err << endl;
+ return;
+ }
+ }
+
+ // sleep one second.
+ sleep(1);
+ }
+
+ close(lockfd);
+}
+
+Lock::~Lock()
+{
+ // remove the lock
+ if (unlink(lock.c_str()) != 0)
+ bincWarning << "failed to unlock mailbox: " << lock << ", "
+ << strerror(errno) << endl;
+}
+
+//------------------------------------------------------------------------
+// scan the maildir. update flags, find messages in new/ and move them
+// to cur, setting the recent flag in memory only. check for expunged
+// messages. give newly arrived messages uids.
+//------------------------------------------------------------------------
+Maildir::ScanResult Maildir::scan(bool forceScan)
+{
+ const string newpath = path + "/new/";
+ const string curpath = path + "/cur/";
+ const string cachepath = path + "/bincimap-cache";
+
+ // check wether or not we need to bother scanning the folder.
+ if (firstscan || forceScan) {
+ struct stat oldstat;
+ if (stat(newpath.c_str(), &oldstat) != 0) {
+ setLastError("Invalid Mailbox, " + newpath + ": "
+ + string(strerror(errno)));
+ return PermanentError;
+ }
+
+ old_new_st_mtime = oldstat.st_mtime;
+ old_new_st_ctime = oldstat.st_ctime;
+
+ if (stat(curpath.c_str(), &oldstat) != 0) {
+ setLastError("Invalid Mailbox, " + curpath + ": "
+ + string(strerror(errno)));
+ return PermanentError;
+ }
+
+ old_cur_st_mtime = oldstat.st_mtime;
+ old_cur_st_ctime = oldstat.st_ctime;
+
+ if (stat(cachepath.c_str(), &oldstat) == 0) {
+ old_bincimap_cache_st_mtime = oldstat.st_mtime;
+ old_bincimap_cache_st_ctime = oldstat.st_ctime;
+ } else {
+ old_bincimap_cache_st_mtime = 0;
+ old_bincimap_cache_st_ctime = 0;
+ }
+ } else {
+ struct stat oldcurstat;
+ struct stat oldnewstat;
+ struct stat oldbincimapcachestat;
+ if (stat(newpath.c_str(), &oldnewstat) != 0) {
+ setLastError("Invalid Mailbox, " + newpath + ": "
+ + string(strerror(errno)));
+ return PermanentError;
+ }
+
+ if (stat(curpath.c_str(), &oldcurstat) != 0) {
+ setLastError("Invalid Mailbox, " + curpath + ": "
+ + string(strerror(errno)));
+ return PermanentError;
+ }
+
+ if (stat(cachepath.c_str(), &oldbincimapcachestat) != 0) {
+ oldbincimapcachestat.st_ctime = 0;
+ oldbincimapcachestat.st_mtime = 0;
+ }
+
+ if (oldnewstat.st_mtime == old_new_st_mtime
+ && oldnewstat.st_ctime == old_new_st_ctime
+ && oldcurstat.st_mtime == old_cur_st_mtime
+ && oldcurstat.st_ctime == old_cur_st_ctime
+ && oldbincimapcachestat.st_mtime == old_bincimap_cache_st_mtime
+ && oldbincimapcachestat.st_ctime == old_bincimap_cache_st_ctime) {
+ return Success;
+ }
+
+ old_bincimap_cache_st_mtime = oldbincimapcachestat.st_mtime;
+ old_bincimap_cache_st_ctime = oldbincimapcachestat.st_ctime;
+ old_cur_st_mtime = oldcurstat.st_mtime;
+ old_cur_st_ctime = oldcurstat.st_ctime;
+ old_new_st_mtime = oldnewstat.st_mtime;
+ old_new_st_ctime = oldnewstat.st_ctime;
+ }
+
+ // lock the directory as we are scanning. this prevents race
+ // conditions with uid delegation
+ Lock lock(path);
+
+ // Read the cache file if it's there. It holds important information
+ // about the state of the depository, and serves to communicate
+ // changes to the depot across Binc IMAP instances that can not be
+ // communicated via the depot itself.
+ switch (readCache()) {
+ case NoCache:
+ case Error:
+ // An error with reading the cache files when it's not the first
+ // time we scan the depot is treated as an error.
+ if (!firstscan && !readOnly) {
+ old_cur_st_mtime = (time_t) 0;
+ old_cur_st_ctime = (time_t) 0;
+ old_new_st_mtime = (time_t) 0;
+ old_new_st_ctime = (time_t) 0;
+ return TemporaryError;
+ }
+ mailboxchanged = true;
+ break;
+ default:
+ break;
+ }
+
+ // open new/ directory
+ DIR *pdir = opendir(newpath.c_str());
+ if (pdir == 0) {
+ string reason = "failed to open \"" + newpath + "\" (";
+ reason += strerror(errno);
+ reason += ")";
+ setLastError(reason);
+
+ return PermanentError;
+ }
+
+ // scan all entries
+ struct dirent *pdirent;
+ while ((pdirent = readdir(pdir)) != 0) {
+ // "Unless you're writing messages to a maildir, the format of a
+ // unique name is none of your business. A unique name can be
+ // anything that doesn't contain a colon (or slash) and doesn't
+ // start with a dot. Do not try to extract information from unique
+ // names." - The Maildir spec from cr.yp.to
+ string filename = pdirent->d_name;
+ if (filename[0] == '.'
+ || filename.find(':') != string::npos
+ || filename.find('/') != string::npos)
+ continue;
+
+ string fullfilename = newpath + filename;
+
+ // We need to find the timestamp of the message in order to
+ // determine whether or not it's safe to move the message in from
+ // new/. qmail's default message file naming algorithm forces us
+ // to never move messages out of new/ that are less than one
+ // second old.
+ struct stat mystat;
+ if (stat(fullfilename.c_str(), &mystat) != 0) {
+ if (errno == ENOENT) {
+ // prevent looping due to stale symlinks
+ if (lstat(fullfilename.c_str(), &mystat) == 0) {
+ bincWarning << "dangling symlink: " << fullfilename << endl;
+ continue;
+ }
+
+ // a rare race between readdir and stat force us to restart the scan.
+ closedir(pdir);
+
+ if ((pdir = opendir(newpath.c_str())) == 0) {
+ string reason = "Warning: opendir(\"" + newpath + "\") == 0 (";
+ reason += strerror(errno);
+ reason += ")";
+ setLastError(reason);
+ return PermanentError;
+ }
+ } else
+ bincWarning << "junk in Maildir: \"" << fullfilename << "\": "
+ << strerror(errno);
+
+ continue;
+ }
+
+ // this is important. do not move messages from new/ that are not
+ // at least one second old or messages may disappear. this
+ // introduces a special case: we can not cache the old st_ctime
+ // and st_mtime. the next time the mailbox is scanned, it must not
+ // simply be skipped. :-)
+
+ vector<MaildirMessage>::const_iterator newIt = newMessages.begin();
+ bool ours = false;
+ for (; newIt != newMessages.end(); ++newIt) {
+ if ((filename == (*newIt).getUnique())
+ && ((*newIt).getInternalFlags() & MaildirMessage::Committed)) {
+ ours = true;
+ break;
+ }
+ }
+
+ if (!ours && ::time(0) <= mystat.st_mtime) {
+ old_cur_st_mtime = (time_t) 0;
+ old_cur_st_ctime = (time_t) 0;
+ old_new_st_mtime = (time_t) 0;
+ old_new_st_ctime = (time_t) 0;
+ continue;
+ }
+
+ // move files from new/ to cur/
+ string newName = curpath + pdirent->d_name;
+ if (rename((newpath + pdirent->d_name).c_str(),
+ (newName + ":2,").c_str()) != 0) {
+ bincWarning << "error moving messages from new to cur: skipping "
+ << newpath
+ << pdirent->d_name << ": " << strerror(errno) << endl;
+ continue;
+ }
+ }
+
+ closedir(pdir);
+
+ // Now, assume all known messages were expunged and have them prove
+ // otherwise.
+ {
+ Mailbox::iterator i = begin(SequenceSet::all(),
+ INCLUDE_EXPUNGED | SQNR_MODE);
+ for (; i != end(); ++i)
+ (*i).setExpunged();
+ }
+
+ // Then, scan cur
+ // open directory
+
+ if ((pdir = opendir(curpath.c_str())) == 0) {
+ string reason = "Maildir::scan::opendir(\"" + curpath + "\") == 0 (";
+ reason += strerror(errno);
+ reason += ")";
+
+ setLastError(reason);
+ return PermanentError;
+ }
+
+ // erase all old maps between fixed filenames and actual file names.
+ // we'll get a new list now, which will be more up to date.
+ index.clearFileNames();
+
+ // this is to sort recent messages by internaldate
+ multimap<unsigned int, MaildirMessage> tempMessageMap;
+
+ // scan all entries
+ while ((pdirent = readdir(pdir)) != 0) {
+ string filename = pdirent->d_name;
+ if (filename[0] == '.') continue;
+
+ string uniquename;
+ string standard;
+ string::size_type pos;
+ if ((pos = filename.find(':')) != string::npos) {
+ uniquename = filename.substr(0, pos);
+ string tmp = filename.substr(pos);
+ if ((pos = tmp.find("2,")) != string::npos)
+ standard = tmp.substr(pos + 2);
+ } else
+ uniquename = filename;
+
+ unsigned char mflags = Message::F_NONE;
+ for (string::const_iterator i = standard.begin();
+ i != standard.end(); ++i) {
+ switch (*i) {
+ case 'R': mflags |= Message::F_ANSWERED; break;
+ case 'S': mflags |= Message::F_SEEN; break;
+ case 'T': mflags |= Message::F_DELETED; break;
+ case 'D': mflags |= Message::F_DRAFT; break;
+ case 'F': mflags |= Message::F_FLAGGED; break;
+ case 'P': mflags |= Message::F_PASSED; break;
+ default: break;
+ }
+ }
+
+ struct stat mystat;
+ MaildirMessage *message = get(uniquename);
+ if (!message || message->getInternalDate() == 0) {
+ string fullfilename = curpath + filename;
+ if (stat(fullfilename.c_str(), &mystat) != 0) {
+ if (errno == ENOENT) {
+ // prevent looping due to stale symlinks
+ if (lstat(fullfilename.c_str(), &mystat) == 0) {
+ bincWarning << "dangling symlink: " << fullfilename << endl;
+ continue;
+ }
+ // a rare race between readdir and stat force us to restart
+ // the scan.
+ index.clearFileNames();
+
+ closedir(pdir);
+
+ if ((pdir = opendir(newpath.c_str())) == 0) {
+ string reason = "Warning: opendir(\"" + newpath + "\") == 0 (";
+ reason += strerror(errno);
+ reason += ")";
+ setLastError(reason);
+ return PermanentError;
+ }
+ }
+
+ continue;
+ }
+
+ mailboxchanged = true;
+ }
+
+ index.insert(uniquename, 0, filename);
+
+ // If we have this message in memory already..
+ if (message) {
+ if (message->getInternalDate() == 0) {
+ mailboxchanged = true;
+ message->setInternalDate(mystat.st_mtime);
+ }
+
+ // then confirm that this message was not expunged
+ message->setUnExpunged();
+
+ // update the flags with what new flags we found in the filename,
+ // but keep the \Recent flag regardless.
+ if (mflags != (message->getStdFlags() & ~Message::F_RECENT)) {
+ int oldflags = message->getStdFlags();
+ message->resetStdFlags();
+ message->setStdFlag(mflags | (oldflags & Message::F_RECENT));
+ }
+
+ continue;
+ }
+
+ // Wait with delegating UIDs until all entries have been
+ // read. Only then can we sort by internaldate and delegate new
+ // UIDs.
+ MaildirMessage m(*this);
+ m.setUID(0);
+ m.setSize(0);
+ m.setInternalDate(mystat.st_mtime);
+ m.setStdFlag((mflags | Message::F_RECENT) & ~Message::F_EXPUNGED);
+ m.setUnique(uniquename);
+ tempMessageMap.insert(make_pair((unsigned int) mystat.st_mtime, m));
+
+ mailboxchanged = true;
+ }
+
+ closedir(pdir);
+
+ // Recent messages are added, ordered by internaldate.
+ {
+ int readonlyuidnext = uidnext;
+ multimap<unsigned int, MaildirMessage>::iterator i = tempMessageMap.begin();
+ while (i != tempMessageMap.end()) {
+ i->second.setUID(readOnly ? readonlyuidnext++ : uidnext++);
+ multimap<unsigned int, MaildirMessage>::iterator itmp = i;
+ ++itmp;
+ add(i->second);
+ tempMessageMap.erase(i);
+ i = itmp;
+ mailboxchanged = true;
+ }
+ }
+
+ tempMessageMap.clear();
+
+ // Messages that existed in the cache that we read, but did not
+ // exist in the Maildir, are removed from the messages list.
+ Mailbox::iterator jj = begin(SequenceSet::all(), INCLUDE_EXPUNGED | SQNR_MODE);
+ while (jj != end()) {
+ MaildirMessage &message = (MaildirMessage &)*jj;
+
+ if (message.isExpunged()) {
+ mailboxchanged = true;
+ if (message.getInternalFlags() & MaildirMessage::JustArrived) {
+ jj.erase();
+ continue;
+ }
+ } else if (message.getInternalFlags() & MaildirMessage::JustArrived) {
+ message.clearInternalFlag(MaildirMessage::JustArrived);
+ }
+
+ ++jj;
+ }
+
+ // Special case: The first time we scan is in SELECT. All flags
+ // changes for new messages will then appear to be recent, and
+ // to avoid this to be sent to the client as a pending update,
+ // we explicitly unset the "flagsChanged" flag in all messages.
+ if (firstscan) {
+ unsigned int lastuid = 0;
+
+ Mailbox::iterator ii
+ = begin(SequenceSet::all(), INCLUDE_EXPUNGED | SQNR_MODE);
+ for (; ii != end(); ++ii) {
+ MaildirMessage &message = (MaildirMessage &)*ii;
+ message.clearInternalFlag(MaildirMessage::JustArrived);
+
+ if (lastuid < message.getUID())
+ lastuid = message.getUID();
+ else {
+ bincWarning << "UID values are not strictly ascending in this"
+ " mailbox: " << path << ". This is usually caused by "
+ << "access from a broken accessor. Bumping UIDVALIDITY."
+ << endl;
+
+ setLastError("An error occurred while scanning the mailbox. "
+ "Please contact your system administrator.");
+
+ if (!readOnly) {
+ bumpUidValidity(path);
+ old_cur_st_mtime = (time_t) 0;
+ old_cur_st_ctime = (time_t) 0;
+ old_new_st_mtime = (time_t) 0;
+ old_new_st_ctime = (time_t) 0;
+ return TemporaryError;
+ } else {
+ return PermanentError;
+ }
+ }
+
+ message.setFlagsUnchanged();
+ }
+ }
+
+ if (mailboxchanged && !readOnly) {
+ if (!writeCache()) return PermanentError;
+ mailboxchanged = false;
+ }
+
+ firstscan = false;
+ newMessages.clear();
+ return Success;
+}
diff --git a/src/maildir-scanfilesnames.cc b/src/maildir-scanfilesnames.cc
new file mode 100644
index 0000000..643460b
--- /dev/null
+++ b/src/maildir-scanfilesnames.cc
@@ -0,0 +1,53 @@
+/** --------------------------------------------------------------------
+ * @file maildir-scanfilesnames.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <iostream>
+#include <iomanip>
+
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "iodevice.h"
+#include "iofactory.h"
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+bool Binc::Maildir::scanFileNames(void) const
+{
+ string curpath = path + "/cur/";
+ DIR *pdir = opendir(curpath.c_str());
+ if (pdir == 0) {
+ setLastError("when scanning mailbox \""
+ + path + "\": " + string(strerror(errno)));
+ bincWarning << getLastError() << endl;
+ return false;
+ }
+
+ index.clearFileNames();
+
+ struct dirent *pdirent;
+ while ((pdirent = readdir(pdir)) != 0) {
+ if (!isdigit(pdirent->d_name[0])) continue;
+
+ string filename = pdirent->d_name;
+ string uniquename;
+
+ string::size_type pos;
+ if ((pos = filename.find(':')) == string::npos)
+ uniquename = filename;
+ else
+ uniquename = filename.substr(0, pos);
+
+ index.insert(uniquename, 0, pdirent->d_name);
+ }
+
+ closedir(pdir);
+ return true;
+}
diff --git a/src/maildir-select.cc b/src/maildir-select.cc
new file mode 100644
index 0000000..78daf9d
--- /dev/null
+++ b/src/maildir-select.cc
@@ -0,0 +1,45 @@
+/** --------------------------------------------------------------------
+ * @file maildir-select.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date Andreas Aardal Hanssen
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+bool Binc::Maildir::selectMailbox(const std::string &name,
+ const std::string &s_in)
+{
+ setName(name);
+
+ if (selected) {
+ closeMailbox();
+ selected = false;
+ }
+
+ oldrecent = 0;
+ oldexists = 0;
+
+ mailboxchanged = false;
+
+ setPath(s_in);
+
+ switch (scan()) {
+ case Success:
+ break;
+ case TemporaryError:
+ if (scan() == Success)
+ break;
+ case PermanentError:
+ return false;
+ }
+
+ selected = true;
+ return true;
+}
diff --git a/src/maildir-updateflags.cc b/src/maildir-updateflags.cc
new file mode 100644
index 0000000..be37234
--- /dev/null
+++ b/src/maildir-updateflags.cc
@@ -0,0 +1,115 @@
+/** --------------------------------------------------------------------
+ * @file maildir-updateflags.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "maildir.h"
+
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "iodevice.h"
+#include "iofactory.h"
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::Maildir::updateFlags(void)
+{
+ // don't update a read-only mailbox.
+ if (readOnly) return;
+
+ // open the cur/ directory
+ string curpath = path + "/cur/";
+ DIR *pdir = opendir(curpath.c_str());
+ if (pdir == 0) {
+ bincError << "failed to open " << curpath << ": "
+ << strerror(errno) << endl;
+ return;
+ }
+
+ // read all entries in the directory
+ vector<string> entries;
+ struct dirent *pdirent;
+ while ((pdirent = readdir(pdir)) != 0) {
+ string filename = pdirent->d_name;
+ if (filename[0] == '.')
+ continue;
+ entries.push_back(filename);
+ }
+ closedir(pdir);
+
+ bool customFlagsChanged = false;
+
+ // loop through all messages in cur/, and update the flags that have
+ // changed.
+ for (vector<string>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
+ string filename = *it;
+
+ // separate the unique name from the flags. accept messages that do not
+ // contain the flags section.
+ string uniquename;
+ string::size_type pos;
+ if ((pos = filename.find(":2,")) != string::npos)
+ uniquename = filename.substr(0, pos);
+ else
+ uniquename = filename;
+
+ // only update flags for messages that are known.
+ MaildirMessage *message = get(uniquename);
+ if (!message)
+ continue;
+
+ // check if custom flags have changed; if so, we need to regenerate the
+ // cache file.
+ if (message->internalFlags & MaildirMessage::CustomFlagsChanged)
+ customFlagsChanged = true;
+
+ // generate the flag string.
+ string flags;
+ int mflags = message->getStdFlags();
+ if (mflags & Message::F_DRAFT) flags += "D";
+ if (mflags & Message::F_FLAGGED) flags += "F";
+ if (mflags & Message::F_PASSED) flags += "P";
+ if (mflags & Message::F_ANSWERED) flags += "R";
+ if (mflags & Message::F_SEEN) flags += "S";
+ if (mflags & Message::F_DELETED) flags += "T";
+
+ // prepare the old and new message name. if equal, skip to the next
+ // message.
+ string srcname = curpath + filename;
+ string destname = curpath + uniquename + ":2," + flags;
+ if (srcname == destname)
+ continue;
+
+ // rename the file
+ if (rename(srcname.c_str(), destname.c_str()) != 0) {
+ if (errno == ENOENT) {
+ closedir(pdir);
+ if ((pdir = opendir(curpath.c_str())) == 0) {
+ bincError << "failed to open " << curpath << ": "
+ << strerror(errno) << endl;
+ return;
+ }
+
+ // restart scan. this catches the race condition where concurrent
+ // clients may have set a flag or removed the message.
+ continue;
+ }
+
+ bincError << "failed to rename " << srcname
+ << " to " << destname << ": "
+ << strerror(errno) << endl;
+ } else {
+ index.insert(uniquename, 0, uniquename + ":2," + flags);
+ }
+ }
+
+ // finally, regenerate the cache file if the custom flags have changed.
+ if (customFlagsChanged) {
+ Lock lock(path);
+ writeCache();
+ }
+}
diff --git a/src/maildir-writecache.cc b/src/maildir-writecache.cc
new file mode 100644
index 0000000..feda4bd
--- /dev/null
+++ b/src/maildir-writecache.cc
@@ -0,0 +1,74 @@
+/** --------------------------------------------------------------------
+ * @file maildir-writecache.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "globals.h"
+#include "maildir.h"
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+bool Binc::Maildir::writeCache(void)
+{
+ if (readOnly)
+ return true;
+
+ char *safename = strdup((path + "/.bincimap-cache-tmp-XXXXXX").c_str());
+ int fd = mkstemp(safename);
+ if (!fd) {
+ free(safename);
+ return false;
+ }
+
+ string safeName = safename;
+ free(safename);
+
+ FILE *fp = fdopen(fd, "w");
+ if (!fp) {
+ unlink(safeName.c_str());
+ return false;
+ }
+
+ if (uidvalidity == 0 || uidnext == 0) {
+ uidvalidity = time(0);
+ uidnext = messages.size() + 1;
+ }
+
+ fprintf(fp, "%s %u %u\n", BINC_CACHE, uidvalidity, uidnext);
+ Mailbox::iterator i = begin(SequenceSet::all(), INCLUDE_EXPUNGED);
+ for (; i != end(); ++i) {
+ MaildirMessage &message = (MaildirMessage &)*i;
+ fprintf(fp, "%u %u %u %s", message.getUID(),
+ (unsigned int) message.getInternalDate(), message.getSize(),
+ message.getUnique().c_str());
+ vector<string> cflags = message.getCustomFlags();
+ for (vector<string>::const_iterator it = cflags.begin();
+ it != cflags.end(); ++it) {
+ fprintf(fp, " %s", (*it).c_str());
+ }
+ fprintf(fp, "\n");
+ }
+
+ if (fflush(fp) || (fsync(fd) && (errno != EROFS || errno != EINVAL)) || fclose(fp)) {
+ unlink(safeName.c_str());
+ return false;
+ }
+
+ if (rename(safeName.c_str(), (path + "/bincimap-cache").c_str()) != 0) {
+ unlink(safeName.c_str());
+ return false;
+ }
+
+ int dfd = open(path.c_str(), O_RDONLY);
+ if (dfd == -1 || (fsync(fd) && (errno != EROFS || errno != EINVAL)) || close(dfd)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/maildir.cc b/src/maildir.cc
new file mode 100644
index 0000000..11f472c
--- /dev/null
+++ b/src/maildir.cc
@@ -0,0 +1,839 @@
+/** --------------------------------------------------------------------
+ * @file maildir.cc
+ * @brief Implementation of the Maildir class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <iostream>
+#include <iomanip>
+#include <algorithm>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "maildir.h"
+#include "maildirmessage.h"
+#include "pendingupdates.h"
+#include "session.h"
+#include "status.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+// Used to generate the unique names for Maildir delivery
+static int numDeliveries = 0;
+
+//------------------------------------------------------------------------
+Maildir::iterator::iterator(void)
+{
+}
+
+//------------------------------------------------------------------------
+Maildir::iterator::iterator(Maildir *home,
+ MessageMap::iterator it,
+ const SequenceSet &_bset,
+ unsigned int _mod)
+ : BaseIterator(1), mailbox(home), bset(_bset), mod(_mod), i(it)
+{
+ uidmax = home->getMaxUid();
+ sqnrmax = home->getMaxSqnr();
+}
+
+//------------------------------------------------------------------------
+Maildir::iterator::iterator(const iterator &copy)
+ : BaseIterator(copy.sqnr), mailbox(copy.mailbox),
+ bset(copy.bset), mod(copy.mod), i(copy.i), uidmax(copy.uidmax),
+ sqnrmax(copy.sqnrmax)
+{
+}
+
+//------------------------------------------------------------------------
+Maildir::iterator &Maildir::iterator::operator =(const iterator &copy)
+{
+ sqnr = copy.sqnr;
+ mailbox = copy.mailbox;
+ bset = copy.bset;
+ mod = copy.mod;
+ i = copy.i;
+ uidmax = copy.uidmax;
+ sqnrmax = copy.sqnrmax;
+ return *this;
+}
+
+//------------------------------------------------------------------------
+Maildir::iterator::~iterator(void)
+{
+}
+
+//------------------------------------------------------------------------
+MaildirMessage &Maildir::iterator::curMessage(void)
+{
+ return i->second;
+}
+
+//------------------------------------------------------------------------
+Message &Maildir::iterator::operator *(void)
+{
+ return curMessage();
+}
+
+//------------------------------------------------------------------------
+void Maildir::iterator::operator ++(void)
+{
+ ++i;
+ ++sqnr;
+ reposition();
+}
+
+//------------------------------------------------------------------------
+bool Maildir::iterator::operator ==(const BaseIterator &a) const
+{
+ const iterator *b = dynamic_cast<const iterator *>(&a);
+ return b ? (i == b->i) : false;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::iterator::operator !=(const BaseIterator &a) const
+{
+ return !((*this) == a);
+}
+
+//------------------------------------------------------------------------
+void Maildir::iterator::reposition(void)
+{
+ for (;;) {
+ if (i == mailbox->messages.end())
+ break;
+
+ Message &message = curMessage();
+ if ((mod & SKIP_EXPUNGED) && message.isExpunged()) {
+ ++i;
+ continue;
+ }
+
+ bool inset = false;
+ if (mod & SQNR_MODE) {
+ if (bset.isInSet(sqnr) || (!bset.isLimited() && sqnr == sqnrmax))
+ inset = true;
+ } else {
+ if (bset.isInSet(message.getUID()) || (!bset.isLimited() && message.getUID() == uidmax))
+ inset = true;
+ }
+
+ if (!inset) {
+ ++i;
+ if (!message.isExpunged())
+ ++sqnr;
+ continue;
+ }
+
+ break;
+ }
+}
+
+//------------------------------------------------------------------------
+Mailbox::iterator Maildir::begin(const SequenceSet &bset,
+ unsigned int mod) const
+{
+ beginIterator = iterator((Maildir *)this, messages.begin(), bset, mod);
+ beginIterator.reposition();
+
+ return Mailbox::iterator(beginIterator);
+}
+
+//------------------------------------------------------------------------
+Mailbox::iterator Maildir::end(void) const
+{
+ endIterator = iterator((Maildir *)this, messages.end(),
+ endIterator.bset, endIterator.mod);
+ return Mailbox::iterator(endIterator);
+}
+
+//------------------------------------------------------------------------
+void Maildir::iterator::erase(void)
+{
+ MessageMap::iterator iter = i;
+ ++iter;
+
+ MaildirMessageCache::getInstance().removeStatus(&curMessage());
+ mailbox->index.remove(i->second.getUnique());
+ mailbox->messages.erase(i);
+
+ i = iter;
+ reposition();
+}
+
+//------------------------------------------------------------------------
+Maildir::Maildir(void) : Mailbox()
+{
+ firstscan = true;
+ cacheRead = false;
+ uidvalidity = 0;
+ uidnext = 1;
+ selected = false;
+ oldrecent = 0;
+ oldexists = 0;
+}
+
+//------------------------------------------------------------------------
+Maildir::~Maildir(void)
+{
+}
+
+//------------------------------------------------------------------------
+void Maildir::setPath(const string &path_in)
+{
+ path = path_in;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::getUpdates(bool doscan, unsigned int type,
+ PendingUpdates &updates, bool forceScan)
+{
+ if (doscan && scan(forceScan) != Success)
+ return false;
+
+ unsigned int exists = 0;
+ unsigned int recent = 0;
+ bool displayExists = false;
+
+ // count messages, find recent
+ if (!readOnly && (type & PendingUpdates::EXPUNGE)) {
+ Mailbox::iterator i = begin(SequenceSet::all(),
+ INCLUDE_EXPUNGED | SQNR_MODE);
+
+ while (i != end()) {
+ Message &message = *i;
+
+ if (message.isExpunged()) {
+ updates.addExpunged(i.getSqnr());
+ i.erase();
+ mailboxchanged = true;
+ displayExists = true;
+ } else
+ ++i;
+ }
+ }
+
+ Mailbox::iterator i = begin(SequenceSet::all(),
+ INCLUDE_EXPUNGED | SQNR_MODE);
+ for (; i != end(); ++i) {
+ Message & message = *i;
+ // at this point, there is a message that is not expunged
+ ++exists;
+ if (message.getStdFlags() & Message::F_RECENT) ++recent;
+ }
+
+ if (displayExists || exists != oldexists)
+ updates.setExists(oldexists = exists);
+
+ if (recent != oldrecent)
+ updates.setRecent(oldrecent = recent);
+
+ if (type & PendingUpdates::FLAGS) {
+ Mailbox::iterator i = begin(SequenceSet::all(), SQNR_MODE);
+ for (; i != end(); ++i) {
+ Message &message = *i;
+ if (message.hasFlagsChanged()) {
+ int flags = message.getStdFlags();
+
+ updates.addFlagUpdates(i.getSqnr(), message.getUID(), flags,
+ message.getCustomFlags());
+
+ message.setFlagsUnchanged();
+ }
+ }
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::isMailbox(const std::string &s_in) const
+{
+ struct stat mystat;
+
+ return ((stat((s_in + "/cur").c_str(), &mystat) == 0
+ && S_ISDIR(mystat.st_mode))
+ && (stat((s_in + "/new").c_str(), &mystat) == 0
+ && S_ISDIR(mystat.st_mode))
+ && (stat((s_in + "/tmp").c_str(), &mystat) == 0
+ && S_ISDIR(mystat.st_mode)));
+}
+
+//------------------------------------------------------------------------
+const std::string Maildir::getTypeName(void) const
+{
+ return "Maildir";
+}
+
+//------------------------------------------------------------------------
+void Maildir::bumpUidValidity(const string &s_in) const
+{
+ unlink((s_in + "/bincimap-uidvalidity").c_str());
+ unlink((s_in + "/bincimap-cache").c_str());
+}
+
+//------------------------------------------------------------------------
+bool Maildir::isMarked(const std::string &s_in) const
+{
+ DIR *dirp = opendir((s_in + "/new").c_str());
+ if (dirp == 0) return false;
+
+ struct dirent *direntp;
+ while ((direntp = readdir(dirp)) != 0) {
+ string s = direntp->d_name;
+ if (s[0] != '.'
+ && s.find('/') == string::npos
+ && s.find(':') == string::npos) {
+ closedir(dirp);
+ return true;
+ }
+ }
+
+ closedir(dirp);
+ return false;
+}
+
+//------------------------------------------------------------------------
+unsigned int Maildir::getStatusID(const string &path) const
+{
+ unsigned int statusid = 0;
+ struct stat mystat;
+ if (stat((path + "/new/").c_str(), &mystat) == 0)
+ statusid = mystat.st_ctime;
+
+ if (stat((path + "/cur/").c_str(), &mystat) == 0)
+ statusid += mystat.st_ctime;
+
+ if (stat((path + "/bincimap-cache").c_str(), &mystat) == 0)
+ statusid += mystat.st_ctime;
+
+ return statusid;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::getStatus(const string &path, Status &s) const
+{
+ unsigned int messages = 0;
+ unsigned int unseen = 0;
+ unsigned int recent = 0;
+
+ unsigned int readUidValidity;
+ unsigned int readUidNext = 1;
+ map<string, bool> mincache;
+
+ const string cachefilename = path + "/bincimap-cache";
+ FILE *fp = fopen(cachefilename.c_str(), "r");
+ if (fp) do {
+ char inputBuffer[512];
+ if (!fgets(inputBuffer, sizeof(inputBuffer), fp)) {
+ fclose(fp);
+ return false;
+ }
+
+ // terminate the buffer
+ inputBuffer[sizeof(inputBuffer) - 1] = '\0';
+
+ char cacheFileVersionBuffer[512];
+
+ if (sscanf(inputBuffer, "%s %u %u", cacheFileVersionBuffer,
+ &readUidValidity, &readUidNext) != 3
+ || strcmp(cacheFileVersionBuffer, BINC_CACHE) != 0) {
+ fclose(fp);
+ readUidValidity = 0;
+ readUidNext = 1;
+ mincache.clear();
+ break;
+ }
+
+ unsigned int readUID;
+ unsigned int readSize;
+ unsigned int readInternalDate;
+ char readUnique[512];
+ while (fgets(inputBuffer, sizeof(inputBuffer), fp)) {
+ inputBuffer[sizeof(inputBuffer) - 1] = '\0';
+ if (sscanf(inputBuffer, "%u %u %u %s", &readUID,
+ &readInternalDate, &readSize, readUnique) != 4) {
+ fclose(fp);
+ readUidValidity = 0;
+ readUidNext = 1;
+ mincache.clear();
+ break;
+ }
+
+ mincache[readUnique] = true;
+ }
+
+ fclose(fp);
+
+ s.setUidValidity(readUidValidity < 1 ? time(0) : readUidValidity);
+ } while (0); else {
+ s.setUidValidity(time(0));
+ }
+
+ // Scan new
+ DIR *dirp = opendir((path + "/new").c_str());
+ if (dirp == 0) return false;
+
+ struct dirent *direntp;
+ while ((direntp = readdir(dirp)) != 0) {
+ const string filename = direntp->d_name;
+ if (filename[0] == '.'
+ || filename.find(':') != string::npos
+ || filename.find('/') != string::npos)
+ continue;
+
+ ++recent;
+ ++readUidNext;
+ ++unseen;
+ ++messages;
+ }
+
+ closedir(dirp);
+
+ // Scan cur
+ if ((dirp = opendir((path + "/cur").c_str())) == 0)
+ return false;
+
+ while ((direntp = readdir(dirp)) != 0) {
+ const string dname = direntp->d_name;
+ if (dname[0] == '.')
+ continue;
+
+ ++messages;
+
+ // Add to unseen if it doesn't have the seen flag or if it has no
+ // flags.
+ const string::size_type pos = dname.find(':');
+ if (pos != string::npos) {
+ if (mincache.find(dname.substr(0, pos)) == mincache.end()) {
+ ++recent;
+ ++readUidNext;
+ }
+
+ if (dname.substr(pos).find('S') == string::npos)
+ ++unseen;
+ } else {
+ if (mincache.find(dname) == mincache.end()) {
+ ++recent;
+ ++readUidNext;
+ }
+
+ ++unseen;
+ }
+ }
+
+ closedir(dirp);
+
+ s.setRecent(recent);
+ s.setMessages(messages);
+ s.setUnseen(unseen);
+ s.setUidNext(uidnext);
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+unsigned int Maildir::getMaxUid(void) const
+{
+ MessageMap::const_iterator i = messages.end();
+ if (i == messages.begin())
+ return 0;
+
+ --i;
+ for (;;) {
+ const MaildirMessage &message = i->second;
+ if (message.isExpunged()) {
+ if (i == messages.begin())
+ return 0;
+ --i;
+ } else {
+ return message.getUID();
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+unsigned int Maildir::getMaxSqnr(void) const
+{
+ int sqnr = messages.size();
+ MessageMap::const_iterator i = messages.end();
+ if (i == messages.begin())
+ return 0;
+
+ --i;
+ for (;;) {
+ const MaildirMessage &message = i->second;
+ if (message.isExpunged()) {
+ if (i == messages.begin())
+ return 0;
+ --sqnr;
+ --i;
+ } else {
+ return sqnr;
+ }
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+unsigned int Maildir::getUidValidity(void) const
+{
+ return uidvalidity;
+}
+
+//------------------------------------------------------------------------
+unsigned int Maildir::getUidNext(void) const
+{
+ return uidnext;
+}
+
+//------------------------------------------------------------------------
+Message *Maildir::createMessage(const string &mbox, time_t idate)
+{
+ string sname = mbox + "/tmp/bincimap-XXXXXX";
+ char *safename = strdup(sname.c_str());
+
+ int fd = mkstemp(safename);
+ if (fd == -1) {
+ setLastError("Unable to create safe name.");
+ return 0;
+ }
+
+ string safenameStr = safename;
+ delete[] safename;
+
+ MaildirMessage message(*this);
+
+ message.setFile(fd);
+ message.setSafeName(safenameStr);
+ message.setInternalDate(idate);
+
+ newMessages.push_back(message);
+ vector<MaildirMessage>::iterator i = newMessages.end();
+ --i;
+ return &(*i);
+}
+
+//------------------------------------------------------------------------
+bool Maildir::commitNewMessages(const string &mbox)
+{
+ Session &session = Session::getInstance();
+
+ vector<MaildirMessage>::iterator i = newMessages.begin();
+ map<MaildirMessage *, string> committedMessages;
+
+ struct timeval youngestFile = {0, 0};
+
+ bool abort = false;
+ while (!abort && i != newMessages.end()) {
+ MaildirMessage &m = *i;
+
+ if (m.getInternalFlags() & MaildirMessage::Committed) {
+ ++i;
+ continue;
+ }
+
+ m.setInternalFlag(MaildirMessage::Committed);
+
+ string safeName = m.getSafeName();
+
+ for (int attempt = 0; !abort && attempt < 1000; ++attempt) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ youngestFile = tv;
+
+ // Generate Maildir conformant file name
+ BincStream ssid;
+ ssid << (int) tv.tv_sec << "."
+ << "R" << (int) rand()
+ << "M" << (int) tv.tv_usec
+ << "P" << (int) session.getPid()
+ << "Q" << numDeliveries++
+ << "." << session.getEnv("TCPLOCALHOST");
+
+ BincStream ss;
+ ss << mbox << "/new/" << ssid.str();
+
+ string fileName = ss.str();
+
+ if (link(safeName.c_str(), fileName.c_str()) == 0) {
+ unlink(safeName.c_str());
+ m.setInternalDate(tv.tv_sec);
+ m.setUnique(ssid.str());
+ m.setUID(0);
+ committedMessages[&m] = fileName;
+ break;
+ }
+
+ if (errno == EEXIST)
+ continue;
+
+ bincWarning << "link(" << toImapString(safeName) << ", "
+ << toImapString(fileName) << ") failed: "
+ << strerror(errno) << endl;
+
+ session.setResponseCode("TRYCREATE");
+ session.setLastError("failed, internal error.");
+ abort = true;
+ break;
+ }
+
+ ++i;
+ }
+
+ // abort means to make an attempt to restore the mailbox to
+ // its original state.
+ if (abort) {
+ // Fixme: Messages that are in committedMessages should be skipped
+ // here.
+ for (i = newMessages.begin(); i != newMessages.end(); ++i)
+ unlink((*i).getSafeName().c_str());
+
+ map<MaildirMessage *, string>::const_iterator j
+ = committedMessages.begin();
+ while (j != committedMessages.end()) {
+ if (unlink(j->second.c_str()) != 0) {
+ if (errno == ENOENT) {
+ // FIXME: The message was probably moves away from new/ by
+ // another IMAP session.
+ bincWarning << "error rollbacking after failed commit to "
+ << toImapString(mbox) << ", failed to unlink "
+ << toImapString(j->second)
+ << ": " << strerror(errno) << endl;
+ } else {
+ bincWarning << "error rollbacking after failed commit to "
+ << toImapString(mbox) << ", failed to unlink "
+ << toImapString(j->second)
+ << ": " << strerror(errno) << endl;
+ newMessages.clear();
+ return false;
+ }
+ }
+
+ ++j;
+ }
+
+ newMessages.clear();
+ return false;
+ }
+
+ // cover the extremely unlikely event that another concurrent
+ // Maildir accessor has just made a file with the same timestamp and
+ // random number by spinning until the timestamp has changed before
+ // moving the message into cur.
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ while (tv.tv_sec == youngestFile.tv_sec
+ && tv.tv_usec == youngestFile.tv_usec) {
+ gettimeofday(&tv, 0);
+ }
+
+ map<MaildirMessage *, string>::const_iterator j
+ = committedMessages.begin();
+ for (;j != committedMessages.end(); ++j) {
+ string basename = j->second.substr(j->second.rfind('/') + 1);
+
+ int flags = j->first->getStdFlags();
+ string flagStr;
+ if (flags & Message::F_DRAFT) flagStr += "D";
+ if (flags & Message::F_FLAGGED) flagStr += "F";
+ if (flags & Message::F_ANSWERED) flagStr += "R";
+ if (flags & Message::F_SEEN) flagStr += "S";
+ if (flags & Message::F_DELETED) flagStr += "T";
+
+ string dest = mbox + "/cur/" + basename + ":2," + flagStr;
+ if (rename(j->second.c_str(), dest.c_str()) == 0)
+ continue;
+
+ if (errno != ENOENT)
+ bincWarning << "when setting flags on: " << j->second
+ << ": " << strerror(errno) << endl;
+ }
+
+ committedMessages.clear();
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::rollBackNewMessages(void)
+{
+ vector<MaildirMessage>::const_iterator i = newMessages.begin();
+ // Fixme: Messages that are in committedMessages should be skipped
+ // here.
+ for (; i != newMessages.end(); ++i)
+ unlink((*i).getSafeName().c_str());
+
+ newMessages.clear();
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool Maildir::fastCopy(Message &m, Mailbox &desttype,
+ const std::string &destname)
+{
+ // At this point, fastCopy is broken because the creation time is
+ // equal for the two clones. The new clone must have a new creation
+ // time. Symlinks are a possibility, but they break if other Maildir
+ // accessors rename mailboxes.
+ // return false;
+
+ Session &session = Session::getInstance();
+
+ MaildirMessage *message = dynamic_cast<MaildirMessage *>(&m);
+ if (!message)
+ return false;
+
+ string mfilename = message->getFileName();
+ if (mfilename == "")
+ return false;
+
+ Maildir *mailbox = dynamic_cast<Maildir *>(&desttype);
+ if (!mailbox)
+ return false;
+
+ for (int attempt = 0; attempt < 1000; ++attempt) {
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ // Generate Maildir conformant file name
+ BincStream ssid;
+ ssid << (int) tv.tv_sec << "."
+ << "R" << (int) rand()
+ << "M" << (int) tv.tv_usec
+ << "P" << (int) session.getPid()
+ << "Q" << numDeliveries++
+ << "." << session.getEnv("TCPLOCALHOST");
+
+ BincStream ss;
+ ss << destname << "/tmp/" << ssid.str();
+
+ string fileName = ss.str();
+
+ if (link((path + "/cur/" + mfilename).c_str(), fileName.c_str()) == 0) {
+ MaildirMessage newmess = *message;
+ newmess.setSafeName(fileName);
+ newmess.setUnique(ssid.str());
+ newmess.setInternalDate((time_t) tv.tv_sec);
+ newmess.setUID(0);
+ newMessages.push_back(newmess);
+ break;
+ }
+
+ if (errno == EEXIST)
+ continue;
+
+ bincWarning << "Warning: link("
+ << toImapString(path + "/cur/" + mfilename)
+ << ", " << toImapString(fileName) << ") failed: "
+ << strerror(errno) << endl;
+
+ session.setResponseCode("TRYCREATE");
+ session.setLastError("failed, internal error.");
+ return false;
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+MaildirMessage *Maildir::get(const std::string &id)
+{
+ MaildirIndexItem *item = index.find(id);
+ if (!item)
+ return 0;
+
+ unsigned int uid = item->uid;
+ if (messages.find(uid) == messages.end())
+ return 0;
+
+ return &messages.find(uid)->second;
+}
+
+//------------------------------------------------------------------------
+void Maildir::add(MaildirMessage &m)
+{
+ MessageMap::iterator it = messages.find(m.getUID());
+ if (it != messages.end())
+ messages.erase(it);
+ messages.insert(make_pair(m.getUID(), m));
+ index.insert(m.getUnique(), m.getUID());
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirIndex::getSize(void) const
+{
+ return idx.size();
+}
+
+//------------------------------------------------------------------------
+void MaildirIndex::insert(const string &unique, unsigned int uid,
+ const string &fileName)
+{
+ if (idx.find(unique) == idx.end()) {
+ MaildirIndexItem item;
+ item.uid = uid;
+ item.fileName = fileName;
+ idx[unique] = item;
+ } else {
+ MaildirIndexItem &item = idx[unique];
+ if (uid != 0) item.uid = uid;
+ if (fileName != "") item.fileName = fileName;
+ }
+}
+
+//------------------------------------------------------------------------
+void MaildirIndex::remove(const string &unique)
+{
+ map<string, MaildirIndexItem>::iterator it = idx.find(unique);
+ if (it != idx.end())
+ idx.erase(it);
+}
+
+//------------------------------------------------------------------------
+MaildirIndexItem *MaildirIndex::find(const string &unique)
+{
+ map<string, MaildirIndexItem>::iterator it = idx.find(unique);
+ if (it != idx.end())
+ return &it->second;
+
+ return 0;
+}
+
+//------------------------------------------------------------------------
+void MaildirIndex::clear(void)
+{
+ idx.clear();
+}
+
+//------------------------------------------------------------------------
+void MaildirIndex::clearUids(void)
+{
+ map<string, MaildirIndexItem>::iterator it = idx.begin();
+ for (; it != idx.end(); ++it)
+ it->second.uid = 0;
+}
+
+//------------------------------------------------------------------------
+void MaildirIndex::clearFileNames(void)
+{
+ map<string, MaildirIndexItem>::iterator it = idx.begin();
+ for (; it != idx.end(); ++it)
+ it->second.fileName = "";
+}
diff --git a/src/maildirmessage.cc b/src/maildirmessage.cc
new file mode 100644
index 0000000..a78a5b9
--- /dev/null
+++ b/src/maildirmessage.cc
@@ -0,0 +1,1153 @@
+/** --------------------------------------------------------------------
+ * @file maildirmessage.cc
+ * @brief Implementation of the MaildirMessage class.
+ * @author Andreas Aardal Hanssen
+ * @date Copyright 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include <stack>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <utime.h>
+
+#include "maildir.h"
+#include "maildirmessage.h"
+#include "convert.h"
+#include "mime.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+
+using namespace ::std;
+using namespace Binc;
+
+string Message::lastError;
+string MaildirMessage::storage;
+
+namespace {
+ //----------------------------------------------------------------------
+ void printOneHeader(IODevice &io, const MimePart *message,
+ const string &s_in, bool removecomments = true)
+ {
+ string tmp = "";
+ HeaderItem hitem;
+
+ if (message->h.getFirstHeader(s_in, hitem)) {
+ tmp = hitem.getValue();
+ io << toImapString(unfold(tmp, removecomments));
+ } else
+ io << "NIL";
+ }
+
+ //----------------------------------------------------------------------
+ void printOneAddressList(IODevice &io, const MimePart *message,
+ const string &s_in, bool removecomments = true)
+ {
+ string tmp = "";
+ HeaderItem hitem;
+
+ if (message->h.getFirstHeader(s_in, hitem)) {
+ tmp = hitem.getValue();
+ vector<string> addr;
+ splitAddr(unfold(tmp, removecomments), addr);
+ if (addr.size() != 0) {
+ io << "(";
+ for (vector<string>::const_iterator i = addr.begin();
+ i != addr.end(); ++i)
+
+ io << Address(*i).toParenList();
+ io << ")";
+ } else io << "NIL";
+ } else
+ io << "NIL";
+ }
+
+ //----------------------------------------------------------------------
+ void envelope(IODevice &io, const MimePart *message)
+ {
+ HeaderItem hitem;
+ io << "(";
+ printOneHeader(io, message, "date");
+ io << " ";
+ printOneHeader(io, message, "subject", false);
+ io << " ";
+ printOneAddressList(io, message, "from", false);
+ io << " ";
+ printOneAddressList(io, message,
+ message->h.getFirstHeader("sender", hitem)
+ ? "sender" : "from", false);
+ io << " ";
+ printOneAddressList(io, message,
+ message->h.getFirstHeader("reply-to", hitem)
+ ? "reply-to" : "from", false);
+ io << " ";
+ printOneAddressList(io, message, "to", false);
+ io << " ";
+ printOneAddressList(io, message, "cc", false);
+ io << " ";
+ printOneAddressList(io, message, "bcc", false);
+ io << " ";
+ printOneHeader(io, message, "in-reply-to");
+ io << " ";
+ printOneHeader(io, message, "message-id");
+ io << ")";
+ }
+
+ //----------------------------------------------------------------------
+ void bodyStructure(IODevice &io, const MimePart *message, bool extended)
+ {
+ HeaderItem hitem;
+ if (message->isMultipart() && message->members.size() > 0) {
+ io << "(";
+
+ for (vector<MimePart>::const_iterator i = message->members.begin();
+ i != message->members.end(); ++i)
+ bodyStructure(io, &(*i), extended);
+
+ io << " ";
+ io << toImapString(message->getSubType());
+
+ if (extended) {
+ io << " ";
+
+ vector<string> parameters;
+ vector<string> headers;
+ string tmp;
+
+ string type, subtype;
+
+ tmp = "";
+ if (message->h.getFirstHeader("content-type", hitem)) {
+ tmp = unfold(hitem.getValue());
+ trim(tmp);
+
+ vector<string> v;
+ split(tmp, ";", v);
+
+ for (vector<string>::const_iterator i = v.begin(); i != v.end(); ++i) {
+ string element = *i;
+ trim(element);
+ if (element.find('=') != string::npos) {
+ string::size_type pos = element.find('=');
+ string s = element.substr(0, pos);
+ string t = element.substr(pos + 1);
+ trim(s, " \"");
+ trim(t, " \"");
+ parameters.push_back(s);
+ parameters.push_back(t);
+ }
+ }
+
+ if (parameters.size() != 0) {
+ io << "(";
+ for (vector<string>::const_iterator i = parameters.begin();
+ i != parameters.end(); ++i) {
+ if (i != parameters.begin()) io << " ";
+ io << toImapString(*i);
+ }
+ io << ")";
+ } else
+ io << "NIL";
+ } else
+ io << "NIL";
+
+ // CONTENT-DISPOSITION
+ io << " ";
+ tmp = "";
+ if (message->h.getFirstHeader("content-disposition", hitem)) {
+ tmp = hitem.getValue();
+ trim(tmp);
+
+ vector<string> v;
+ split(tmp, ";", v);
+ if (v.size() > 0) {
+ string disp = v[0];
+ trim(disp);
+ io << "(" << toImapString(disp);
+ io << " ";
+ if (v.size() > 1) {
+ io << "(";
+ vector<string>::const_iterator i = v.begin();
+ ++i;
+ bool wrote = false;
+ while (i != v.end()) {
+ string s = *i;
+ trim(s);
+ string::size_type pos = s.find('=');
+ string key = s.substr(0, pos);
+ string value = s.substr(pos + 1);
+
+ trim(key);
+ trim(value);
+
+ trim(key, " \"");
+ trim(value, " \"");
+
+ if (!wrote) wrote = true;
+ else io << " ";
+
+ io << toImapString(key);
+ io << " ";
+ io << toImapString(value);
+
+ ++i;
+ }
+ io << ")";
+ } else
+ io << "NIL";
+ io << ")";
+ } else
+ io << "NIL";
+ } else
+ io << "NIL";
+
+ // CONTENT-LANGUAGE
+ io << " ";
+ printOneHeader(io, message, "content-language");
+ }
+ io << ")";
+ } else {
+ io << "(";
+
+ vector<string> parameters;
+ vector<string> headers;
+ string tmp;
+ tmp = "";
+ string type, subtype;
+
+ tmp = "";
+ if (message->h.getFirstHeader("content-type", hitem)) {
+ tmp = unfold(hitem.getValue());
+
+ vector<string> v;
+ split(tmp, ";", v);
+
+ if (v.size() > 0) {
+ vector<string> b;
+ split(v[0], "/", b);
+
+ if (b.size() > 0)
+ type = b[0];
+ else
+ type = "text";
+
+ if (b.size() > 1)
+ subtype = b[1];
+ else
+ subtype = "plain";
+ }
+
+ for (vector<string>::const_iterator i = v.begin(); i != v.end(); ++i) {
+ if (i == v.begin()) continue;
+ string element = *i;
+ trim(element);
+ if (element.find('=') != string::npos) {
+ string::size_type pos = element.find('=');
+ string s = element.substr(0, pos);
+ string t = element.substr(pos + 1);
+ trim(s, " \"");
+ trim(t, " \"");
+ parameters.push_back(s);
+ parameters.push_back(t);
+ }
+ }
+
+ } else {
+ type = "text";
+ subtype = "plain";
+ }
+
+ io << toImapString(type);
+ io << " ";
+ io << toImapString(subtype);
+
+ io << " ";
+ if (parameters.size() != 0) {
+ io << "(";
+ for (vector<string>::const_iterator i = parameters.begin();
+ i != parameters.end(); ++i) {
+ if (i != parameters.begin())
+ io << " ";
+ io << toImapString(*i);
+ }
+ io << ")";
+ } else
+ io << "NIL";
+
+ // CONTENT-ID
+ io << " ";
+ printOneHeader(io, message, "content-id");
+
+ // CONTENT-DESCRIPTION
+ io << " ";
+ printOneHeader(io, message, "content-description");
+
+ // CONTENT-TRANSFER-ENCODING
+ io << " ";
+ tmp = "";
+ if (message->h.getFirstHeader("content-transfer-encoding", hitem)) {
+ tmp = hitem.getValue();
+ trim(tmp);
+ io << toImapString(tmp);
+ } else
+ io << "\"7bit\"";
+ io << " ";
+
+ // Size of body in octets
+ io << message->getBodyLength();
+
+ lowercase(type);
+ if (type == "text") {
+ io << " ";
+ io << message->getNofBodyLines();
+ } else if (message->isMessageRFC822()) {
+ io << " ";
+ envelope(io, &message->members[0]);
+ io << " ";
+ bodyStructure(io, &message->members[0], extended);
+ io << " ";
+ io << message->getNofBodyLines();
+ }
+
+ // Extension data follows
+
+ if (extended) {
+ // CONTENT-MD5
+ io << " ";
+ printOneHeader(io, message, "content-md5");
+
+ // CONTENT-DISPOSITION
+ io << " ";
+ tmp = "";
+ if (message->h.getFirstHeader("content-disposition", hitem)) {
+ tmp = hitem.getValue();
+ trim(tmp);
+ vector<string> v;
+ split(tmp, ";", v);
+ if (v.size() > 0) {
+ string disp = v[0];
+ trim(disp);
+ io << "(" << toImapString(disp);
+ io << " ";
+ if (v.size() > 1) {
+ io << "(";
+ vector<string>::const_iterator i = v.begin();
+ ++i;
+ bool wrote = false;
+ while (i != v.end()) {
+ string s = *i;
+ trim(s);
+ string::size_type pos = s.find('=');
+ string key = s.substr(0, pos);
+ string value = s.substr(pos + 1);
+ trim(key);
+ trim(value);
+ trim(key, " \"");
+ trim(value, " \"");
+ if (!wrote) wrote = true;
+ else io << " ";
+ io << toImapString(key);
+ io << " ";
+ io << toImapString(value);
+ ++i;
+ }
+ io << ")";
+ } else
+ io << "NIL";
+ io << ")";
+ } else
+ io << "NIL";
+ } else
+ io << "NIL";
+
+ // CONTENT-LANGUAGE
+ io << " ";
+ printOneHeader(io, message, "content-language");
+
+ // CONTENT-LOCATION
+ io << " ";
+ printOneHeader(io, message, "content-location");
+ }
+
+ io << ")";
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+MaildirMessage::MaildirMessage(Maildir &hom)
+ : fd(-1), doc(0), internalFlags(None), stdflags(F_NONE),
+ uid(0), size(0), unique(""), safeName(""), internaldate(0),
+ home(hom), customFlags(0)
+{
+}
+
+//------------------------------------------------------------------------
+MaildirMessage::MaildirMessage(const MaildirMessage &copy)
+ : fd(copy.fd), doc(copy.doc), internalFlags(copy.internalFlags),
+ stdflags(copy.stdflags), uid(copy.uid), size(copy.size),
+ unique(copy.unique), safeName(copy.safeName),
+ internaldate(copy.internaldate), home(copy.home)
+{
+ if (copy.customFlags) {
+ customFlags = new vector<string>;
+ *customFlags = *copy.customFlags;
+ } else {
+ customFlags = 0;
+ }
+}
+
+//------------------------------------------------------------------------
+MaildirMessage::~MaildirMessage(void)
+{
+ delete customFlags;
+}
+
+//------------------------------------------------------------------------
+MaildirMessage &MaildirMessage::operator =(const MaildirMessage &copy)
+{
+ fd = copy.fd;
+ doc = copy.doc;
+ internalFlags = copy.internalFlags;
+ stdflags = copy.stdflags;
+ uid = copy.uid;
+ size = copy.size;
+ unique = copy.unique;
+ safeName = copy.safeName;
+ internaldate = copy.internaldate;
+ home = copy.home;
+
+ if (copy.customFlags) {
+ customFlags = new vector<string>;
+ *customFlags = *copy.customFlags;
+ } else {
+ customFlags = 0;
+ }
+
+ return *this;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::operator <(const MaildirMessage &a) const
+{
+ return uid < a.uid;
+}
+//------------------------------------------------------------------------
+void MaildirMessage::close(void)
+{
+ if (fd != -1) {
+ if ((internalFlags & WasWrittenTo) && fsync(fd) != 0
+ && errno != EINVAL && errno != EROFS) {
+ // FIXME: report this error
+ }
+
+ if (::close(fd) != 0) {
+ // FIXME: report this error
+ }
+
+ fd = -1;
+ }
+
+ // The file will not be moved from tmp/ before this function has
+ // finished. So it's safe to assume that safeName is still valid.
+ if (internalFlags & WasWrittenTo) {
+ if (internaldate != 0) {
+ struct utimbuf tim = {internaldate, internaldate};
+ utime(safeName.c_str(), &tim);
+ } else {
+ time_t t = time(0);
+ struct utimbuf tim = {t, t};
+ utime(safeName.c_str(), &tim);
+ }
+
+ internalFlags &= ~WasWrittenTo;
+ }
+
+
+ if (doc) {
+ doc->clear();
+ delete doc;
+ doc = 0;
+ }
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setExpunged(void)
+{
+ internalFlags |= Expunged;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setUnExpunged(void)
+{
+ internalFlags &= ~Expunged;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setFlagsUnchanged(void)
+{
+ internalFlags &= ~FlagsChanged;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::hasFlagsChanged(void) const
+{
+ return (internalFlags & FlagsChanged) != 0;
+}
+
+//------------------------------------------------------------------------
+unsigned char MaildirMessage::getStdFlags(void) const
+{
+ return stdflags;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::isExpunged(void) const
+{
+ return (internalFlags & Expunged) != 0;
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirMessage::getUID(void) const
+{
+ return uid;
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirMessage::getSize(bool render) const
+{
+ if (size == 0 && render) {
+ size = getDocSize();
+ home.mailboxchanged = true;
+ }
+
+ return size;
+}
+
+//------------------------------------------------------------------------
+const string &MaildirMessage::getUnique(void) const
+{
+ return unique;
+}
+
+//------------------------------------------------------------------------
+time_t MaildirMessage::getInternalDate(void) const
+{
+ return internaldate;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setInternalDate(time_t t)
+{
+ internaldate = t;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setStdFlag(unsigned char f_in)
+{
+ internalFlags |= FlagsChanged;
+ stdflags |= f_in;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::resetStdFlags(void)
+{
+ internalFlags |= FlagsChanged;
+ stdflags = F_NONE;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setUID(unsigned int i_in)
+{
+ uid = i_in;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setSize(unsigned int i_in)
+{
+ size = i_in;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setUnique(const string &s_in)
+{
+ unique = s_in;
+}
+
+//------------------------------------------------------------------------
+int MaildirMessage::getFile(void) const
+{
+ if (fd != -1)
+ return fd;
+
+ const string &id = getUnique();
+ MaildirIndexItem *item = home.index.find(id);
+ if (item) {
+ string fpath = home.path + "/cur/" + item->fileName;
+
+ unsigned int oflags = O_RDONLY;
+#ifdef HAVE_OLARGEFILE
+ oflags |= O_LARGEFILE;
+#endif
+ while ((fd = open(fpath.c_str(), oflags)) == -1) {
+ if (errno == ENOENT) {
+ struct stat st;
+ if (lstat(fpath.c_str(), &st) != -1) {
+ bincWarning << "dangling symlink: " << fpath << endl;
+ return -1;
+ }
+ } else {
+ bincWarning << "unable to open " << fpath << ": "
+ << strerror(errno) << endl;
+ return -1;
+ }
+
+ home.scanFileNames();
+ if ((item = home.index.find(id)) == 0)
+ break;
+ else
+ fpath = home.path + "/cur/" + item->fileName;
+ }
+
+ MaildirMessageCache &cache = MaildirMessageCache::getInstance();
+ cache.addStatus(this, MaildirMessageCache::NotParsed);
+
+ return fd;
+ }
+
+ return -1;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setFile(int fd)
+{
+ this->fd = fd;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setSafeName(const string &name)
+{
+ safeName = name;
+}
+
+//------------------------------------------------------------------------
+const string &MaildirMessage::getSafeName(void) const
+{
+ return safeName;
+}
+
+//------------------------------------------------------------------------
+string MaildirMessage::getFileName(void) const
+{
+ MaildirIndexItem *item = home.index.find(getUnique());
+ if (!item) {
+ home.scanFileNames();
+
+ if ((item = home.index.find(getUnique())) == 0)
+ return "";
+ }
+
+ return item->fileName;
+}
+
+//------------------------------------------------------------------------
+int MaildirMessage::readChunk(string &chunk)
+{
+ if (fd == -1) {
+ if ((fd = getFile()) == -1)
+ return -1;
+ }
+
+ char buffer[1024];
+ ssize_t readBytes = read(fd, buffer, (size_t) sizeof(buffer));
+ if (readBytes == -1) {
+ setLastError("Error reading from " + getFileName()
+ + ": " + string(strerror(errno)));
+ return -1;
+ }
+
+ chunk = string(buffer, readBytes);
+ return readBytes;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::appendChunk(const string &chunk)
+{
+ if (fd == -1) {
+ setLastError("Error writing to " + getSafeName()
+ + ": File is not open");
+ return false;
+ }
+
+ internalFlags |= WasWrittenTo;
+
+ string out;
+ for (string::const_iterator i = chunk.begin(); i != chunk.end(); ++i) {
+ const char c = *i;
+ if (c != '\r')
+ out += c;
+ }
+
+ ssize_t wroteBytes = 0;
+ for (;;) {
+ wroteBytes = write(fd, out.c_str(), (size_t) out.length());
+ if (wroteBytes == -1) {
+ if (errno == EINTR)
+ continue;
+ wroteBytes = 0;
+ }
+
+ break;
+ }
+
+ if (wroteBytes == (ssize_t) out.length())
+ return true;
+
+ setLastError("Error writing to " + getSafeName()
+ + ": " + string(strerror(errno)));
+ return false;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::parseFull(void) const
+{
+ MaildirMessageCache &cache = MaildirMessageCache::getInstance();
+ MaildirMessageCache::ParseStatus ps = cache.getStatus(this);
+ if (ps == MaildirMessageCache::AllParsed && doc)
+ return true;
+
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ // FIXME: parse errors
+ if (!doc)
+ doc = new MimeDocument;
+ doc->parseFull(fd);
+
+ cache.addStatus(this, MaildirMessageCache::AllParsed);
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::parseHeaders(void) const
+{
+ MaildirMessageCache &cache = MaildirMessageCache::getInstance();
+ MaildirMessageCache::ParseStatus ps = cache.getStatus(this);
+ if ((ps == MaildirMessageCache::AllParsed
+ || ps == MaildirMessageCache::HeaderParsed) && doc)
+ return true;
+
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ // FIXME: parse errors
+ if (!doc)
+ doc = new MimeDocument;
+ doc->parseOnlyHeader(fd);
+
+ cache.addStatus(this, MaildirMessageCache::HeaderParsed);
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::printBodyStructure(bool extended) const
+{
+ if (!parseFull())
+ return false;
+
+ bodyStructure(bincClient, doc, extended);
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::printEnvelope(void) const
+{
+ if (!parseFull())
+ return false;
+
+ envelope(bincClient, doc);
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::printHeader(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders,
+ unsigned int startOffset,
+ unsigned int length,
+ bool mime) const
+{
+ bincClient << storage;
+ storage = "";
+ return true;
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirMessage::getHeaderSize(const std::string &section,
+ std::vector<std::string> headers,
+ bool includeHeaders,
+ unsigned int startOffset,
+ unsigned int length,
+ bool mime) const
+{
+ if (section == "") {
+ if (!parseHeaders())
+ return 0;
+ } else if (!parseFull())
+ return 0;
+
+ const MimePart *part = doc->getPart(section, "", mime ? MimePart::FetchMime
+ : MimePart::FetchHeader);
+ if (!part) {
+ storage = "";
+ return 0;
+ }
+
+ int fd = getFile();
+ if (fd == -1)
+ return 0;
+
+ storage = "";
+ part->printHeader(fd, bincClient, headers,
+ includeHeaders, startOffset,
+ length, storage);
+
+ return storage.size();
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::printBody(const std::string &section,
+ unsigned int startOffset,
+ unsigned int length) const
+{
+ if (!parseFull())
+ return false;
+
+ const MimePart *part = doc->getPart(section, "");
+ if (!part) {
+ storage = "";
+ return 0;
+ }
+
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ storage = "";
+ part->printBody(fd, bincClient, startOffset, length);
+ return true;
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirMessage::getBodySize(const std::string &section,
+ unsigned int startOffset,
+ unsigned int length) const
+{
+ if (!parseFull())
+ return false;
+
+ const MimePart *part = doc->getPart(section, "");
+ if (!part) {
+ storage = "";
+ return 0;
+ }
+
+ if (startOffset > part->bodylength)
+ return 0;
+
+ unsigned int s = part->bodylength - startOffset;
+ return s < length ? s : length;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::printDoc(unsigned int startOffset,
+ unsigned int length, bool onlyText) const
+{
+ if (!parseFull())
+ return false;
+
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ if (onlyText)
+ startOffset += doc->bodystartoffsetcrlf;
+
+ storage = "";
+ doc->printDoc(fd, bincClient, startOffset, length);
+ return true;
+}
+
+//------------------------------------------------------------------------
+unsigned int MaildirMessage::getDocSize(unsigned int startOffset,
+ unsigned int length,
+ bool onlyText) const
+{
+ if (!parseFull())
+ return false;
+
+ unsigned int s = doc->size;
+ if (onlyText) s -= doc->bodystartoffsetcrlf;
+
+ if (startOffset > s)
+ return 0;
+
+ s -= startOffset;
+ return s < length ? s : length;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::headerContains(const std::string &header,
+ const std::string &text)
+{
+ if (!parseHeaders())
+ return false;
+
+ HeaderItem hitem;
+ if (!doc->h.getFirstHeader(header, hitem))
+ return false;
+
+ string tmp = hitem.getValue();
+ uppercase(tmp);
+ string tmp2 = text;
+ uppercase(tmp2);
+ return (tmp.find(tmp2) != string::npos);
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::bodyContains(const std::string &text)
+{
+ if (!parseFull())
+ return false;
+
+ // search the body part of the message..
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ MimeInputSource mimeSource(fd);
+
+ char c;
+ for (unsigned int i = 0; i < doc->getBodyStartOffset(); ++i)
+ if (!mimeSource.getChar(&c))
+ break;
+
+ char *ring = new char[text.length()];
+ int pos = 0;
+ int length = doc->getBodyLength();
+ const char *textStr = text.c_str();
+ unsigned int textLength = text.length();
+ while (mimeSource.getChar(&c) && length--) {
+ ring[pos % textLength] = toupper(c);
+
+ if (compareStringToQueue(textStr, ring, pos + 1, textLength)) {
+ delete[] ring;
+ return true;
+ }
+
+ ++pos;
+ }
+
+ delete[] ring;
+ return false;
+}
+
+//------------------------------------------------------------------------
+bool MaildirMessage::textContains(const std::string &text)
+{
+ // search the body part of the message..
+ int fd = getFile();
+ if (fd == -1)
+ return false;
+
+ MimeInputSource mimeSource(fd);
+
+ char c;
+ char *ring = new char[text.length()];
+ int pos = 0;
+ const char *textStr = text.c_str();
+ unsigned int textLength = text.length();
+ while (mimeSource.getChar(&c)) {
+ ring[pos % textLength] = toupper(c);
+
+ if (compareStringToQueue(textStr, ring, pos + 1, textLength)) {
+ delete[] ring;
+ return true;
+ }
+
+ ++pos;
+ }
+
+ delete[] ring;
+ return false;
+}
+
+//------------------------------------------------------------------------
+const std::string &MaildirMessage::getHeader(const std::string &header)
+{
+ static string NIL = "";
+
+ if (!parseHeaders())
+ return NIL;
+
+ HeaderItem hitem;
+ if (!doc->h.getFirstHeader(header, hitem))
+ return NIL;
+
+ return hitem.getValue();
+}
+
+//------------------------------------------------------------------------
+MaildirMessageCache::MaildirMessageCache(void)
+{
+}
+
+//------------------------------------------------------------------------
+MaildirMessageCache::~MaildirMessageCache(void)
+{
+ clear();
+}
+
+//------------------------------------------------------------------------
+MaildirMessageCache &MaildirMessageCache::getInstance(void)
+{
+ static MaildirMessageCache cache;
+ return cache;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessageCache::addStatus(const MaildirMessage *m,
+ ParseStatus s)
+{
+ if (statuses.find(m) == statuses.end()) {
+ // Insert status. Perhaps remove oldest status.
+ if (statuses.size() > 2) {
+ MaildirMessage *message = const_cast<MaildirMessage *>(parsed.front());
+
+ removeStatus(message);
+ }
+
+ parsed.push_back(m);
+ }
+
+ statuses[m] = s;
+}
+
+//------------------------------------------------------------------------
+MaildirMessageCache::ParseStatus
+MaildirMessageCache::getStatus(const MaildirMessage *m) const
+{
+ if (statuses.find(m) == statuses.end())
+ return NotParsed;
+
+ return statuses[m];
+}
+
+//------------------------------------------------------------------------
+void MaildirMessageCache::clear(void)
+{
+ for (deque<const MaildirMessage *>::iterator i = parsed.begin();
+ i != parsed.end(); ++i)
+ const_cast<MaildirMessage *>(*i)->close();
+
+ parsed.clear();
+ statuses.clear();
+}
+
+//------------------------------------------------------------------------
+void MaildirMessageCache::removeStatus(const MaildirMessage *m)
+{
+ if (statuses.find(m) == statuses.end())
+ return;
+
+ statuses.erase(statuses.find(m));
+
+ for (deque<const MaildirMessage *>::iterator i = parsed.begin();
+ i != parsed.end(); ++i) {
+ if (*i == m) {
+ const_cast<MaildirMessage *>(*i)->close();
+ parsed.erase(i);
+ break;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setInternalFlag(unsigned char f)
+{
+ internalFlags |= f;
+}
+
+//------------------------------------------------------------------------
+unsigned char MaildirMessage::getInternalFlags(void) const
+{
+ return internalFlags;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::clearInternalFlag(unsigned char f)
+{
+ internalFlags &= ~f;
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::setCustomFlag(const string &flag)
+{
+ if (!customFlags) {
+ internalFlags |= FlagsChanged | CustomFlagsChanged;
+ customFlags = new vector<string>;
+ }
+
+ for (vector<string>::const_iterator it = customFlags->begin();
+ it != customFlags->end(); ++it) {
+ if (*it == flag)
+ return;
+ }
+
+ internalFlags |= FlagsChanged | CustomFlagsChanged;
+ customFlags->push_back(flag);
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::removeCustomFlag(const string &flag)
+{
+ internalFlags |= FlagsChanged | CustomFlagsChanged;
+
+ if (!customFlags)
+ return;
+
+ for (vector<string>::iterator it = customFlags->begin();
+ it != customFlags->end(); ++it) {
+ if (*it == flag) {
+ customFlags->erase(it);
+ return;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+void MaildirMessage::resetCustomFlags(void)
+{
+ internalFlags |= FlagsChanged | CustomFlagsChanged;
+
+ delete customFlags;
+ customFlags = 0;
+}
+
+//------------------------------------------------------------------------
+vector<string> MaildirMessage::getCustomFlags(void) const
+{
+ if (!customFlags)
+ return vector<string>();
+
+ return *customFlags;
+}
diff --git a/src/mime-getpart.cc b/src/mime-getpart.cc
new file mode 100644
index 0000000..7ce84fd
--- /dev/null
+++ b/src/mime-getpart.cc
@@ -0,0 +1,69 @@
+/** --------------------------------------------------------------------
+ * @file mime-getpart.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "convert.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+const Binc::MimePart *Binc::MimePart::getPart(const string &findpart,
+ string genpart, FetchType fetchType) const
+{
+ if (findpart == genpart)
+ return this;
+
+ if (isMultipart()) {
+ if (members.size() != 0) {
+ vector<MimePart>::const_iterator i = members.begin();
+ int part = 1;
+ while (i != members.end()) {
+ BincStream ss;
+ ss << genpart;
+ if (genpart != "")
+ ss << ".";
+ ss << part;
+
+ const MimePart *m;
+ if ((m = (*i).getPart(findpart, ss.str())) != 0) {
+ if (fetchType == FetchHeader && m->isMessageRFC822())
+ m = &m->members[0];
+ return m;
+ }
+
+ ++i;
+ ++part;
+ }
+ }
+ } else if (isMessageRFC822()) {
+ if (members.size() == 1) {
+ const MimePart *m = members[0].getPart(findpart, genpart);
+ return m;
+ } else {
+ return 0;
+ }
+ } else {
+ // Singlepart
+ if (genpart != "")
+ genpart += ".";
+ genpart += "1";
+
+ if (findpart == genpart)
+ return this;
+ }
+
+ return 0;
+}
diff --git a/src/mime-parsefull.cc b/src/mime-parsefull.cc
new file mode 100644
index 0000000..53d07db
--- /dev/null
+++ b/src/mime-parsefull.cc
@@ -0,0 +1,603 @@
+/** --------------------------------------------------------------------
+ * @file mime-parsefull.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+#include "convert.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+Binc::MimeInputSource *mimeSource = 0;
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::MimeDocument::parseFull(int fd) const
+{
+ if (allIsParsed)
+ return;
+
+ allIsParsed = true;
+
+ if (!mimeSource || mimeSource->getFileDescriptor() != fd) {
+ delete mimeSource;
+ mimeSource = new MimeInputSource(fd);
+ } else {
+ mimeSource->reset();
+ }
+
+ headerstartoffsetcrlf = 0;
+ headerlength = 0;
+ bodystartoffsetcrlf = 0;
+ bodylength = 0;
+ size = 0;
+ messagerfc822 = false;
+ multipart = false;
+
+ int bsize = 0;
+ string bound;
+ MimePart::parseFull(bound, bsize);
+
+ // eat any trailing junk to get the correct size
+ char c;
+ while (mimeSource->getChar(&c));
+
+ size = mimeSource->getOffset();
+}
+
+//------------------------------------------------------------------------
+static bool parseOneHeaderLine(Binc::Header *header, unsigned int *nlines)
+{
+ using namespace ::Binc;
+ char c;
+ bool eof = false;
+ char cqueue[4];
+ string name;
+ string content;
+
+ while (mimeSource->getChar(&c)) {
+ // If we encounter a \r before we got to the first ':', then
+ // rewind back to the start of the line and assume we're at the
+ // start of the body.
+ if (c == '\r') {
+ for (int i = 0; i < (int) name.length() + 1; ++i)
+ mimeSource->ungetChar();
+ return false;
+ }
+
+ // A colon marks the end of the header name
+ if (c == ':') break;
+
+ // Otherwise add to the header name
+ name += c;
+ }
+
+ cqueue[0] = '\0';
+ cqueue[1] = '\0';
+ cqueue[2] = '\0';
+ cqueue[3] = '\0';
+
+ // Read until the end of the header.
+ bool endOfHeaders = false;
+ while (!endOfHeaders) {
+ if (!mimeSource->getChar(&c)) {
+ eof = true;
+ break;
+ }
+
+ if (c == '\n') ++*nlines;
+
+ for (int i = 0; i < 3; ++i)
+ cqueue[i] = cqueue[i + 1];
+ cqueue[3] = c;
+
+ if (strncmp(cqueue, "\r\n\r\n", 4) == 0) {
+ endOfHeaders = true;
+ break;
+ }
+
+ // If the last character was a newline, and the first now is not
+ // whitespace, then rewind one character and store the current
+ // key,value pair.
+ if (cqueue[2] == '\n' && c != ' ' && c != '\t') {
+ if (content.length() > 2)
+ content.resize(content.length() - 2);
+
+ trim(content);
+ header->add(name, content);
+
+ if (c != '\r') {
+ mimeSource->ungetChar();
+ if (c == '\n') --*nlines;
+ return true;
+ }
+
+ mimeSource->getChar(&c);
+ return false;
+ }
+
+ content += c;
+ }
+
+ if (name != "") {
+ if (content.length() > 2)
+ content.resize(content.length() - 2);
+ header->add(name, content);
+ }
+
+ return !(eof || endOfHeaders);
+}
+
+//------------------------------------------------------------------------
+static void parseHeader(Binc::Header *header, unsigned int *nlines)
+{
+ while (parseOneHeaderLine(header, nlines))
+ { }
+}
+
+//------------------------------------------------------------------------
+static void analyzeHeader(Binc::Header *header, bool *multipart,
+ bool *messagerfc822, string *subtype, string *boundary)
+{
+ using namespace ::Binc;
+
+ // Do simple parsing of headers to determine the
+ // type of message (multipart,messagerfc822 etc)
+ HeaderItem ctype;
+ if (header->getFirstHeader("content-type", ctype)) {
+ vector<string> types;
+ split(ctype.getValue(), ";", types);
+
+ if (types.size() > 0) {
+ // first element should describe content type
+ string tmp = types[0];
+ trim(tmp);
+ vector<string> v;
+ split(tmp, "/", v);
+ string key, value;
+
+ key = (v.size() > 0) ? v[0] : "text";
+ value = (v.size() > 1) ? v[1] : "plain";
+ lowercase(key);
+
+ if (key == "multipart") {
+ *multipart = true;
+ lowercase(value);
+ *subtype = value;
+ } else if (key == "message") {
+ lowercase(value);
+ if (value == "rfc822")
+ *messagerfc822 = true;
+ }
+ }
+
+ for (vector<string>::const_iterator i = types.begin();
+ i != types.end(); ++i) {
+ string element = *i;
+ trim(element);
+
+ if (element.find("=") != string::npos) {
+ string::size_type pos = element.find('=');
+ string key = element.substr(0, pos);
+ string value = element.substr(pos + 1);
+
+ lowercase(key);
+ trim(key);
+
+ if (key == "boundary") {
+ trim(value, " \"");
+ *boundary = value;
+ }
+ }
+ }
+ }
+}
+
+static void parseMessageRFC822(vector<Binc::MimePart> *members,
+ bool *foundendofpart,
+ unsigned int *bodylength,
+ unsigned int *nbodylines,
+ const string &toboundary)
+{
+ using namespace ::Binc;
+
+ // message rfc822 means a completely enclosed mime document. we
+ // call the parser recursively, and pass on the boundary string
+ // that we got. when parse() finds this boundary, it returns 0. if
+ // it finds the end boundary (boundary + "--"), it returns != 0.
+ MimePart m;
+
+ unsigned int bodystartoffsetcrlf = mimeSource->getOffset();
+
+ // parsefull returns the number of bytes that need to be removed
+ // from the body because of the terminating boundary string.
+ int bsize = 0;
+ if (m.parseFull(toboundary, bsize))
+ *foundendofpart = true;
+
+ // make sure bodylength doesn't overflow
+ *bodylength = mimeSource->getOffset();
+ if (*bodylength >= bodystartoffsetcrlf) {
+ *bodylength -= bodystartoffsetcrlf;
+ if (*bodylength >= (unsigned int) bsize) {
+ *bodylength -= (unsigned int) bsize;
+ } else {
+ *bodylength = 0;
+ }
+ } else {
+ *bodylength = 0;
+ }
+
+ *nbodylines += m.getNofLines();
+
+ members->push_back(m);
+}
+
+static bool skipUntilBoundary(const string &delimiter,
+ unsigned int *nlines, bool *eof)
+{
+ int endpos = delimiter.length();
+ char *delimiterqueue = 0;
+ int delimiterpos = 0;
+ const char *delimiterStr = delimiter.c_str();
+ if (delimiter != "") {
+ delimiterqueue = new char[endpos];
+ memset(delimiterqueue, 0, endpos);
+ }
+
+ // first, skip to the first delimiter string. Anything between the
+ // header and the first delimiter string is simply ignored (it's
+ // usually a text message intended for non-mime clients)
+ char c;
+
+ bool foundBoundary = false;
+ for (;;) {
+ if (!mimeSource->getChar(&c)) {
+ *eof = true;
+ break;
+ }
+
+ if (c == '\n') ++*nlines;
+
+ // if there is no delimiter, we just read until the end of the
+ // file.
+ if (!delimiterqueue) continue;
+
+ delimiterqueue[delimiterpos++ % endpos] = c;
+
+ if (compareStringToQueue(delimiterStr, delimiterqueue,
+ delimiterpos, endpos)) {
+ foundBoundary = true;
+ break;
+ }
+ }
+
+ delete[] delimiterqueue;
+ delimiterqueue = 0;
+
+ return foundBoundary;
+}
+
+
+static void parseMultipart(const string &boundary,
+ const string &toboundary,
+ bool *eof,
+ unsigned int *nlines,
+ int *boundarysize,
+ bool *foundendofpart,
+ unsigned int *bodylength,
+ vector<Binc::MimePart> *members)
+{
+ using namespace ::Binc;
+ unsigned int bodystartoffsetcrlf = mimeSource->getOffset();
+
+ // multipart parsing starts with skipping to the first
+ // boundary. then we call parse() for all parts. the last parse()
+ // command will return a code indicating that it found the last
+ // boundary of this multipart. Note that the first boundary does
+ // not have to start with CRLF.
+ string delimiter = "--" + boundary;
+
+ skipUntilBoundary(delimiter, nlines, eof);
+
+ if (!eof) *boundarysize = delimiter.size();
+
+ // Read two more characters. This may be CRLF, it may be "--" and
+ // it may be any other two characters.
+
+ char a;
+ if (!mimeSource->getChar(&a)) *eof = true;
+ if (a == '\n') ++*nlines;
+
+ char b;
+ if (!mimeSource->getChar(&b)) *eof = true;
+ if (b == '\n') ++*nlines;
+
+ // If we find two dashes after the boundary, then this is the end
+ // of boundary marker.
+ if (!*eof) {
+ if (a == '-' && b == '-') {
+ *foundendofpart = true;
+ *boundarysize += 2;
+
+ if (!mimeSource->getChar(&a)) *eof = true;
+ if (a == '\n') ++*nlines;
+ if (!mimeSource->getChar(&b)) *eof = true;
+ if (b == '\n') ++*nlines;
+ }
+
+ if (a == '\r' && b == '\n') {
+ // This exception is to handle a special case where the
+ // delimiter of one part is not followed by CRLF, but
+ // immediately followed by a CRLF prefixed delimiter.
+ if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b))
+ *eof = true;
+ else if (a == '-' && b == '-') {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+
+ *boundarysize += 2;
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+ }
+
+ // read all mime parts.
+ if (!*foundendofpart && !*eof) {
+ bool quit = false;
+ do {
+ MimePart m;
+
+ // If parseFull returns != 0, then it encountered the multipart's
+ // final boundary.
+ int bsize = 0;
+ if (m.parseFull(boundary, bsize)) {
+ quit = true;
+ *boundarysize = bsize;
+ }
+
+ members->push_back(m);
+
+ } while (!quit);
+ }
+
+ if (!*foundendofpart && !*eof) {
+ // multipart parsing starts with skipping to the first
+ // boundary. then we call parse() for all parts. the last parse()
+ // command will return a code indicating that it found the last
+ // boundary of this multipart. Note that the first boundary does
+ // not have to start with CRLF.
+ string delimiter = "\r\n--" + toboundary;
+
+ skipUntilBoundary(delimiter, nlines, eof);
+
+ if (!*eof) *boundarysize = delimiter.size();
+
+ // Read two more characters. This may be CRLF, it may be "--" and
+ // it may be any other two characters.
+
+ char a = '\0';
+ if (!mimeSource->getChar(&a)) *eof = true;
+ if (a == '\n') ++*nlines;
+
+ char b = '\0';
+ if (!mimeSource->getChar(&b)) *eof = true;
+ if (b == '\n') ++*nlines;
+
+ // If we find two dashes after the boundary, then this is the end
+ // of boundary marker.
+ if (!*eof) {
+ if (a == '-' && b == '-') {
+ *foundendofpart = true;
+ *boundarysize += 2;
+ if (!mimeSource->getChar(&a))
+ *eof = true;
+ if (a == '\n')
+ ++*nlines;
+ if (!mimeSource->getChar(&b))
+ *eof = true;
+ if (b == '\n')
+ ++*nlines;
+ }
+
+ if (a == '\r' && b == '\n') {
+ // This exception is to handle a special case where the
+ // delimiter of one part is not followed by CRLF, but
+ // immediately followed by a CRLF prefixed delimiter.
+ if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b))
+ *eof = true;
+ else if (a == '-' && b == '-') {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+
+ *boundarysize += 2;
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+ }
+ }
+
+ // make sure bodylength doesn't overflow
+ *bodylength = mimeSource->getOffset();
+ if (*bodylength >= bodystartoffsetcrlf) {
+ *bodylength -= bodystartoffsetcrlf;
+ if (*bodylength >= (unsigned int) *boundarysize) {
+ *bodylength -= (unsigned int) *boundarysize;
+ } else {
+ *bodylength = 0;
+ }
+ } else {
+ *bodylength = 0;
+ }
+}
+
+static void parseSinglePart(const string &toboundary,
+ int *boundarysize,
+ unsigned int *nbodylines,
+ unsigned int *nlines,
+ bool *eof, bool *foundendofpart,
+ unsigned int *bodylength)
+{
+ using namespace ::Binc;
+ unsigned int bodystartoffsetcrlf = mimeSource->getOffset();
+
+ // If toboundary is empty, then we read until the end of the
+ // file. Otherwise we will read until we encounter toboundary.
+ string _toboundary;
+ if (toboundary != "") {
+ _toboundary = "\r\n--";
+ _toboundary += toboundary;
+ }
+
+ // if (skipUntilBoundary(_toboundary, nlines, eof))
+ // *boundarysize = _toboundary.length();
+
+ char *boundaryqueue = 0;
+ int endpos = _toboundary.length();
+ if (toboundary != "") {
+ boundaryqueue = new char[endpos];
+ memset(boundaryqueue, 0, endpos);
+ }
+ int boundarypos = 0;
+
+ *boundarysize = 0;
+
+ const char *_toboundaryStr = _toboundary.c_str();
+ string line;
+ bool toboundaryIsEmpty = (toboundary == "");
+ char c;
+ while (mimeSource->getChar(&c)) {
+ if (c == '\n') { ++*nbodylines; ++*nlines; }
+ if (toboundaryIsEmpty) continue;
+
+ // find boundary
+ boundaryqueue[boundarypos++ % endpos] = c;
+
+ if (compareStringToQueue(_toboundaryStr, boundaryqueue,
+ boundarypos, endpos)) {
+ *boundarysize = _toboundary.length();
+ break;
+ }
+ }
+
+ delete[] boundaryqueue;
+
+ if (toboundary != "") {
+
+ char a;
+ if (!mimeSource->getChar(&a)) *eof = true;
+ if (a == '\n') ++*nlines;
+
+ char b;
+ if (!mimeSource->getChar(&b)) *eof = true;
+ if (b == '\n') ++*nlines;
+
+ if (a == '-' && b == '-') {
+ *boundarysize += 2;
+ *foundendofpart = true;
+ if (!mimeSource->getChar(&a)) *eof = true;
+ if (a == '\n') ++*nlines;
+ if (!mimeSource->getChar(&b)) *eof = true;
+ if (b == '\n') ++*nlines;
+ }
+
+ if (a == '\r' && b == '\n') {
+ // This exception is to handle a special case where the
+ // delimiter of one part is not followed by CRLF, but
+ // immediately followed by a CRLF prefixed delimiter.
+ if (!mimeSource->getChar(&a) || !mimeSource->getChar(&b))
+ *eof = true;
+ else if (a == '-' && b == '-') {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+
+ *boundarysize += 2;
+ } else {
+ mimeSource->ungetChar();
+ mimeSource->ungetChar();
+ }
+ }
+
+ // make sure bodylength doesn't overflow
+ *bodylength = mimeSource->getOffset();
+ if (*bodylength >= bodystartoffsetcrlf) {
+ *bodylength -= bodystartoffsetcrlf;
+ if (*bodylength >= (unsigned int) *boundarysize) {
+ *bodylength -= (unsigned int) *boundarysize;
+ } else {
+ *bodylength = 0;
+ }
+ } else {
+ *bodylength = 0;
+ }
+
+}
+
+//------------------------------------------------------------------------
+int Binc::MimePart::parseFull(const string &toboundary,
+ int &boundarysize) const
+{
+ headerstartoffsetcrlf = mimeSource->getOffset();
+
+ // Parse the header of this mime part.
+ parseHeader(&h, &nlines);
+
+ // Headerlength includes the seperating CRLF. Body starts after the
+ // CRLF.
+ headerlength = mimeSource->getOffset() - headerstartoffsetcrlf;
+ bodystartoffsetcrlf = mimeSource->getOffset();
+ bodylength = 0;
+
+ // Determine the type of mime part by looking at fields in the
+ // header.
+ analyzeHeader(&h, &multipart, &messagerfc822, &subtype, &boundary);
+
+ bool eof = false;
+ bool foundendofpart = false;
+
+ if (messagerfc822) {
+ parseMessageRFC822(&members, &foundendofpart, &bodylength,
+ &nbodylines, toboundary);
+
+ } else if (multipart) {
+ parseMultipart(boundary, toboundary, &eof, &nlines, &boundarysize,
+ &foundendofpart, &bodylength, &members);
+ } else {
+ parseSinglePart(toboundary, &boundarysize, &nbodylines, &nlines,
+ &eof, &foundendofpart, &bodylength);
+ }
+
+ return (eof || foundendofpart) ? 1 : 0;
+}
diff --git a/src/mime-parseonlyheader.cc b/src/mime-parseonlyheader.cc
new file mode 100644
index 0000000..d36efbf
--- /dev/null
+++ b/src/mime-parseonlyheader.cc
@@ -0,0 +1,146 @@
+/** --------------------------------------------------------------------
+ * @file mime-parseonlyheader.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+#include "convert.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::MimeDocument::parseOnlyHeader(int fd) const
+{
+ if (allIsParsed || headerIsParsed)
+ return;
+
+ headerIsParsed = true;
+
+ if (!mimeSource || mimeSource->getFileDescriptor() != fd) {
+ delete mimeSource;
+ mimeSource = new MimeInputSource(fd);
+ } else {
+ mimeSource->reset();
+ }
+
+
+ headerstartoffsetcrlf = 0;
+ headerlength = 0;
+ bodystartoffsetcrlf = 0;
+ bodylength = 0;
+ messagerfc822 = false;
+ multipart = false;
+
+ nlines = 0;
+ nbodylines = 0;
+
+ MimePart::parseOnlyHeader("");
+}
+
+//------------------------------------------------------------------------
+int Binc::MimePart::parseOnlyHeader(const string &toboundary) const
+{
+ string name;
+ string content;
+ char cqueue[4];
+ memset(cqueue, 0, sizeof(cqueue));
+
+ headerstartoffsetcrlf = mimeSource->getOffset();
+
+ bool quit = false;
+ char c = '\0';
+
+ while (!quit) {
+ // read name
+ while (1) {
+ if (!mimeSource->getChar(&c)) {
+ quit = true;
+ break;
+ }
+
+ if (c == '\n') ++nlines;
+ if (c == ':') break;
+ if (c == '\n') {
+ for (int i = name.length() - 1; i >= 0; --i)
+ mimeSource->ungetChar();
+
+ quit = true;
+ name = "";
+ break;
+ }
+
+ name += c;
+
+ if (name.length() == 2 && name.substr(0, 2) == "\r\n") {
+ name = "";
+ quit = true;
+ break;
+ }
+ }
+
+ if (name.length() == 1 && name[0] == '\r') {
+ name = "";
+ break;
+ }
+
+ if (quit) break;
+
+ while (!quit) {
+ if (!mimeSource->getChar(&c)) {
+ quit = true;
+ break;
+ }
+
+ if (c == '\n') ++nlines;
+
+ for (int i = 0; i < 3; ++i)
+ cqueue[i] = cqueue[i + 1];
+
+ cqueue[3] = c;
+ if (strncmp(cqueue, "\r\n\r\n", 4) == 0) {
+ quit = true;
+ break;
+ }
+
+ if (cqueue[2] == '\n') {
+ // guess the mime rfc says what can not appear on the beginning
+ // of a line.
+ if (!isspace(cqueue[3])) {
+ if (content.length() > 2)
+ content.resize(content.length() - 2);
+
+ trim(content);
+ h.add(name, content);
+ name = c;
+ content = "";
+ break;
+ }
+ }
+
+ content += c;
+ }
+ }
+
+ if (name != "") {
+ if (content.length() > 2)
+ content.resize(content.length() - 2);
+ h.add(name, content);
+ }
+
+ headerlength = mimeSource->getOffset() - headerstartoffsetcrlf;
+
+ return 1;
+}
diff --git a/src/mime-printbody.cc b/src/mime-printbody.cc
new file mode 100644
index 0000000..0c053d3
--- /dev/null
+++ b/src/mime-printbody.cc
@@ -0,0 +1,51 @@
+/** --------------------------------------------------------------------
+ * @file mime-printbody.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::MimePart::printBody(int fd, IODevice &output,
+ unsigned int startoffset,
+ unsigned int length) const
+{
+ if (!mimeSource || mimeSource->getFileDescriptor() != fd) {
+ delete mimeSource;
+ mimeSource = new MimeInputSource(fd);
+ }
+
+ mimeSource->reset();
+ mimeSource->seek(bodystartoffsetcrlf + startoffset);
+
+ if (startoffset + length > bodylength)
+ length = bodylength - startoffset;
+
+ char c = '\0';
+ for (unsigned int i = 0; i < length; ++i) {
+ if (!mimeSource->getChar(&c))
+ break;
+
+ output << (char)c;
+ }
+}
diff --git a/src/mime-printdoc.cc b/src/mime-printdoc.cc
new file mode 100644
index 0000000..bef673b
--- /dev/null
+++ b/src/mime-printdoc.cc
@@ -0,0 +1,47 @@
+/** --------------------------------------------------------------------
+ * @file mime-printdoc.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::MimePart::printDoc(int fd, IODevice &output,
+ unsigned int startoffset,
+ unsigned int length) const
+{
+ if (!mimeSource || mimeSource->getFileDescriptor() != fd) {
+ delete mimeSource;
+ mimeSource = new MimeInputSource(fd);
+ }
+
+ mimeSource->reset();
+ mimeSource->seek(headerstartoffsetcrlf);
+
+ char c;
+ for (unsigned int i = 0; i < length; ++i) {
+ if (!mimeSource->getChar(&c))
+ break;
+
+ output << (char)c;
+ }
+}
diff --git a/src/mime-printheader.cc b/src/mime-printheader.cc
new file mode 100644
index 0000000..84dca1e
--- /dev/null
+++ b/src/mime-printheader.cc
@@ -0,0 +1,172 @@
+/** --------------------------------------------------------------------
+ * @file mime-printheader.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "mime-utils.h"
+#include "mime-inputsource.h"
+#include "convert.h"
+#include "iodevice.h"
+#include "iofactory.h"
+
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+void Binc::MimePart::printHeader(int fd, IODevice &output,
+ vector<string> headers, bool includeheaders,
+ unsigned int startoffset,
+ unsigned int length, string &store) const
+{
+ if (!mimeSource || mimeSource->getFileDescriptor() != fd) {
+ delete mimeSource;
+ mimeSource = new MimeInputSource(fd);
+ }
+
+ mimeSource->seek(headerstartoffsetcrlf);
+
+ string name;
+ string content;
+ char cqueue[2];
+ memset(cqueue, 0, sizeof(cqueue));
+
+ bool quit = false;
+ char c = '\0';
+
+ unsigned int wrotebytes = 0;
+ unsigned int processedbytes = 0;
+ bool hasHeaderSeparator = false;
+
+ while (!quit) {
+ // read name
+ while (1) {
+ // allow EOF to end the header
+ if (!mimeSource->getChar(&c)) {
+ quit = true;
+ break;
+ }
+
+ // assume this character is part of the header name.
+ name += c;
+
+ // break on the first colon
+ if (c == ':') break;
+
+ // break if a '\n' turned up.
+ if (c == '\n') {
+ // end of headers detected
+ if (name == "\r\n") {
+ hasHeaderSeparator = true;
+ quit = true;
+ break;
+ }
+
+ // put all data back in the buffer to the beginning of this
+ // line.
+ for (int i = name.length(); i >= 0; --i)
+ mimeSource->ungetChar();
+
+ // abort printing of header. note that in this case, the
+ // headers will not end with a seperate \r\n.
+ quit = true;
+ name = "";
+ break;
+ }
+ }
+ if (quit) break;
+
+ // at this point, we have a name, that is - the start of a
+ // header. we'll read until the end of the header.
+ while (!quit) {
+ // allow EOF to end the header.
+ if (!mimeSource->getChar(&c)) {
+ quit = true;
+ break;
+ }
+ if (c == '\n') ++nlines;
+
+ // make a fifo queue of the last 4 characters.
+ cqueue[0] = cqueue[1];
+ cqueue[1] = c;
+
+ // print header
+ if (cqueue[0] == '\n' && cqueue[1] != '\t' && cqueue[1] != ' ') {
+ // it wasn't a space, so put it back as it is most likely
+ // the start of a header name. in any case it terminates the
+ // content part of this header.
+ mimeSource->ungetChar();
+
+ string lowername = name;
+ lowercase(lowername);
+ trim(lowername, ": \t");
+ bool foundMatch = false;
+
+ for (vector<string>::const_iterator i = headers.begin();
+ i != headers.end(); ++i) {
+ string nametmp = *i;
+ lowercase(nametmp);
+ if (nametmp == lowername) {
+ foundMatch = true;
+ break;
+ }
+ }
+
+ if (foundMatch == includeheaders || headers.size() == 0) {
+ string out = name + content;
+ for (string::const_iterator i = out.begin(); i != out.end(); ++i)
+ if (processedbytes >= startoffset && wrotebytes < length) {
+ if (processedbytes >= startoffset) {
+ store += *i;
+ ++wrotebytes;
+ }
+ } else
+ ++processedbytes;
+ }
+
+ // move on to the next header
+ content = "";
+ name = "";
+ break;
+ }
+ content += c;
+ }
+ } // end while loop
+
+ if (name != "") {
+ string lowername = name;
+ lowercase(lowername);
+ trim(lowername, ": \t");
+ bool foundMatch = false;
+ for (vector<string>::const_iterator i = headers.begin();
+ i != headers.end(); ++i) {
+ string nametmp = *i;
+ lowercase(nametmp);
+ if (nametmp == lowername) {
+ foundMatch = true;
+ break;
+ }
+ }
+
+ if (hasHeaderSeparator || foundMatch == includeheaders || headers.size() == 0) {
+ string out = name + content;
+ for (string::const_iterator i = out.begin(); i != out.end(); ++i)
+ if (processedbytes >= startoffset && wrotebytes < length) {
+ store += *i;
+ ++wrotebytes;
+ } else
+ ++processedbytes;
+ }
+ }
+}
diff --git a/src/mime.cc b/src/mime.cc
new file mode 100644
index 0000000..972d5e5
--- /dev/null
+++ b/src/mime.cc
@@ -0,0 +1,134 @@
+/** --------------------------------------------------------------------
+ * @file mime.cc
+ * @brief Implementation of main mime parser components
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "mime.h"
+#include "convert.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <exception>
+#include <iostream>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+Binc::MimeDocument::MimeDocument(void) : MimePart()
+{
+ allIsParsed = false;
+ headerIsParsed = false;
+}
+
+//------------------------------------------------------------------------
+Binc::MimeDocument::~MimeDocument(void)
+{
+}
+
+//------------------------------------------------------------------------
+void Binc::MimeDocument::clear(void) const
+{
+ members.clear();
+ h.clear();
+ headerIsParsed = false;
+ allIsParsed = false;
+}
+
+//------------------------------------------------------------------------
+void Binc::MimePart::clear(void) const
+{
+ members.clear();
+ h.clear();
+}
+
+//------------------------------------------------------------------------
+Binc::MimePart::MimePart(void)
+{
+ size = 0;
+ messagerfc822 = false;
+ multipart = false;
+
+ nlines = 0;
+ nbodylines = 0;
+}
+
+//------------------------------------------------------------------------
+Binc::MimePart::~MimePart(void)
+{
+}
+
+//------------------------------------------------------------------------
+Binc::HeaderItem::HeaderItem(void)
+{
+}
+
+//------------------------------------------------------------------------
+Binc::HeaderItem::HeaderItem(const string &key, const string &value)
+{
+ this->key = key;
+ this->value = value;
+}
+
+//------------------------------------------------------------------------
+Binc::Header::Header(void)
+{
+}
+
+//------------------------------------------------------------------------
+Binc::Header::~Header(void)
+{
+}
+
+//------------------------------------------------------------------------
+bool Binc::Header::getFirstHeader(const string &key, HeaderItem &dest) const
+{
+ string k = key;
+ lowercase(k);
+
+ for (vector<HeaderItem>::const_iterator i = content.begin();
+ i != content.end(); ++i) {
+ string tmp = (*i).getKey();
+ lowercase(tmp);
+
+ if (tmp == k) {
+ dest = *i;
+ return true;
+ }
+ }
+ return false;
+}
+
+//------------------------------------------------------------------------
+bool Binc::Header::getAllHeaders(const string &key, vector<HeaderItem> &dest) const
+{
+ string k = key;
+ lowercase(k);
+
+ for (vector<HeaderItem>::const_iterator i = content.begin();
+ i != content.end(); ++i) {
+ string tmp = (*i).getKey();
+ lowercase(tmp);
+ if (tmp == k)
+ dest.push_back(*i);
+ }
+
+ return (dest.size() != 0);
+}
+
+//------------------------------------------------------------------------
+void Binc::Header::clear(void) const
+{
+ content.clear();
+}
+
+//------------------------------------------------------------------------
+void Binc::Header::add(const string &key, const string &value)
+{
+ content.push_back(HeaderItem(key, value));
+}
diff --git a/src/multilogdevice.cc b/src/multilogdevice.cc
new file mode 100644
index 0000000..612bb33
--- /dev/null
+++ b/src/multilogdevice.cc
@@ -0,0 +1,86 @@
+/** --------------------------------------------------------------------
+ * @file multilogdevice.cc
+ * @brief Implementation of the MultilogDevice class
+ * @author Andreas Aardal Hanssen
+ * @date 2003/2023
+ * --------------------------------------------------------------- **/
+#include "multilogdevice.h"
+#include <string>
+
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace ::std;
+using namespace ::Binc;
+
+//------------------------------------------------------------------------
+MultilogDevice::MultilogDevice(int f) : IODevice(f)
+{
+}
+
+//------------------------------------------------------------------------
+MultilogDevice::~MultilogDevice(void)
+{
+}
+
+//------------------------------------------------------------------------
+string MultilogDevice::service(void) const
+{
+ return "log";
+}
+
+//------------------------------------------------------------------------
+bool MultilogDevice::waitForWrite(void) const
+{
+ fd_set writeMask;
+ FD_ZERO(&writeMask);
+ FD_SET(fileno(stderr), &writeMask);
+
+ struct timeval tv;
+ tv.tv_sec = getTimeout();
+ tv.tv_usec = 0;
+
+ int result = select(fileno(stderr) + 1, 0, &writeMask,
+ 0, tv.tv_sec ? &tv : 0);
+
+ return result > 0;
+}
+
+//------------------------------------------------------------------------
+bool MultilogDevice::waitForRead(void) const
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+IODevice::WriteResult MultilogDevice::write(void)
+{
+ for (;;) {
+ ssize_t wrote = ::write(fileno(stderr), outputBuffer.str().c_str(),
+ outputBuffer.getSize());
+
+ if (wrote == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return WriteError;
+ }
+
+ if ((unsigned int) wrote == outputBuffer.getSize()) {
+ outputBuffer.clear();
+ return WriteDone;
+ }
+
+ outputBuffer.popString(wrote);
+ return WriteWait;
+ }
+}
+
+//------------------------------------------------------------------------
+bool MultilogDevice::fillInputBuffer(void)
+{
+ return false;
+}
diff --git a/src/operator-append.cc b/src/operator-append.cc
new file mode 100644
index 0000000..d4650ee
--- /dev/null
+++ b/src/operator-append.cc
@@ -0,0 +1,297 @@
+/** --------------------------------------------------------------------
+ * @file operator-append.cc
+ * @brief Implementation of the APPEND command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <algorithm>
+#include <string>
+
+#include <fcntl.h>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "pendingupdates.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+AppendOperator::AppendOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+AppendOperator::~AppendOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string AppendOperator::getName(void) const
+{
+ return "APPEND";
+}
+
+//----------------------------------------------------------------------
+int AppendOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult AppendOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ const string &srcmailbox = command.getMailbox();
+ const string &canonmailbox = toCanonMailbox(srcmailbox);
+ Mailbox *mailbox = 0;
+
+ if ((mailbox = depot.get(canonmailbox)) == 0) {
+ session.setResponseCode("TRYCREATE");
+ session.setLastError("invalid destination mailbox "
+ + toImapString(srcmailbox));
+ return NO;
+ }
+
+ // mask all passed flags together
+ unsigned int newflags = (unsigned int) Message::F_NONE;
+ vector<string>::const_iterator f_i = command.flags.begin();
+ while (f_i != command.flags.end()) {
+ if (*f_i == "\\Deleted") newflags |= Message::F_DELETED;
+ if (*f_i == "\\Answered") newflags |= Message::F_ANSWERED;
+ if (*f_i == "\\Seen") newflags |= Message::F_SEEN;
+ if (*f_i == "\\Draft") newflags |= Message::F_DRAFT;
+ if (*f_i == "\\Flagged") newflags |= Message::F_FLAGGED;
+ ++f_i;
+ }
+
+ int mday, year, hour, minute, second;
+ char month[4];
+
+ struct tm mytm;
+ if (command.getDate() != "") {
+ sscanf(command.getDate().c_str(), "%2i-%3s-%4i %2i:%2i:%2i",
+ &mday, month, &year, &hour, &minute, &second);
+
+ month[3] = '\0';
+ string monthstr = month;
+ lowercase(monthstr);
+ mytm.tm_sec = second;
+ mytm.tm_min = minute;
+ mytm.tm_hour = hour;
+ mytm.tm_year = year - 1900;
+ mytm.tm_mday = mday;
+ if (monthstr == "jan") mytm.tm_mon = 0;
+ else if (monthstr == "feb") mytm.tm_mon = 1;
+ else if (monthstr == "mar") mytm.tm_mon = 2;
+ else if (monthstr == "apr") mytm.tm_mon = 3;
+ else if (monthstr == "may") mytm.tm_mon = 4;
+ else if (monthstr == "jun") mytm.tm_mon = 5;
+ else if (monthstr == "jul") mytm.tm_mon = 6;
+ else if (monthstr == "aug") mytm.tm_mon = 7;
+ else if (monthstr == "sep") mytm.tm_mon = 8;
+ else if (monthstr == "oct") mytm.tm_mon = 9;
+ else if (monthstr == "nov") mytm.tm_mon = 10;
+ else if (monthstr == "dec") mytm.tm_mon = 11;
+ mytm.tm_isdst = -1;
+ }
+
+ // Read number of characters in literal. Literal is required here.
+ char c;
+ if (!bincClient.readChar(&c)) return ABORT;
+
+ if (c != '{') {
+ session.setLastError("expected literal");
+ return BAD;
+ }
+
+ string nr;
+ bool literalPlus = false;
+ while (1) {
+ if (!bincClient.readChar(&c)) {
+ session.setLastError("unexcepted EOF");
+ return BAD;
+ }
+
+ if (c == '}') break;
+
+ // Support LITERAL+
+ if (c == '+' && !literalPlus) {
+ literalPlus = true;
+ continue;
+ }
+
+ if (!isdigit(c)) {
+ session.setLastError("unexcepted non-digit character");
+ return BAD;
+ }
+
+ if (literalPlus) {
+ session.setLastError("expected '}'");
+ return BAD;
+ }
+
+ nr += (char) c;
+ }
+
+ int nchars = atoi(nr.c_str());
+ if (nchars < 0) {
+ session.setLastError("expected positive size of appended message");
+ return BAD;
+ }
+
+ if (!bincClient.readChar(&c)) return ABORT;
+
+ if (c != '\r') {
+ session.setLastError("expected CR");
+ return BAD;
+ }
+
+ if (!bincClient.readChar(&c)) return ABORT;
+
+ if (c != '\n') {
+ session.setLastError("expected LF");
+ return BAD;
+ }
+
+ time_t newtime = (command.getDate() != "") ? mktime(&mytm) : time(0);
+ if (newtime == -1) newtime = time(0);
+ Message *dest = mailbox->createMessage(depot.mailboxToFilename(canonmailbox),
+ newtime);
+ if (!dest) {
+ session.setLastError(mailbox->getLastError());
+ return NO;
+ }
+
+ if (!literalPlus) {
+ bincClient << "+ go ahead with " << nchars << " characters" << endl;
+ bincClient.flush();
+ }
+
+ bincClient.clearFlags(IODevice::HasInputLimit);
+
+ while (nchars > 0) {
+ // Read in chunks of 8192, followed by an optional chunk at the
+ // end which is < 8192 bytes.
+ string s;
+ int bytesToRead = nchars > 8192 ? 8192 : nchars;
+ if (!bincClient.readStr(&s, bytesToRead)) {
+ mailbox->rollBackNewMessages();
+ session.setLastError(bincClient.getLastErrorString());
+ return NO;
+ }
+
+ // Write the chunk to the message.
+ if (!dest->appendChunk(s)) {
+ mailbox->rollBackNewMessages();
+ session.setLastError(dest->getLastError());
+ return NO;
+ }
+
+ // Update the message count.
+ nchars -= s.size();
+ }
+
+ // Read the trailing CRLF after the message data.
+ if (!bincClient.readChar(&c)) return ABORT;
+
+ if (c != '\r') {
+ mailbox->rollBackNewMessages();
+ session.setLastError("expected CR");
+ return BAD;
+ }
+
+ if (!bincClient.readChar(&c)) return ABORT;
+
+ if (c != '\n') {
+ mailbox->rollBackNewMessages();
+ session.setLastError("expected LF");
+ return BAD;
+ }
+
+ // Commit the message.
+ dest->close();
+ dest->setStdFlag(newflags);
+ dest->setInternalDate(mktime(&mytm));
+
+ if (!mailbox->commitNewMessages(depot.mailboxToFilename(canonmailbox))) {
+ session.setLastError("failed to commit after successful APPEND: "
+ + mailbox->getLastError());
+ return NO;
+ }
+
+ if (mailbox == depot.getSelected()) {
+ pendingUpdates(mailbox, PendingUpdates::EXISTS
+ | PendingUpdates::RECENT
+ | PendingUpdates::FLAGS, true, false, true);
+ }
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult AppendOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+ Operator::ParseResult res;
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after APPEND");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after APPEND SPACE");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after APPEND SPACE mailbox");
+ return res;
+ }
+
+ if ((res = expectThisString("(")) == ACCEPT) {
+ if ((res = expectFlag(c_in.getFlags())) == ACCEPT)
+ while (1) {
+ if ((res = expectSPACE()) != ACCEPT) break;
+ if ((res = expectFlag(c_in.getFlags())) != ACCEPT) {
+ session.setLastError("expected a flag after the '('");
+ return res;
+ }
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("expected a ')'");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("expected a SPACE after the flag list");
+ return res;
+ }
+ }
+
+ string date;
+ if ((res = expectDateTime(date)) == ACCEPT)
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("expected a SPACE after date_time");
+ return res;
+ }
+
+ c_in.setDate(date);
+ c_in.setName("APPEND");
+ return ACCEPT;
+}
diff --git a/src/operator-authenticate.cc b/src/operator-authenticate.cc
new file mode 100644
index 0000000..03f994c
--- /dev/null
+++ b/src/operator-authenticate.cc
@@ -0,0 +1,315 @@
+/** --------------------------------------------------------------------
+ * @file operator-authenticate.cc
+ * @brief Implementation of the AUTHENTICATE command, incl. CRAM-MD5.
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "authenticate.h"
+#include "base64.h"
+#include "convert.h"
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "globals.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include <cstring>
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+AuthenticateOperator::AuthenticateOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+AuthenticateOperator::~AuthenticateOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string AuthenticateOperator::getName(void) const
+{
+ return "AUTHENTICATE";
+}
+
+//----------------------------------------------------------------------
+int AuthenticateOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult AuthenticateOperator::Login(string& username, string& password)
+{
+ Session &session = Session::getInstance();
+
+ bincClient << "+ " << base64encode("User Name") << endl;
+ bincClient.flush();
+
+ // Read user name
+ string b64usr;
+ for (;;) {
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError("unexpected EOF");
+ return BAD;
+ }
+ if (c == '\n') break;
+ b64usr += c;
+ }
+
+ if (b64usr != "" && b64usr[0] == '*') {
+ session.setLastError("Authentication cancelled by user");
+ return NO;
+ }
+
+ bincClient << "+ " << base64encode("Password") << endl;
+ bincClient.flush();
+
+ // Read password
+ string b64pwd;
+ for (;;) {
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError("unexpected EOF");
+ return BAD;
+ }
+ if (c == '\n') break;
+ b64pwd += c;
+ }
+
+ if (b64pwd != "" && b64pwd[0] == '*') {
+ session.setLastError("Authentication cancelled by user");
+ return NO;
+ }
+
+ username = base64decode(b64usr);
+ password = base64decode(b64pwd);
+
+ return OK;
+}
+//------------------------------------------------------------------------
+Operator::ProcessResult AuthenticateOperator::Plain(string& username, string& password)
+{
+ Session &session = Session::getInstance();
+
+ bincClient << "+ " << endl;
+ bincClient.flush();
+
+ string b64;
+ for (;;) {
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError("unexpected EOF");
+ return BAD;
+ }
+ if (c == '\n') break;
+
+ b64 += c;
+ }
+
+ if (b64.size() >= 1 && b64[0] == '*') {
+ session.setLastError("Authentication cancelled by user");
+ return NO;
+ }
+
+ string plain = base64decode(b64);
+ string::size_type pos = 0;
+
+ if ((pos = plain.find('\0')) == string::npos) {
+ session.setLastError("Authentication failed. In PLAIN mode, "
+ "there must be at least two null characters "
+ "in the input string, but none were found");
+ return NO;
+ }
+
+ plain = plain.substr(pos + 1);
+ if ((pos = plain.find('\0')) == string::npos) {
+ session.setLastError("Authentication failed. In PLAIN mode, "
+ "there must be at least two null characters "
+ "in the input string, but only one was found");
+ return NO;
+ }
+
+ username = plain.substr(0, pos);
+ password = plain.substr(pos + 1);
+
+ return OK;
+}
+//------------------------------------------------------------------------
+Operator::ProcessResult AuthenticateOperator::Cram(string& username, string& password,
+ string& challenge)
+{
+ Session &session = Session::getInstance();
+
+ // generate challenge first: <pid.time@fqdn> and deploy it to authenticator
+ time_t timer;
+ struct tm y2k = {0};
+ int timestamp;
+ y2k.tm_hour = 0; y2k.tm_min = 0; y2k.tm_sec = 0;
+ y2k.tm_year = 100; y2k.tm_mon = 0; y2k.tm_mday = 1;
+
+ time(&timer); /* get current time; same as: timer = time(NULL) */
+ timestamp = difftime(timer,mktime(&y2k));
+
+ challenge += "<";
+ challenge += to_string(session.getPid());
+ challenge += ".";
+ challenge += to_string(timestamp);
+ challenge += "@";
+ challenge += session.getEnv("TCPLOCALHOST");
+ challenge += ">";
+
+ bincClient << "+ " << base64encode(challenge) << endl;
+ bincClient.flush();
+
+ // Read response
+ string b64;
+ for (;;) {
+ char c;
+ if (!bincClient.readChar(&c)) return BAD;
+ if (c == '\n') break;
+ b64 += c;
+ }
+
+ // Disentangle response
+ string response = base64decode(b64);
+ string::size_type pos = 0;
+
+ if ((pos = response.find(' ')) == string::npos) {
+ session.setLastError("Authentication failed. In CRAM-MD5 mode, "
+ "there must be a white space in the "
+ "input string between username and digest");
+ return NO;
+ }
+
+ username = response.substr(0, pos);
+ password = response.substr(pos + 1);
+
+ return OK;
+}
+//------------------------------------------------------------------------
+Operator::ProcessResult AuthenticateOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ string authtype = command.getAuthType();
+ uppercase(authtype);
+
+ string username;
+ string password;
+ string challenge;
+ ProcessResult r = NOTHING;
+
+ if (authtype == "LOGIN") {
+ // we only allow this type of authentication over an unencryted connection
+ // if it is explicitely commanded
+ if (!session.command.ssl
+ && !session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
+ session.setLastError("Plain text password authentication is disallowd. "
+ "Please enable StartTLS or TLS in your mail client.");
+ return NO;
+ }
+ if ((r = Login(username, password)) != OK) return r;
+
+ } else if (authtype == "PLAIN") {
+ // we only allow this type of authentication over an TLS encrypted connection.
+ if (!session.command.ssl
+ && !session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
+ session.setLastError("Plain text password authentication is disallowd. "
+ "Please enable StartTLS or TLS in your mail client.");
+ return NO;
+ }
+ if ((r = Plain(username, password)) != OK) return r;
+
+ } else if (authtype == "CRAM-MD5" ) {
+ // this type can be used even over unencrypted connections
+ if ((r = Cram(username, password, challenge)) != OK) return r;
+
+
+ } else { // Any other disallowed
+ session.setLastError("The authentication method "
+ + toImapString(authtype) + " is not supported. "
+ "Please try again with a different method. "
+ "There is built in support for \"PLAIN\" "
+ "and \"LOGIN\".");
+ return NO;
+ }
+
+ putenv(strdup(("BINCIMAP_LOGIN=AUTHENTICATE+" + command.getTag()).c_str()));
+
+ // put the username in the environment for logging purpose
+
+ session.setEnv("USER", username.c_str());
+
+ // the authenticate function calls a stub which does the actual
+ // authentication. the function returns 0 (success), 1 (internal
+ // error) or 2 (failed)
+
+ switch (authenticate(depot, username, password, challenge)) {
+ case 1:
+ session.setLastError("An internal error occurred when you attempted "
+ "to log in to the IMAP server. Please contact "
+ "your system administrator.");
+ return NO;
+ case 2:
+ session.setLastError("Login failed. Either your user name "
+ "or your password was wrong. Please try again, "
+ "and if the problem persists, please contact "
+ "your system administrator.");
+ return NO;
+ case 3:
+ bincClient << "* BYE Timeout after " << IDLE_TIMEOUT
+ << " seconds of inactivity." << endl;
+ break;
+ case -1:
+ bincClient << "* BYE The server died unexpectedly. Please contact "
+ "your system administrator for more information." << endl;
+ break;
+ default:
+// bincLog << "<" << username.c_str() << "> authenticated" << endl;
+ break;
+ }
+
+ // auth was ok. go to logout state
+ session.setState(Session::LOGOUT);
+ return NOTHING;
+}
+
+
+//----------------------------------------------------------------------
+Operator::ParseResult AuthenticateOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected single SPACE after AUTHENTICATE");
+ return res;
+ }
+
+ string authtype;
+ if ((res = expectAtom(authtype)) != ACCEPT) {
+ session.setLastError("Expected auth_type after AUTHENTICATE SPACE");
+ return ERROR;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after AUTHENTICATE SPACE auth_type");
+ return res;
+ }
+
+ c_in.setAuthType(authtype);
+
+ c_in.setName("AUTHENTICATE");
+ return ACCEPT;
+}
diff --git a/src/operator-capability.cc b/src/operator-capability.cc
new file mode 100644
index 0000000..bdead58
--- /dev/null
+++ b/src/operator-capability.cc
@@ -0,0 +1,100 @@
+/** --------------------------------------------------------------------
+ * @file bincimapd-capability.cc
+ * @brief Implementation of the CAPABILITY command
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CapabilityOperator::CapabilityOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+CapabilityOperator::~CapabilityOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string CapabilityOperator::getName(void) const
+{
+ return "CAPABILITY";
+}
+
+//----------------------------------------------------------------------
+int CapabilityOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+void CapabilityOperator::addCapability(const string &cap)
+{
+ capabilities.push_back(cap);
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult CapabilityOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ bincClient << "* CAPABILITY " << IMAP_VERSION ;
+
+ if (session.getState() == Session::NONAUTHENTICATED) {
+ if (getenv("UCSPITLS"))
+ if (!session.command.ssl) bincClient << " STARTTLS";
+
+ const string authmethods = session.getEnv("BINCIMAP_LOGIN");
+ auto cram = authmethods.find("+CRAM-MD5");
+
+ if (session.command.ssl || session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
+ if (cram != string::npos) bincClient << " AUTH=LOGIN AUTH=PLAIN AUTH=CRAM-MD5";
+ else bincClient << " AUTH=LOGIN AUTH=PLAIN";
+ } else
+ bincClient << " LOGINDISABLED";
+ }
+
+ bincClient << " IDLE LITERAL+ NAMESPACE CHILDREN";
+
+ vector<string>::const_iterator i = capabilities.begin();
+ while (i != capabilities.end()) {
+ bincClient << " " << *i;
+ ++i;
+ }
+ bincClient << endl;
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult CapabilityOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after CAPABILITY");
+ return res;
+ }
+
+ c_in.setName("CAPABILITY");
+
+ return ACCEPT;
+}
diff --git a/src/operator-check.cc b/src/operator-check.cc
new file mode 100644
index 0000000..ee58d8f
--- /dev/null
+++ b/src/operator-check.cc
@@ -0,0 +1,71 @@
+/** --------------------------------------------------------------------
+ * @file operator-check.cc
+ * @author Implementation of the CHECK command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "pendingupdates.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CheckOperator::CheckOperator(void)
+{
+}
+
+
+//----------------------------------------------------------------------
+CheckOperator::~CheckOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string CheckOperator::getName(void) const
+{
+ return "CHECK";
+}
+
+//----------------------------------------------------------------------
+int CheckOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult CheckOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+ if (mailbox != 0)
+ pendingUpdates(mailbox, PendingUpdates::FLAGS
+ | PendingUpdates::EXISTS
+ | PendingUpdates::RECENT, true);
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult CheckOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after CHECK");
+ return res;
+ }
+
+ c_in.setName("CHECK");
+ return ACCEPT;
+}
+
diff --git a/src/operator-close.cc b/src/operator-close.cc
new file mode 100644
index 0000000..d67dcc7
--- /dev/null
+++ b/src/operator-close.cc
@@ -0,0 +1,71 @@
+/** --------------------------------------------------------------------
+ * @file operator-close.cc
+ * @brief Implementation of the CLOSE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CloseOperator::CloseOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+CloseOperator::~CloseOperator(void)
+{
+}
+
+//------------------------------------------------------------------------
+const string CloseOperator::getName(void) const
+{
+ return "CLOSE";
+}
+
+//------------------------------------------------------------------------
+int CloseOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult CloseOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+ mailbox->expungeMailbox();
+ mailbox->closeMailbox();
+ depot.resetSelected();
+
+ Session &session = Session::getInstance();
+ session.setState(Session::AUTHENTICATED);
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult CloseOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after CLOSE");
+ return res;
+ }
+
+ c_in.setName("CLOSE");
+ return ACCEPT;
+}
diff --git a/src/operator-copy.cc b/src/operator-copy.cc
new file mode 100644
index 0000000..cbe8767
--- /dev/null
+++ b/src/operator-copy.cc
@@ -0,0 +1,169 @@
+/** --------------------------------------------------------------------
+ * @file operator-copy.cc
+ * @brief Implementation of the COPY command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "maildir.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CopyOperator::CopyOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+CopyOperator::~CopyOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string CopyOperator::getName(void) const
+{
+ return "COPY";
+}
+
+//----------------------------------------------------------------------
+int CopyOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult CopyOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ // Get the current mailbox
+ Mailbox *srcMailbox = depot.getSelected();
+
+ // Get the destination mailbox
+ string dmailbox = command.getMailbox();
+ Mailbox *destMailbox = depot.get(toCanonMailbox(dmailbox));
+ if (destMailbox == 0) {
+ session.setResponseCode("TRYCREATE");
+ session.setLastError("invalid mailbox " + toImapString(dmailbox));
+ return NO;
+ }
+
+ unsigned int mode = Mailbox::SKIP_EXPUNGED;
+ mode |= command.getUidMode() ? Mailbox::UID_MODE : Mailbox::SQNR_MODE;
+
+ // Copy each message in the sequence set to the destination mailbox.
+ bool success = true;
+ Mailbox::iterator i = srcMailbox->begin(command.bset, mode);
+ for (; success && i != srcMailbox->end(); ++i) {
+ Message &source = *i;
+
+ if (srcMailbox->fastCopy(source, *destMailbox,
+ depot.mailboxToFilename(toCanonMailbox(dmailbox))))
+ continue;
+
+ // Have the destination mailbox create a message for us.
+ Message *dest
+ = destMailbox->createMessage(depot.mailboxToFilename(toCanonMailbox(dmailbox)),
+ source.getInternalDate());
+ if (!dest) {
+ session.setLastError(destMailbox->getLastError());
+ success = false;
+ break;
+ }
+
+ // Set the flags and internal date.
+ dest->setStdFlag(source.getStdFlags());
+ dest->setInternalDate(source.getInternalDate());
+
+ // Copy chunks from the source message over to the destination
+ // message.
+ string chunk;
+ do {
+ int readSize = source.readChunk(chunk);
+
+ if (readSize == 0) break;
+ else if (readSize == -1) {
+ bincWarning << "when reading from message "
+ << i.getSqnr() << "/" << source.getUID()
+ << " in \"" << srcMailbox->getName() << "\": "
+ << source.getLastError() << endl;
+ success = false;
+ } else if (!dest->appendChunk(chunk)) {
+ bincWarning << "when writing to \""
+ << dmailbox << "\": "
+ << dest->getLastError() << endl;
+ success = false;
+ }
+ } while (success);
+
+ dest->close();
+ }
+
+ if (!success && !destMailbox->rollBackNewMessages()) {
+ session.setLastError("Failed to rollback after unsuccessful copy: "
+ + destMailbox->getLastError());
+ return NO;
+ }
+
+ if (success)
+ if (!destMailbox->commitNewMessages(depot.mailboxToFilename(toCanonMailbox(dmailbox)))) {
+ session.setLastError("Failed to commit after successful copy: "
+ + destMailbox->getLastError());
+ return NO;
+ }
+
+ if (!success)
+ session.setLastError("The transaction was unrolled. Please "
+ "contant your system administrator for "
+ "more information.");
+
+ return success ? OK : NO;
+}
+
+//------------------------------------------------------------------------
+Operator::ParseResult CopyOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after COPY");
+ return res;
+ }
+
+ if ((res = expectSet(c_in.getSet())) != ACCEPT) {
+ session.setLastError("Expected sequence set after COPY SPACE");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after COPY SPACE set");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after COPY SPACE set SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after COPY SPACE set SPACE mailbox");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+ c_in.setName("COPY");
+
+ return ACCEPT;
+}
diff --git a/src/operator-create.cc b/src/operator-create.cc
new file mode 100644
index 0000000..409b73f
--- /dev/null
+++ b/src/operator-create.cc
@@ -0,0 +1,84 @@
+/** --------------------------------------------------------------------
+ * @file bincimapd-create.cc
+ * @brief Implementation of the CREATE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "imapparser.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+CreateOperator::CreateOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+CreateOperator::~CreateOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string CreateOperator::getName(void) const
+{
+ return "CREATE";
+}
+
+//----------------------------------------------------------------------
+int CreateOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult CreateOperator::process(Depot &depot,
+ Request &command)
+{
+ if (depot.createMailbox(command.getMailbox()))
+ return OK;
+ else {
+ Session &session = Session::getInstance();
+ session.setLastError(depot.getLastError());
+ return NO;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult CreateOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after CREATE");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after CREATE SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after CREATE SPACE CRLF");
+ return res;
+ }
+
+ session.mailboxchanges = true;
+
+ c_in.setName("CREATE");
+ c_in.setMailbox(mailbox);
+ return ACCEPT;
+}
diff --git a/src/operator-delete.cc b/src/operator-delete.cc
new file mode 100644
index 0000000..0365927
--- /dev/null
+++ b/src/operator-delete.cc
@@ -0,0 +1,84 @@
+/** --------------------------------------------------------------------
+ * @file operator-delete.cc
+ * @briefe Implementation of the DELETE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "imapparser.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+DeleteOperator::DeleteOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+DeleteOperator::~DeleteOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string DeleteOperator::getName(void) const
+{
+ return "DELETE";
+}
+
+//----------------------------------------------------------------------
+int DeleteOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult DeleteOperator::process(Depot &depot,
+ Request &command)
+{
+ if (depot.deleteMailbox(command.getMailbox()))
+ return OK;
+ else {
+ Session &session = Session::getInstance();
+ session.setLastError(depot.getLastError());
+ return NO;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult DeleteOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after DELETE");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after DELETE SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after DELETE SPACE mailbox");
+ return res;
+ }
+
+ session.mailboxchanges = true;
+
+ c_in.setName("DELETE");
+ c_in.setMailbox(mailbox);
+ return ACCEPT;
+}
diff --git a/src/operator-examine.cc b/src/operator-examine.cc
new file mode 100644
index 0000000..314641c
--- /dev/null
+++ b/src/operator-examine.cc
@@ -0,0 +1,26 @@
+/** --------------------------------------------------------------------
+ * @file operator-examine.cc
+ * @brief Implementation of the EXAMINE command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+const std::string ExamineOperator::getName(void) const
+{
+ return "EXAMINE";
+}
+
+//----------------------------------------------------------------------
+ExamineOperator::ExamineOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+ExamineOperator::~ExamineOperator(void)
+{
+}
diff --git a/src/operator-expunge.cc b/src/operator-expunge.cc
new file mode 100644
index 0000000..24904d5
--- /dev/null
+++ b/src/operator-expunge.cc
@@ -0,0 +1,73 @@
+/** --------------------------------------------------------------------
+ * @file operator-expunge.cc
+ * @brief Implementation of the EXPUNGE command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "imapparser.h"
+#include "recursivedescent.h"
+#include "pendingupdates.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+ExpungeOperator::ExpungeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+ExpungeOperator::~ExpungeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string ExpungeOperator::getName(void) const
+{
+ return "EXPUNGE";
+}
+
+//----------------------------------------------------------------------
+int ExpungeOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult ExpungeOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+ mailbox->expungeMailbox();
+
+ pendingUpdates(mailbox, PendingUpdates::EXPUNGE
+ | PendingUpdates::EXISTS
+ | PendingUpdates::RECENT
+ | PendingUpdates::FLAGS, true);
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult ExpungeOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setName("EXPUNGE");
+ return ACCEPT;
+}
diff --git a/src/operator-fetch.cc b/src/operator-fetch.cc
new file mode 100644
index 0000000..810afb5
--- /dev/null
+++ b/src/operator-fetch.cc
@@ -0,0 +1,641 @@
+/** --------------------------------------------------------------------
+ * @file operator-fetch.cc
+ * @brief Implementation of the FETCH command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "imapparser.h"
+#include "pendingupdates.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+namespace {
+ void outputFlags(const Message & message)
+ {
+ bincClient << "FLAGS ";
+
+ bincClient << "(";
+ int flags = message.getStdFlags();
+ vector<string> flagv;
+ if (flags & Message::F_SEEN) flagv.push_back("\\Seen");
+ if (flags & Message::F_ANSWERED) flagv.push_back("\\Answered");
+ if (flags & Message::F_DELETED) flagv.push_back("\\Deleted");
+ if (flags & Message::F_DRAFT) flagv.push_back("\\Draft");
+ if (flags & Message::F_RECENT) flagv.push_back("\\Recent");
+ if (flags & Message::F_FLAGGED) flagv.push_back("\\Flagged");
+
+ for (vector<string>::const_iterator k
+ = flagv.begin(); k != flagv.end(); ++k) {
+ if (k != flagv.begin()) bincClient << " ";
+ bincClient << *k;
+ }
+
+ vector<string> customFlags = message.getCustomFlags();
+ for (vector<string>::const_iterator it = customFlags.begin();
+ it != customFlags.end(); ++it) {
+ if (flagv.size() > 0 || it != customFlags.begin()) bincClient << " ";
+ bincClient << *it;
+ }
+
+ bincClient << ")";
+ }
+
+}
+
+//----------------------------------------------------------------------
+FetchOperator::FetchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+FetchOperator::~FetchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string FetchOperator::getName(void) const
+{
+ return "FETCH";
+}
+
+//----------------------------------------------------------------------
+int FetchOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult FetchOperator::process(Depot &depot,
+ Request &request)
+{
+ Session &session = Session::getInstance();
+
+ bool updateFlags = false;
+ Request req = request;
+
+ Mailbox *mailbox = depot.getSelected();
+
+ // If this is a UID FETCH, check if the UID attribute is fetched. If
+ // it is not, then add it to the list of fetch attributes.
+ vector<BincImapParserFetchAtt>::const_iterator f_i;
+ bool uidfetched = false;
+ if (request.getUidMode()) {
+ f_i = request.fatt.begin();
+ while (f_i != request.fatt.end()) {
+ if ((*f_i).type == "UID") {
+ uidfetched = true;
+ break;
+ }
+ f_i++;
+ }
+
+ if (!uidfetched) {
+ BincImapParserFetchAtt b;
+ b.type = "UID";
+ req.fatt.push_back(b);
+ }
+ }
+
+ // Convert macros ALL, FULL and FAST
+ f_i = request.fatt.begin();
+ while (f_i != request.fatt.end()) {
+ const string &type = (*f_i).type;
+ if (type == "ALL") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE"));
+ } else if (type == "FULL") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE"));
+ req.fatt.push_back(BincImapParserFetchAtt("BODY"));
+ } else if (type == "FAST") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ }
+
+ ++f_i;
+ }
+
+ int mode;
+ if (req.getUidMode())
+ mode = Mailbox::UID_MODE;
+ else
+ mode = Mailbox::SQNR_MODE;
+
+ Mailbox::iterator i
+ = mailbox->begin(req.bset, Mailbox::SKIP_EXPUNGED | mode);
+
+ for (; i != mailbox->end(); ++i) {
+ Message &message = *i;
+
+ bincClient << "* " << i.getSqnr() << " FETCH (";
+ bool hasprinted = false;
+ f_i = req.fatt.begin();
+ while (f_i != req.fatt.end()) {
+ BincImapParserFetchAtt fatt = *f_i;
+
+ string prefix = "";
+ if (hasprinted) prefix = " ";
+
+ if (fatt.type == "FLAGS") {
+ // FLAGS
+ hasprinted = true;
+ bincClient << prefix;
+
+ outputFlags(message);
+ } else if (fatt.type == "UID") {
+ // UID
+ hasprinted = true;
+ bincClient << prefix << "UID " << message.getUID();
+ } else if (fatt.type == "RFC822.SIZE") {
+ // RFC822.SIZE
+ hasprinted = true;
+ bincClient << prefix << "RFC822.SIZE " << message.getSize(true);
+ } else if (fatt.type == "ENVELOPE") {
+ // ENVELOPE
+ hasprinted = true;
+ bincClient << prefix << "ENVELOPE ";
+ message.printEnvelope();
+ } else if (fatt.type == "BODYSTRUCTURE") {
+ // BODYSTRUCTURE
+ hasprinted = true;
+ bincClient << prefix << "BODYSTRUCTURE ";
+ message.printBodyStructure(true);
+ } else if (fatt.type == "BODY" && !fatt.hassection) {
+ // BODY with no section
+ hasprinted = true;
+ session.addBody();
+ bincClient << prefix << "BODY ";
+ message.printBodyStructure(false);
+ } else if (fatt.type == "INTERNALDATE") {
+ // INTERNALDATE
+ hasprinted = true;
+ bincClient << prefix << "INTERNALDATE ";
+
+ time_t iDate = message.getInternalDate();
+ struct tm *_tm = gmtime(&iDate);
+ char internal[64];
+ string iDateStr;
+ if (strftime(internal, sizeof(internal),
+ "%d-%b-%Y %H:%M:%S %z", _tm) != 0) {
+ if (internal[0] == '0') internal[0] = ' ';
+ iDateStr = internal;
+ } else
+ iDateStr = "NIL";
+
+ bincClient << toImapString(iDateStr);
+ } else if (fatt.type == "BODY" || fatt.type == "BODY.PEEK") {
+ // BODY & BODY.PEEK
+ hasprinted = true;
+ session.addBody();
+
+ bincClient << prefix;
+ bool peek = (fatt.type == "BODY.PEEK");
+ bincClient << fatt.toString();
+
+ bool includeheaders = true;
+ bool fullheader = false;
+ bool bodyfetch = false;
+
+ if (fatt.section != "" || fatt.sectiontext == ""
+ || fatt.sectiontext == "TEXT") {
+ bodyfetch = true;
+ fullheader = true;
+ }
+
+ if (fatt.sectiontext == "HEADER.FIELDS.NOT")
+ includeheaders = false;
+
+ if (fatt.sectiontext == "HEADER"
+ || fatt.sectiontext == "HEADER.FIELDS"
+ || fatt.sectiontext == "HEADER.FIELDS.NOT"
+ || fatt.sectiontext == "MIME") {
+ vector<string> v;
+
+ if (fatt.sectiontext == "MIME") {
+ v.push_back("content-type");
+ v.push_back("content-transfer-encoding");
+ v.push_back("content-disposition");
+ v.push_back("content-description");
+ } else
+ v = fatt.headerlist;
+
+ string dummy;
+ unsigned int size = fullheader
+ ? message.getHeaderSize(fatt.section, v, true,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME")
+ : message.getHeaderSize(fatt.section, fatt.headerlist,
+ includeheaders,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+
+ bincClient << "{" << size << "}\r\n";
+
+ if (fullheader) {
+ message.printHeader(fatt.section, v, true,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+ } else {
+ message.printHeader(fatt.section, fatt.headerlist,
+ includeheaders,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+ }
+ } else {
+ unsigned int size;
+ if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT")
+ && fatt.section == "")
+ size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "TEXT");
+ else
+ size = message.getBodySize(fatt.section,
+ fatt.offsetstart,
+ fatt.offsetlength);
+
+ bincClient << "{" << size << "}\r\n";
+
+ if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT")
+ && fatt.section == "")
+ message.printDoc(fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "TEXT");
+ else
+ message.printBody(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+ }
+
+ // set the \Seen flag if .PEEK is not used.
+ if (!peek)
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+ } else if (fatt.type == "RFC822") {
+ bincClient << prefix;
+ hasprinted = true;
+ session.addBody();
+ bincClient << fatt.toString();
+ unsigned int size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength);
+ bincClient << " {" << size << "}\r\n";
+ message.printDoc(fatt.offsetstart, fatt.offsetlength);
+
+ // set the \Seen flag
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+
+ } else if (fatt.type == "RFC822.HEADER") {
+ bincClient << prefix;
+ hasprinted = true;
+ bincClient << fatt.toString();
+ vector<string> v;
+ string dummy;
+ unsigned int size = message.getHeaderSize("", v, true,
+ fatt.offsetstart,
+ fatt.offsetlength);
+ bincClient << " {" << size << "}\r\n";
+ message.printHeader("", v, true, fatt.offsetstart,
+ fatt.offsetlength);
+ } else if (fatt.type == "RFC822.TEXT") {
+ // RFC822.TEXT
+ bincClient << prefix;
+ hasprinted = true;
+ session.addBody();
+
+ bincClient << fatt.toString();
+
+ bool bodyfetch = false;
+ bodyfetch = true;
+
+ unsigned int size;
+ if (fatt.sectiontext == "" && fatt.section == "")
+ size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength, true);
+ else
+ size = message.getBodySize(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+
+ bincClient << " {" << size << "}\r\n";
+
+ if (fatt.sectiontext == "" && fatt.section == "")
+ message.printDoc(fatt.offsetstart,
+ fatt.offsetlength, true);
+ else
+ message.printBody(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+
+ // set the \Seen flag
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+
+ } else {
+ // Unrecognized fetch_att, this is stopped by the parser
+ // so we never get here.
+ }
+
+ f_i++;
+ }
+
+ // FIXME: how are parse error passed back?
+
+ bincClient << ")" << endl;
+
+ if (message.hasFlagsChanged()) {
+ updateFlags = true;
+ bincClient << "* " << i.getSqnr() << " FETCH (";
+ outputFlags(message);
+ bincClient << ")" << endl;
+ message.setFlagsUnchanged();
+ }
+ }
+
+ if (updateFlags) mailbox->updateFlags();
+
+ pendingUpdates(mailbox,
+ PendingUpdates::FLAGS
+ | PendingUpdates::EXISTS
+ | PendingUpdates::EXPUNGE
+ | PendingUpdates::RECENT, true);
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult FetchOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after FETCH");
+ return res;
+ }
+
+ if ((res = expectSet(c_in.getSet())) != ACCEPT) {
+ session.setLastError("Expected sequence set after FETCH SPACE");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after FETCH SPACE set");
+ return res;
+ }
+
+ BincImapParserFetchAtt f;
+
+ if ((res = expectThisString("ALL")) == ACCEPT) {
+ f.type = "ALL";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("FULL")) == ACCEPT) {
+ f.type = "FULL";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("FAST")) == ACCEPT) {
+ f.type = "FAST";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectFetchAtt(f)) == ACCEPT) {
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("(")) == ACCEPT) {
+ while (1) {
+ BincImapParserFetchAtt ftmp;
+ if ((res = expectFetchAtt(ftmp)) != ACCEPT) {
+ session.setLastError("Expected fetch_att");
+ return res;
+ }
+
+ c_in.fatt.push_back(ftmp);
+
+ if ((res = expectSPACE()) == REJECT) break;
+ else if (res == ERROR) return ERROR;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+ } else {
+ session.setLastError("Expected ALL, FULL, FAST, fetch_att or (");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setName("FETCH");
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectSectionText(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("HEADER")) == ACCEPT) {
+ f_in.sectiontext = "HEADER";
+
+ if ((res = expectThisString(".FIELDS")) == ACCEPT) {
+ f_in.sectiontext += ".FIELDS";
+
+ if ((res = expectThisString(".NOT")) == ACCEPT)
+ f_in.sectiontext += ".NOT";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("expected SPACE");
+ return res;
+ }
+
+ if ((res = expectHeaderList(f_in)) != ACCEPT) {
+ session.setLastError("Expected header_list");
+ return res;
+ }
+ }
+ } else if ((res = expectThisString("TEXT")) == ACCEPT)
+ f_in.sectiontext = "TEXT";
+ else
+ return REJECT;
+
+ return ACCEPT;
+
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectSection(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("[")) != ACCEPT)
+ return REJECT;
+
+ if ((res = expectSectionText(f_in)) != ACCEPT) {
+ unsigned int n;
+ if ((res = expectNZNumber(n)) == ACCEPT) {
+ BincStream nstr;
+ nstr << n;
+ f_in.section = nstr.str();
+
+ bool gotadotalready = false;
+ while (1) {
+ if ((res = expectThisString(".")) != ACCEPT) break;
+
+ if ((res = expectNZNumber(n)) != ACCEPT) {
+ gotadotalready = true;
+ break;
+ }
+
+ f_in.section += ".";
+ BincStream nstr;
+ nstr << n;
+ f_in.section += nstr.str();
+ }
+
+ if (gotadotalready || (res = expectThisString(".")) == ACCEPT) {
+ if ((res = expectThisString("MIME")) == ACCEPT) {
+ f_in.sectiontext = "MIME";
+ } else if ((res = expectSectionText(f_in)) != ACCEPT) {
+ session.setLastError("Expected MIME or section_text");
+ return res;
+ }
+ }
+ }
+ }
+
+ if ((res = expectThisString("]")) != ACCEPT) {
+ session.setLastError("Expected ]");
+ return res;
+ }
+
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectHeaderList(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("(")) != ACCEPT)
+ return REJECT;
+
+ string header_fld_name;
+ while (1) {
+ if ((res = expectAstring(header_fld_name)) != ACCEPT) {
+ session.setLastError("Expected header_fld_name");
+ return res;
+ }
+
+ f_in.headerlist.push_back(header_fld_name);
+
+ if ((res = expectSPACE()) == ACCEPT) continue;
+ else break;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectOffset(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+ Operator::ParseResult res;
+
+ if ((res = expectThisString("<")) != ACCEPT) return REJECT;
+
+ unsigned int i;
+ if ((res = expectNumber(i)) != ACCEPT) {
+ session.setLastError("Expected number");
+ return res;
+ }
+
+ if ((res = expectThisString(".")) != ACCEPT) {
+ session.setLastError("Expected .");
+ return res;
+ }
+
+ unsigned int j;
+ if ((res = expectNZNumber(j)) != ACCEPT) {
+ session.setLastError("expected nz_number");
+ return res;
+ }
+
+ if ((res = expectThisString(">")) != ACCEPT) {
+ session.setLastError("Expected >");
+ return res;
+ }
+
+ f_in.offsetstart = i;
+ f_in.offsetlength = j;
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectFetchAtt(BincImapParserFetchAtt &f_in) const
+{
+ Operator::ParseResult res;
+
+ Session &session = Session::getInstance();
+
+ if ((res = expectThisString("ENVELOPE")) == ACCEPT) f_in.type = "ENVELOPE";
+ else if ((res = expectThisString("FLAGS")) == ACCEPT) f_in.type = "FLAGS";
+ else if ((res = expectThisString("INTERNALDATE")) == ACCEPT)
+ f_in.type = "INTERNALDATE";
+ else if ((res = expectThisString("UID")) == ACCEPT) f_in.type = "UID";
+ else if ((res = expectThisString("RFC822")) == ACCEPT) {
+ f_in.type = "RFC822";
+ if ((res = expectThisString(".HEADER")) == ACCEPT) f_in.type += ".HEADER";
+ else if ((res = expectThisString(".SIZE")) == ACCEPT) f_in.type += ".SIZE";
+ else if ((res = expectThisString(".TEXT")) == ACCEPT) f_in.type += ".TEXT";
+ else if ((res = expectThisString(".")) == ACCEPT) {
+ session.setLastError("Expected RFC822, RFC822.HEADER,"
+ " RFC822.SIZE or RFC822.TEXT");
+ return ERROR;
+ }
+
+ } else if ((res = expectThisString("BODY")) == ACCEPT) {
+ f_in.type = "BODY";
+
+ if ((res = expectThisString("STRUCTURE")) == ACCEPT) f_in.type += "STRUCTURE";
+ else if ((res = expectThisString(".PEEK")) == ACCEPT) f_in.type += ".PEEK";
+
+ if ((res = expectSection(f_in)) != ACCEPT)
+ f_in.hassection = false;
+ else {
+ f_in.hassection = true;
+ if ((res = expectOffset(f_in)) == ERROR) return ERROR;
+ }
+ } else
+ return REJECT;
+
+ return ACCEPT;
+}
diff --git a/src/operator-id.cc b/src/operator-id.cc
new file mode 100644
index 0000000..842c0a3
--- /dev/null
+++ b/src/operator-id.cc
@@ -0,0 +1,71 @@
+/** --------------------------------------------------------------------
+ * @file operator-id.cc
+ * @brief Operator for the ID extension. Described in RFC2971 Oct 2000.
+ * @author Erwin Hoffmann
+ * @date 22.09.2023
+ * ------------------------------------------------------------------ **/
+#include <string>
+#include <iostream>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "globals.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+IdOperator::IdOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+IdOperator::~IdOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string IdOperator::getName(void) const
+{
+ return "ID";
+}
+
+//----------------------------------------------------------------------
+int IdOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult IdOperator::process(Depot &depot,
+ Request &command)
+{
+ bincClient << "* ID (\"name\" \"Binc IMAP\""
+ << " \"version\" \"" << BINC_VERSION "\")" << endl;
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult IdOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT && (res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected SPACE or CRLF");
+ return res;
+ }
+
+ c_in.setName("ID");
+
+ return ACCEPT;
+}
diff --git a/src/operator-id.o b/src/operator-id.o
new file mode 100644
index 0000000..ff93eae
--- /dev/null
+++ b/src/operator-id.o
Binary files differ
diff --git a/src/operator-idle.cc b/src/operator-idle.cc
new file mode 100644
index 0000000..ccd70ea
--- /dev/null
+++ b/src/operator-idle.cc
@@ -0,0 +1,239 @@
+/** --------------------------------------------------------------------
+ * @file operator-idle.cc
+ * @brief Operator for the IDLE command. Described in RFC2177 / June 1997.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <unistd.h>
+
+#include <string>
+#include <iostream>
+
+#include "iodevice.h"
+#include "iofactory.h"
+#include "convert.h"
+#include "depot.h"
+#include "globals.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "pendingupdates.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+static bool directoryChangeNotification = false;
+
+#ifdef HAVE_FNOTIFY // GNU dependencies removed
+#include <sys/types.h>
+#include <sys/select.h>
+#include <stdio.h>
+#include <signal.h>
+#include <fcntl.h>
+
+void fnotifyEventHandler(int sig)
+{
+ directoryChangeNotification = true;
+}
+#endif
+
+using namespace ::std;
+using namespace Binc;
+
+// Seconds between each poll. With FNOTIFY support, we can idle for 30
+// minutes before timing out.
+#ifdef HAVE_FNOTIFY
+static const int POLLTIMEOUT = 30 * 60;
+#else
+static const int POLLTIMEOUT = 30;
+#endif
+
+//----------------------------------------------------------------------
+IdleOperator::IdleOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+IdleOperator::~IdleOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string IdleOperator::getName(void) const
+{
+ return "IDLE";
+}
+
+//----------------------------------------------------------------------
+int IdleOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult IdleOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+ string mailboxDir = depot.mailboxToFilename(mailbox->getName());
+
+#ifdef HAVE_FNOTIFY
+ // Check for FNOTIFY support.
+ bool waitForNotification = false;
+ int newfd = open((mailboxDir + "/new").c_str(), O_RDONLY);
+ int curfd = open((mailboxDir + "/cur").c_str(), O_RDONLY);
+
+ // Watch for notifications for renames, deletes or creates.
+ if (newfd && curfd
+ && !fcntl(newfd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_CREATE)
+ && !fcntl(curfd, F_NOTIFY, DN_RENAME|DN_DELETE|DN_CREATE)) {
+ struct sigaction fnotifyAction;
+ fnotifyAction.sa_handler = fnotifyEventHandler;
+ sigemptyset(&fnotifyAction.sa_mask);
+ fnotifyAction.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &fnotifyAction, 0);
+ fcntl(newfd, F_SETSIG, SIGUSR1);
+ fcntl(curfd, F_SETSIG, SIGUSR1);
+ waitForNotification = true;
+ }
+#endif
+
+ // when not using FNOTIFY, we need to check the session timeout.
+ time_t startTime = time(0);
+#ifdef HAVE_FNOTIFY
+ (void)startTime; // removes a compile warning
+#endif
+
+ bincClient << "+ idling" << endl;
+ bincClient.flush();
+
+ // loop until the session times out or the client submits DONE.
+ for (;;) {
+ int maxfd = 0;
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+
+ // check for data from stdin. with FNOTIFY enabled, this select
+ // will be interrupted by the notification signal.
+ string input;
+ struct timeval tv = {POLLTIMEOUT, 0};
+ int ret = select(maxfd + 1, &readfds, 0, 0, &tv);
+
+ // check if the select timed out.
+ if (ret == 0) {
+ Session &session = Session::getInstance();
+#ifdef HAVE_FNOTIFY
+ if (waitForNotification) {
+ bincClient << "* BYE Timeout after " << session.timeout()
+ << " seconds of inactivity." << endl;
+ session.setState(Session::LOGOUT);
+ close(newfd);
+ close(curfd);
+ return NOTHING;
+ } else
+#endif
+ if (time(0) > startTime + IDLE_TIMEOUT) {
+ bincClient << "* BYE Timeout after " << IDLE_TIMEOUT
+ << " seconds of inactivity." << endl;
+ session.setState(Session::LOGOUT);
+ return NOTHING;
+ }
+ }
+
+ // unless the select failed, attempt to read client input.
+ if (ret > 0 && FD_ISSET(0, &readfds)) {
+ if (bincClient.readStr(&input) == 0) {
+ break;
+ } else {
+ uppercase(input);
+ trim(input);
+ if (input == "DONE") {
+ break;
+ } else {
+ bincClient << "* BAD Syntax error: \"" << input << "\"" << endl;
+ bincClient.flush();
+ continue;
+ }
+ }
+ }
+
+ // at this point, we either got a directory change notification,
+ // or the select simply timed out, in which case we poll.
+ bool scanForChanges = false;
+#ifdef HAVE_FNOTIFY
+ if (directoryChangeNotification)
+ scanForChanges = true;
+ else if (!waitForNotification)
+#endif
+ scanForChanges = true;
+
+ if (scanForChanges) {
+ if (directoryChangeNotification) {
+ // sleep the magic 1 second to ensure that anything that
+ // arrived in new/ the last second isn't skipped by
+ // pendingUpdates' scan.
+ sleep(1);
+ }
+
+ // scan for changes in the mailbox and report to the client.
+ if (pendingUpdates(mailbox, PendingUpdates::EXPUNGE
+ | PendingUpdates::EXISTS
+ | PendingUpdates::RECENT
+ | PendingUpdates::FLAGS, true) == false) {
+ Session &session = Session::getInstance();
+ bincClient << "* NO " << session.getLastError() << endl;
+ bincWarning << "when scanning mailbox: "
+ << session.getLastError() << endl;
+
+#ifdef HAVE_FNOTIFY
+ close(newfd);
+ close(curfd);
+#endif
+ return NO;
+ }
+
+#ifdef HAVE_FNOTIFY
+ // if FNOTIFY is enabled, set it up again.
+ if (waitForNotification) {
+ directoryChangeNotification = false;
+
+ // set up F_NOTIFY again.
+ if (!fcntl(newfd, F_NOTIFY, DN_MODIFY|DN_RENAME|DN_DELETE|DN_CREATE)
+ && !fcntl(curfd, F_NOTIFY, DN_MODIFY|DN_RENAME|DN_DELETE|DN_CREATE)) {
+ struct sigaction fnotifyAction;
+ fnotifyAction.sa_handler = fnotifyEventHandler;
+ sigemptyset(&fnotifyAction.sa_mask);
+ fnotifyAction.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &fnotifyAction, 0);
+ } else {
+ waitForNotification = false;
+ }
+ }
+#endif
+ bincClient.flush();
+ }
+ }
+
+#ifdef HAVE_FNOTIFY
+ close(newfd);
+ close(curfd);
+#endif
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult IdleOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after IDLE");
+ return res;
+ }
+
+ c_in.setName("IDLE");
+ return ACCEPT;
+}
diff --git a/src/operator-list.cc b/src/operator-list.cc
new file mode 100644
index 0000000..baa8107
--- /dev/null
+++ b/src/operator-list.cc
@@ -0,0 +1,252 @@
+/** --------------------------------------------------------------------
+ * @file operator-list.cc
+ * @brief Implementation of the LIST command.
+ * ----------------------------------------------------------------- **/
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <string>
+#include <iostream>
+
+#include "convert.h"
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "regmatch.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+namespace {
+ const time_t LIST_CACHE_TIMEOUT = 10;
+}
+
+//----------------------------------------------------------------------
+ListOperator::ListOperator(void)
+{
+ cacheTimeout = 0;
+}
+
+//----------------------------------------------------------------------
+ListOperator::~ListOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string ListOperator::getName(void) const
+{
+ return "LIST";
+}
+
+//----------------------------------------------------------------------
+int ListOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult ListOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+ const char delim = depot.getDelimiter();
+
+ // special case: if the mailbox argument is empty, then give a
+ // hard coded reply.
+ string wildcard;
+ if ((wildcard = command.getListMailbox()) == "") {
+ bincClient << "* LIST (\\Noselect) \"" << delim << "\" \"\"" << endl;
+ return OK;
+ }
+
+ // remove leading or trailing delimiter in wildcard
+ trim(wildcard, string(&delim, 1));
+
+ // convert wildcard to regular expression
+ string regex = toRegex(wildcard, depot.getDelimiter());
+ string wildcardLower = regex;
+ lowercase(wildcardLower);
+ if (wildcardLower.substr(0, 6) == "^inbox")
+ regex = "^[iI][nN][bB][oO][xX]" + regex.substr(6);
+
+ // remove leading or trailing delimiter in reference
+ string ref = command.getMailbox();
+ trim(ref, string(&delim, 1));
+ wildcardLower = ref;
+ lowercase(wildcardLower);
+ if (wildcardLower.substr(0, 6) == "^inbox")
+ ref = "^[iI][nN][bB][oO][xX]" + ref.substr(6);
+ if (wildcardLower.substr(0, 5) == "inbox"
+ && (wildcardLower.length() == 5 || wildcardLower[5] == delim))
+ ref = "INBOX" + ref.substr(5);
+
+ // a map from mailbox name to flags
+ map<string, unsigned int> mailboxes;
+
+ if (cacheTimeout == 0 || cacheTimeout < time(0) - LIST_CACHE_TIMEOUT
+ || session.mailboxchanges) {
+ session.mailboxchanges = false;
+
+ // read through all entries in depository.
+ for (Depot::iterator i = depot.begin("."); i != depot.end(); ++i) {
+ const string path = *i;
+ const string mpath = depot.filenameToMailbox(path);
+ Mailbox *m = 0;
+
+ // skip entries that are not identified as mailboxes
+ if ((m = depot.get(mpath)) == 0)
+ continue;
+
+ // convert file name to mailbox name. skip it if there is no
+ // corresponding mailbox name.
+ string tmp = toCanonMailbox(depot.filenameToMailbox(path));
+ trim(tmp, string(&delim, 1));
+ if (tmp == "") continue;
+ else {
+ // inherit flags that were already set for this mailbox.
+ int flags = DIR_SELECT;
+ if (m->isMarked(path)) flags |= DIR_MARKED;
+ if (mailboxes.find(tmp) != mailboxes.end()) flags |= mailboxes[tmp];
+ mailboxes[tmp] = flags;
+ }
+
+ // now add all superior mailboxes with no flags set if not
+ // added already.
+ string::size_type pos = tmp.rfind(delim);
+ while (pos != string::npos) {
+ tmp = tmp.substr(0, pos);
+ trim(tmp, string(&delim, 1));
+
+ if (mailboxes.find(tmp) == mailboxes.end())
+ mailboxes[tmp] = 0;
+
+ pos = tmp.rfind(delim);
+ }
+ }
+
+ // find leaf nodes O(N^2)
+ map<string, unsigned int>::iterator i;
+ for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
+ string mailbox = i->first;
+ mailbox += delim;
+
+ bool leaf = true;
+ map<string, unsigned int>::const_iterator j = mailboxes.begin();
+ for (; j != mailboxes.end(); ++j) {
+ string::size_type pos = j->first.rfind(delim);
+ if (pos == string::npos) continue;
+
+ string base = j->first.substr(0, pos + 1);
+
+ if (mailbox == base) {
+ leaf = false;
+ break;
+ }
+ }
+
+ if (leaf) {
+ unsigned int flags = i->second;
+ flags |= DIR_LEAF;
+ i->second = flags;
+ }
+ }
+
+ cache = mailboxes;
+ cacheTimeout = time(0);
+ } else {
+ mailboxes = cache;
+ cacheTimeout = time(0);
+ }
+
+ // finally, print all mailbox entries with flags.
+ map<string, unsigned int>::iterator i = mailboxes.begin();
+
+ for (; i != mailboxes.end(); ++i) {
+ if (ref == "" || (ref.length() <= i->first.length()
+ && ref == i->first.substr(0, ref.length())))
+ if (regexMatch(i->first.substr(ref.length()), regex) == 0) {
+ bincClient << "* LIST (";
+ string sep = "";
+
+ int flags = i->second;
+ bool noselect = false;
+
+ if (!(flags & DIR_SELECT)) {
+ bincClient << sep << "\\Noselect";
+ sep = " ";
+ noselect = true;
+ }
+
+ if (!noselect) {
+ if (flags & DIR_MARKED)
+ bincClient << sep << "\\Marked";
+ else
+ bincClient << sep << "\\Unmarked";
+ sep = " ";
+ }
+
+ if (flags & DIR_LEAF)
+ bincClient << sep << "\\HasNoChildren";
+ else
+ bincClient << sep << "\\HasChildren";
+ sep = " ";
+
+ if (flags & DIR_NOINFERIORS)
+ bincClient << sep << "\\Noinferiors";
+
+ bincClient << ") \"" << depot.getDelimiter() << "\" "
+ << toImapString(i->first) << endl;
+ }
+ }
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult ListOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after LIST");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after LIST SPACE");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after LIST SPACE mailbox");
+ return res;
+ }
+
+ string listmailbox;
+ if ((res = expectListMailbox(listmailbox)) != ACCEPT) {
+ session.setLastError("Expected list_mailbox after LIST SPACE"
+ " mailbox SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after LIST SPACE mailbox"
+ " SPACE list_mailbox");
+ return res;
+ }
+
+ c_in.setListMailbox(listmailbox);
+ c_in.setName("LIST");
+ return ACCEPT;
+}
diff --git a/src/operator-login.cc b/src/operator-login.cc
new file mode 100644
index 0000000..a0ecacd
--- /dev/null
+++ b/src/operator-login.cc
@@ -0,0 +1,134 @@
+/** --------------------------------------------------------------------
+ * @file operator-login.cc
+ * @brief Implementation of the rapid LOGIN command
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <string>
+#include <iostream>
+
+#include "authenticate.h"
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "globals.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+LoginOperator::LoginOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+LoginOperator::~LoginOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string LoginOperator::getName(void) const
+{
+ return "LOGIN";
+}
+
+//----------------------------------------------------------------------
+int LoginOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult LoginOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ if (!session.command.ssl
+ && !session.hasEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS")) {
+ session.setLastError("Plain text password authentication is disallowd. "
+ "Please enable StartTLS or TLS in your mail client.");
+ return NO;
+ }
+
+ session.setEnv("BINCIMAP_LOGIN", "LOGIN+" + command.getTag());
+ string challenge;
+
+ switch (authenticate(depot, command.getUserID(), command.getPassword(), challenge)) {
+ case 1:
+ session.setLastError("An internal error occurred when you attempted "
+ "to log in to the IMAP server. Please contact "
+ "your system administrator.");
+ return NO;
+ case 2:
+ session.setLastError("Login failed. Either your user name "
+ "or your password was wrong. Please try again, "
+ "and if the problem persists, please contact "
+ "your system administrator.");
+ return NO;
+ case 3:
+ bincClient << "* BYE Timeout after " << IDLE_TIMEOUT
+ << " seconds of inactivity." << endl;
+ break;
+ case -1:
+ bincClient << "* BYE The server died unexpectedly. Please contact "
+ "your system administrator for more information." << endl;
+ break;
+ }
+
+ // go to logout
+ session.setState(Session::LOGOUT);
+
+ return NOTHING;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult LoginOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected single SPACE after LOGIN");
+ return res;
+ }
+
+ string userid;
+ if ((res = expectAstring(userid)) != ACCEPT) {
+ session.setLastError("Expected userid after LOGIN SPACE");
+ return res;
+ }
+
+ c_in.setUserID(userid);
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after LOGIN SPACE userid");
+ return res;
+ }
+
+ string password;
+ if ((res = expectAstring(password)) != ACCEPT) {
+ session.setLastError("Expected password after LOGIN "
+ "SPACE userid SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after password");
+ return res;
+ }
+
+ c_in.setPassword(password);
+ c_in.setName("LOGIN");
+
+ return ACCEPT;
+}
diff --git a/src/operator-logout.cc b/src/operator-logout.cc
new file mode 100644
index 0000000..643d412
--- /dev/null
+++ b/src/operator-logout.cc
@@ -0,0 +1,86 @@
+/** --------------------------------------------------------------------
+ * @file operator-logout.cc
+ * @brief Implementation of the LOGOUT command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+
+#include "iodevice.h"
+#include "iofactory.h"
+
+#include "mailbox.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+LogoutOperator::LogoutOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+LogoutOperator::~LogoutOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string LogoutOperator::getName(void) const
+{
+ return "LOGOUT";
+}
+
+//----------------------------------------------------------------------
+int LogoutOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult LogoutOperator::process(Depot &depot,
+ Request &command)
+{
+ bincClient << "* BYE Binc IMAP shutting down" << endl;
+ bincClient << command.getTag() << " OK LOGOUT completed" << endl;
+ bincClient.flush();
+
+#ifdef BINCIMAPD
+ Mailbox *mailbox = 0;
+ if ((mailbox = depot.getSelected()) != 0) {
+ mailbox->closeMailbox();
+ delete mailbox;
+ }
+#endif
+
+ Session &session = Session::getInstance();
+ session.setState(Session::LOGOUT);
+
+ return NOTHING;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult LogoutOperator::parse(Request & c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setName("LOGOUT");
+ return ACCEPT;
+}
diff --git a/src/operator-lsub.cc b/src/operator-lsub.cc
new file mode 100644
index 0000000..f776503
--- /dev/null
+++ b/src/operator-lsub.cc
@@ -0,0 +1,238 @@
+/** --------------------------------------------------------------------
+ * @file operator-lsub.cc
+ * @brief Implementation of the LSUB command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "convert.h"
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "regmatch.h"
+#include "session.h"
+
+namespace {
+ const int DIR_SELECT = 0x01;
+ const int DIR_MARKED = 0x02;
+ const int DIR_NOINFERIORS = 0x04;
+ const int DIR_LEAF = 0x08;
+}
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+LsubOperator::LsubOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+LsubOperator::~LsubOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string LsubOperator::getName(void) const
+{
+ return "LSUB";
+}
+
+//----------------------------------------------------------------------
+int LsubOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult LsubOperator::process(Depot &depot,
+ Request &command)
+{
+ const char delim = depot.getDelimiter();
+
+ // remove leading or trailing delimiter in wildcard
+ string wildcard = command.getListMailbox();
+ trim(wildcard, string(&delim, 1));
+
+ // convert wildcard to regular expression
+ string regex = toRegex(wildcard, depot.getDelimiter());
+ string wildcardLower = regex;
+ lowercase(wildcardLower);
+ if (wildcardLower.substr(0, 6) == "^inbox")
+ regex = "^[iI][nN][bB][oO][xX]" + regex.substr(6);
+
+ // remove leading or trailing delimiter in reference
+ string ref = command.getMailbox();
+ trim(ref, string(&delim, 1));
+ wildcardLower = ref;
+ lowercase(wildcardLower);
+ if (wildcardLower.substr(0, 5) == "inbox"
+ && (wildcardLower.length() == 5 || wildcardLower[5] == delim))
+ ref = "INBOX" + ref.substr(5);
+
+ // a multimap from mailbox name to flags
+ multimap<string, int> mailboxes;
+
+ // read through all entries in depository.
+ for (Depot::iterator i = depot.begin("."); i != depot.end(); ++i) {
+ const string path = *i;
+ const string mpath = depot.filenameToMailbox(path);
+ Mailbox *m = 0;
+
+ // skip entries that are not identified as mailboxes
+ if ((m = depot.get(mpath)) == 0)
+ continue;
+
+ // convert file name to mailbox name. skip it if there is no
+ // corresponding mailbox name.
+ string tmp = toCanonMailbox(depot.filenameToMailbox(path));
+ trim(tmp, string(&delim, 1));
+ if (tmp == "")
+ continue;
+ else {
+ int flags = DIR_SELECT;
+ multimap<string, int>::iterator mi = mailboxes.find(tmp);
+ if (mi != mailboxes.end()) {
+ flags |= mi->second;
+ mailboxes.erase(mi);
+ }
+ mailboxes.insert(make_pair(tmp, flags));
+ }
+
+ // now add all superior mailboxes with no flags set if not
+ // added already.
+ string::size_type pos = tmp.rfind(delim);
+ while (pos != string::npos) {
+ tmp = tmp.substr(0, pos);
+ trim(tmp, string(&delim, 1));
+
+ multimap<string, int>::iterator mi = mailboxes.find(tmp);
+ if (mi == mailboxes.end()) mailboxes.insert(make_pair(tmp, 0));
+
+ pos = tmp.rfind(delim);
+ }
+ }
+
+ // find leaf nodes O(N^2)
+ multimap<string, int>::iterator i;
+ for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
+ string mailbox = i->first;
+ mailbox += delim;
+
+ bool leaf = true;
+ multimap<string, int>::const_iterator j;
+ for (j = mailboxes.begin(); j != mailboxes.end(); ++j) {
+ string::size_type pos = j->first.rfind(delim);
+ if (pos == string::npos) continue;
+ string base = j->first.substr(0, pos + 1);
+ if (mailbox == base) {
+ leaf = false;
+ break;
+ }
+ }
+ if (leaf) {
+ unsigned int flags = i->second;
+ flags |= DIR_LEAF;
+ i->second = flags;
+ }
+ }
+
+ depot.loadSubscribes();
+
+ vector<string> subscribed = depot.getSubscriptions();
+ sort(subscribed.begin(), subscribed.end());
+
+ // finally, print all mailbox entries with flags.
+ for (vector<string>::const_iterator j = subscribed.begin();
+ j != subscribed.end(); ++j) {
+ if (ref == "" || (ref.length() <= (*j).length() && ref == (*j).substr(0, ref.length())))
+ if (regexMatch((*j).substr(ref.length()), regex) == 0) {
+ int flags = 0;
+ for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
+ if (i->first == *j) {
+ flags = i->second;
+ break;
+ }
+ }
+ bincClient << "* LSUB (";
+ string sep = "";
+ bool noselect = false;
+ if (!(flags & DIR_SELECT)) {
+ bincClient << sep << "\\Noselect";
+ sep = " ";
+ noselect = true;
+ }
+ if (!noselect) {
+ if (flags & DIR_MARKED)
+ bincClient << sep << "\\Marked";
+ else
+ bincClient << sep << "\\Unmarked";
+ sep = " ";
+ }
+ if (flags & DIR_LEAF)
+ bincClient << sep << "\\HasNoChildren";
+ else
+ bincClient << sep << "\\HasChildren";
+ sep = " ";
+ if (flags & DIR_NOINFERIORS)
+ bincClient << sep << "\\Noinferiors";
+ bincClient << ") \"" << depot.getDelimiter() << "\" "
+ << toImapString(*j) << endl;
+ }
+ }
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult LsubOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after LSUB");
+ return ERROR;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after LSUB SPACE");
+ return ERROR;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after LSUB SPACE mailbox");
+ return ERROR;
+ }
+
+ string listmailbox;
+ if ((res = expectListMailbox(listmailbox)) != ACCEPT) {
+ session.setLastError("Expected list_mailbox after LSUB SPACE"
+ " mailbox SPACE");
+ return ERROR;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after LSUB SPACE"
+ " mailbox SPACE list_mailbox");
+ return ERROR;
+ }
+
+ c_in.setListMailbox(listmailbox);
+ c_in.setName("LSUB");
+
+ return ACCEPT;
+}
diff --git a/src/operator-namespace.cc b/src/operator-namespace.cc
new file mode 100644
index 0000000..8574f91
--- /dev/null
+++ b/src/operator-namespace.cc
@@ -0,0 +1,79 @@
+/** --------------------------------------------------------------------
+ * @file operator-namespace.cc
+ * @brief Operator for the NAMESPACE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+NamespaceOperator::NamespaceOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+NamespaceOperator::~NamespaceOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string NamespaceOperator::getName(void) const
+{
+ return "NAMESPACE";
+}
+
+//----------------------------------------------------------------------
+int NamespaceOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult NamespaceOperator::process(Depot &depot,
+ Request &command)
+{
+ bincClient << "* NAMESPACE ";
+
+ bincClient << "(("; // personal namespace
+ bincClient << toImapString(depot.getPersonalNamespace());
+ bincClient << " ";
+ char c = depot.getDelimiter();
+ bincClient << toImapString(string(&c, 1));
+ bincClient << "))";
+
+ bincClient << " NIL"; // others' namespaces
+ bincClient << " NIL"; // shared namespaces
+ bincClient << endl;
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult NamespaceOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after NAMESPACE");
+ return res;
+ }
+
+ c_in.setName("NAMESPACE");
+
+ return ACCEPT;
+}
diff --git a/src/operator-noop-pending.cc b/src/operator-noop-pending.cc
new file mode 100644
index 0000000..f98405e
--- /dev/null
+++ b/src/operator-noop-pending.cc
@@ -0,0 +1,44 @@
+/** --------------------------------------------------------------------
+ * @file operator-noop-pending.cc
+ * @brief Operator for the NOOP command, with pending extension
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+
+#include "mailbox.h"
+#include "pendingupdates.h"
+
+#include "recursivedescent.h"
+#include "session.h"
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+NoopPendingOperator::NoopPendingOperator(void) : NoopOperator()
+{
+}
+
+//----------------------------------------------------------------------
+NoopPendingOperator::~NoopPendingOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult NoopPendingOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+ if (mailbox) {
+ pendingUpdates(mailbox, PendingUpdates::EXPUNGE
+ | PendingUpdates::EXISTS
+ | PendingUpdates::RECENT
+ | PendingUpdates::FLAGS, true);
+ }
+
+ return OK;
+}
diff --git a/src/operator-noop.cc b/src/operator-noop.cc
new file mode 100644
index 0000000..d267513
--- /dev/null
+++ b/src/operator-noop.cc
@@ -0,0 +1,65 @@
+/** --------------------------------------------------------------------
+ * @file operator-noop.cc
+ * @brief Operator for the NOOP command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <string>
+#include <iostream>
+
+#include "recursivedescent.h"
+#include "session.h"
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+NoopOperator::NoopOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+NoopOperator::~NoopOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string NoopOperator::getName(void) const
+{
+ return "NOOP";
+}
+
+//----------------------------------------------------------------------
+int NoopOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+Operator::ProcessResult NoopOperator::process(Depot &depot,
+ Request &command)
+{
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult NoopOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after NOOP");
+ return res;
+ }
+
+ c_in.setName("NOOP");
+
+ return ACCEPT;
+}
diff --git a/src/operator-rename.cc b/src/operator-rename.cc
new file mode 100644
index 0000000..cc11d14
--- /dev/null
+++ b/src/operator-rename.cc
@@ -0,0 +1,119 @@
+/** --------------------------------------------------------------------
+ * @file operator-rename.cc
+ * @brief Implementation of the RENAME command.
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "convert.h"
+#include "depot.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+RenameOperator::RenameOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+RenameOperator::~RenameOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string RenameOperator::getName(void) const
+{
+ return "RENAME";
+}
+
+//----------------------------------------------------------------------
+int RenameOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult RenameOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ const string &srcmailbox = command.getMailbox();
+ const string &canonmailbox = toCanonMailbox(srcmailbox);
+ const string &canondestmailbox = toCanonMailbox(command.getNewMailbox());
+
+ // renaming INBOX should actually create the destination mailbox,
+ // move over all the messages and then leave INBOX empty.
+ if (canonmailbox == "INBOX") {
+ session.setLastError("Sorry, renaming INBOX is not yet supported"
+ " by this IMAP server. Try copying the messages"
+ " instead");
+ return NO;
+ }
+
+ if (canondestmailbox == "INBOX") {
+ session.setLastError("It is not allowed to rename a mailbox to INBOX");
+ return NO;
+ }
+
+ if (depot.renameMailbox(canonmailbox, canondestmailbox))
+ return OK;
+ else
+ return NO;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult RenameOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after RENAME");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after RENAME SPACE");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after RENAME SPACE mailbox");
+ return res;
+ }
+
+ string newmailbox;
+ if ((res = expectMailbox(newmailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after RENAME SPACE"
+ " mailbox SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after RENAME SPACE"
+ " mailbox SPACE mailbox");
+ return res;
+ }
+
+ session.mailboxchanges = true;
+
+ c_in.setName("RENAME");
+ c_in.setMailbox(mailbox);
+ c_in.setNewMailbox(newmailbox);
+
+ return ACCEPT;
+}
diff --git a/src/operator-search.cc b/src/operator-search.cc
new file mode 100644
index 0000000..0470eb5
--- /dev/null
+++ b/src/operator-search.cc
@@ -0,0 +1,898 @@
+/** --------------------------------------------------------------------
+ * @file operator-search.cc
+ * @brief Implementation of the SEARCH command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+#include <algorithm>
+
+#include <ctype.h>
+
+#include "convert.h"
+#include "depot.h"
+#include "imapparser.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "mime.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+bool SearchOperator::SearchNode::convertDate(const string &date,
+ time_t &t, const string &delim)
+{
+ vector<string> parts;
+ split(date, delim, parts);
+ if (parts.size() < 3) return false;
+
+ struct tm mold;
+ memset((char *) &mold, 0, sizeof(struct tm));
+ mold.tm_mday = atoi(parts[0].c_str());
+ mold.tm_year = atoi(parts[2].c_str()) - 1900;
+
+ // accept mixed case months. this is more than the standard
+ // accepts.
+ string month = parts[1];
+ lowercase(month);
+
+ if (month == "jan") mold.tm_mon = 0;
+ else if (month == "feb") mold.tm_mon = 1;
+ else if (month == "mar") mold.tm_mon = 2;
+ else if (month == "apr") mold.tm_mon = 3;
+ else if (month == "may") mold.tm_mon = 4;
+ else if (month == "jun") mold.tm_mon = 5;
+ else if (month == "jul") mold.tm_mon = 6;
+ else if (month == "aug") mold.tm_mon = 7;
+ else if (month == "sep") mold.tm_mon = 8;
+ else if (month == "oct") mold.tm_mon = 9;
+ else if (month == "nov") mold.tm_mon = 10;
+ else if (month == "dec") mold.tm_mon = 11;
+
+ t = mktime(&mold);
+ return true;
+}
+
+//----------------------------------------------------------------------
+bool SearchOperator::SearchNode::convertDateHeader(const string &d_in,
+ time_t &t)
+{
+ string date = d_in;
+ string::size_type n = date.find(',');
+ if (n != string::npos)
+ date = date.substr(n + 1);
+ trim(date);
+
+ bool result = convertDate(date, t, " ");
+ return result;
+}
+
+//----------------------------------------------------------------------
+SearchOperator::SearchNode::SearchNode(void)
+{
+}
+
+//----------------------------------------------------------------------
+SearchOperator::SearchNode::SearchNode(const BincImapParserSearchKey &a)
+{
+ init(a);
+}
+
+//----------------------------------------------------------------------
+int SearchOperator::SearchNode::getType(void) const
+{
+ return type;
+}
+
+//----------------------------------------------------------------------
+bool SearchOperator::SearchNode::match(Mailbox *mailbox,
+ Message *m, unsigned int seqnr,
+ unsigned int lastmessage,
+ unsigned int lastuid) const
+{
+ HeaderItem hitem;
+ string tmp;
+
+ switch (type) {
+ //--------------------------------------------------------------------
+ case S_ALL:
+ return true;
+ //--------------------------------------------------------------------
+ case S_ANSWERED:
+ return (m->getStdFlags() & Message::F_ANSWERED);
+ //--------------------------------------------------------------------
+ case S_BCC:
+ return m->headerContains("bcc", astring);
+ //--------------------------------------------------------------------
+ case S_BEFORE: {
+ time_t mtime = m->getInternalDate();
+ struct tm *mtime_ = localtime(&mtime);
+ mtime_->tm_sec = 0;
+ mtime_->tm_min = 0;
+ mtime_->tm_hour = 0;
+ mtime_->tm_wday = 0;
+ mtime_->tm_yday = 0;
+ mtime_->tm_isdst = 0;
+ mtime = mktime(mtime_);
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date <<
+ " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime < atime;
+ } //--------------------------------------------------------------------
+ case S_BODY:
+ return m->bodyContains(astring);
+ //--------------------------------------------------------------------
+ case S_CC:
+ return m->headerContains("cc", astring);
+ //--------------------------------------------------------------------
+ case S_DELETED:
+ return (m->getStdFlags() & Message::F_DELETED);
+ //--------------------------------------------------------------------
+ case S_FLAGGED:
+ return (m->getStdFlags() & Message::F_FLAGGED);
+ //--------------------------------------------------------------------
+ case S_FROM:
+ return m->headerContains("from", astring);
+ //--------------------------------------------------------------------
+ case S_KEYWORD:
+ // the server does not support keywords
+ return false;
+ //--------------------------------------------------------------------
+ case S_NEW:
+ return (m->getStdFlags() & Message::F_RECENT)
+ && !(m->getStdFlags() & Message::F_SEEN);
+ //--------------------------------------------------------------------
+ case S_OLD:
+ return !(m->getStdFlags() & Message::F_RECENT);
+ //--------------------------------------------------------------------
+ case S_ON: {
+ time_t mtime = m->getInternalDate();
+ struct tm *mtime_ = localtime(&mtime);
+ mtime_->tm_sec = 0;
+ mtime_->tm_min = 0;
+ mtime_->tm_hour = 0;
+ mtime_->tm_wday = 0;
+ mtime_->tm_yday = 0;
+ mtime_->tm_isdst = 0;
+ mtime = mktime(mtime_);
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date <<
+ " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime == atime;
+ } //--------------------------------------------------------------------
+ case S_RECENT:
+ return (m->getStdFlags() & Message::F_RECENT);
+ //--------------------------------------------------------------------
+ case S_SEEN:
+ return (m->getStdFlags() & Message::F_SEEN);
+ //--------------------------------------------------------------------
+ case S_SINCE: {
+ time_t mtime = m->getInternalDate();
+ struct tm *mtime_ = localtime(&mtime);
+ mtime_->tm_sec = 0;
+ mtime_->tm_min = 0;
+ mtime_->tm_hour = 0;
+ mtime_->tm_wday = 0;
+ mtime_->tm_yday = 0;
+ mtime_->tm_isdst = 0;
+ mtime = mktime(mtime_);
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date <<
+ " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime >= atime;
+ } //--------------------------------------------------------------------
+ case S_SUBJECT:
+ return m->headerContains("subject", astring);
+ //--------------------------------------------------------------------
+ case S_TEXT:
+ return m->textContains(astring);
+ //--------------------------------------------------------------------
+ case S_TO:
+ return m->headerContains("to", astring);
+ //--------------------------------------------------------------------
+ case S_UNANSWERED:
+ return !(m->getStdFlags() & Message::F_ANSWERED);
+ //--------------------------------------------------------------------
+ case S_UNDELETED:
+ return !(m->getStdFlags() & Message::F_DELETED);
+ //--------------------------------------------------------------------
+ case S_UNFLAGGED:
+ return !(m->getStdFlags() & Message::F_FLAGGED);
+ //--------------------------------------------------------------------
+ case S_UNKEYWORD:
+ // the server does not support keywords
+ return true;
+ //--------------------------------------------------------------------
+ case S_UNSEEN:
+ return !(m->getStdFlags() & Message::F_SEEN);
+ //--------------------------------------------------------------------
+ case S_DRAFT:
+ return (m->getStdFlags() & Message::F_DRAFT);
+ //--------------------------------------------------------------------
+ case S_HEADER:
+ return m->headerContains(astring, bstring);
+ //--------------------------------------------------------------------
+ case S_LARGER: {
+ return (m->getSize(true) > number);
+ }
+ //--------------------------------------------------------------------
+ case S_NOT:
+ for (vector<SearchNode>::const_iterator i = children.begin();
+ i != children.end(); ++i)
+ if ((*i).match(mailbox, m, seqnr, lastmessage, lastuid)) return false;
+ return true;
+ //--------------------------------------------------------------------
+ case S_OR:
+ for (vector<SearchNode>::const_iterator i = children.begin();
+ i != children.end(); ++i)
+ if ((*i).match(mailbox, m, seqnr, lastmessage, lastuid)) return true;
+ return false;
+ //--------------------------------------------------------------------
+ case S_SENTBEFORE: {
+ string tmp = m->getHeader("date");
+ if (tmp == "")
+ return false;
+
+ lowercase(tmp);
+
+ time_t mtime;
+ if (!convertDateHeader(tmp, mtime)) return false;
+
+ if (mtime == (time_t) -1) return false;
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date <<
+ " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime < atime;
+ } //--------------------------------------------------------------------
+ case S_SENTON: {
+ string tmp = m->getHeader("date");
+ if (tmp == "") return false;
+
+ lowercase(tmp);
+
+ time_t mtime;
+ if (!convertDateHeader(tmp, mtime)) return false;
+
+ if (mtime == (time_t) -1) return false;
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date <<
+ " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime == atime;
+ } //--------------------------------------------------------------------
+ case S_SENTSINCE: {
+ string tmp = m->getHeader("date");
+ if (tmp == "") return false;
+
+ lowercase(tmp);
+
+ time_t mtime;
+ if (!convertDateHeader(tmp, mtime)) return false;
+
+ if (mtime == (time_t) -1) return false;
+
+ time_t atime;
+ if (!convertDate(date, atime)) {
+ bincWarning << "warning, unable to convert " << date
+ << " to a time_t" << endl;
+ return false;
+ }
+
+ return mtime >= atime;
+ } //--------------------------------------------------------------------
+ case S_SMALLER:
+ return (m->getSize(true) < number);
+ //--------------------------------------------------------------------
+ case S_UID:
+ if (!bset->isInSet(m->getUID()))
+ if (!(m->getUID() == lastuid && !bset->isLimited())) return false;
+ return true;
+ //--------------------------------------------------------------------
+ case S_UNDRAFT:
+ return !(m->getStdFlags() & Message::F_DRAFT);
+ //--------------------------------------------------------------------
+ case S_SET:
+ if (!bset->isInSet(seqnr))
+ if (!(seqnr == lastmessage && !bset->isLimited())) return false;
+ return true;
+ //--------------------------------------------------------------------
+ case S_AND:
+ for (vector<SearchNode>::const_iterator i = children.begin();
+ i != children.end(); ++i)
+ if (!(*i).match(mailbox, m, seqnr, lastmessage, lastuid)) return false;
+ return true;
+ }
+
+ return false;
+}
+
+//----------------------------------------------------------------------
+void SearchOperator::SearchNode::init(const BincImapParserSearchKey &a)
+{
+ astring = a.astring;
+ bstring = a.bstring;
+ date = a.date;
+ number = a.number;
+ uppercase(astring);
+ uppercase(bstring);
+ uppercase(date);
+
+ if (a.name == "ALL") { type = S_ALL; weight = 1; }
+ else if (a.name == "ANSWERED") { type = S_ANSWERED; weight = 1; }
+ else if (a.name == "BCC") { type = S_BCC; weight = 2; }
+ else if (a.name == "BEFORE") { type = S_BEFORE; weight = 2; }
+ else if (a.name == "BODY") { type = S_BODY; weight = 1; }
+ else if (a.name == "CC") { type = S_CC; weight = 2; }
+ else if (a.name == "DELETED") { type = S_DELETED; weight = 1; }
+ else if (a.name == "FLAGGED") { type = S_FLAGGED; weight = 1; }
+ else if (a.name == "FROM") { type = S_FROM; weight = 2; }
+ else if (a.name == "KEYWORD") { type = S_KEYWORD; weight = 3; }
+ else if (a.name == "NEW") { type = S_NEW; weight = 1; }
+ else if (a.name == "OLD") { type = S_OLD; weight = 1; }
+ else if (a.name == "ON") { type = S_ON; weight = 1; }
+ else if (a.name == "RECENT") { type = S_RECENT; weight = 1; }
+ else if (a.name == "SEEN") { type = S_SEEN; weight = 1; }
+ else if (a.name == "SINCE") { type = S_SINCE; weight = 1; }
+ else if (a.name == "SUBJECT") { type = S_SUBJECT; weight = 2; }
+ else if (a.name == "TEXT") { type = S_TEXT; weight = 4; }
+ else if (a.name == "TO") { type = S_TO; weight = 2; }
+ else if (a.name == "UNANSWERED") { type = S_UNANSWERED; weight = 1; }
+ else if (a.name == "UNDELETED") { type = S_UNDELETED; weight = 1; }
+ else if (a.name == "UNFLAGGED") { type = S_UNFLAGGED; weight = 1; }
+ else if (a.name == "UNKEYWORD") { type = S_UNKEYWORD; weight = 1; }
+ else if (a.name == "UNSEEN") { type = S_UNSEEN; weight = 1; }
+ else if (a.name == "DRAFT") { type = S_DRAFT; weight = 1; }
+ else if (a.name == "HEADER") { type = S_HEADER; weight = 3; }
+ else if (a.name == "LARGER") { type = S_LARGER; weight = 4; }
+ else if (a.name == "NOT") {
+ // ******* NOT
+ type = S_NOT;
+ weight = 1;
+
+ vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
+ while (i != a.children.end()) {
+ SearchNode b(*i);
+ weight += b.getWeight();
+ children.push_back(b);
+ ++i;
+ }
+
+ } else if (a.name == "OR") {
+ // ******* OR
+ type = S_OR;
+ weight = 0;
+
+ vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
+ while (i != a.children.end()) {
+ SearchNode b(*i);
+ weight += b.getWeight();
+
+ children.push_back(b);
+ ++i;
+ }
+
+ } else if (a.name == "SENTBEFORE") { type = S_SENTBEFORE; weight = 1; }
+ else if (a.name == "SENTON") { type = S_SENTON; weight = 1; }
+ else if (a.name == "SENTSINCE") { type = S_SENTSINCE; weight = 1; }
+ else if (a.name == "SMALLER") { type = S_SMALLER; weight = 4; }
+ else if (a.name == "UID") {
+ bset = &a.getSet();
+ type = S_UID;
+ weight = 1;
+ } else if (a.name == "UNDRAFT") { type = S_UNDRAFT; weight = 1; }
+ else if (a.type == BincImapParserSearchKey::KEY_SET) {
+ bset = &a.getSet();
+ type = S_SET;
+ weight = 1;
+ } else if (a.type == BincImapParserSearchKey::KEY_AND) {
+ // ******* AND
+ type = S_AND;
+ weight = 0;
+
+ vector<BincImapParserSearchKey>::const_iterator i = a.children.begin();
+ while (i != a.children.end()) {
+ SearchNode b(*i);
+ weight += b.getWeight();
+ children.push_back(b);
+ ++i;
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+int SearchOperator::SearchNode::getWeight(void) const
+{
+ return weight;
+}
+
+//----------------------------------------------------------------------
+void SearchOperator::SearchNode::setWeight(int i)
+{
+ weight = i;
+}
+
+//----------------------------------------------------------------------
+void SearchOperator::SearchNode::order(void)
+{
+ for (vector<SearchNode>::iterator i = children.begin();
+ i != children.end(); ++i)
+ (*i).order();
+ ::stable_sort(children.begin(), children.end(), compareNodes);
+}
+
+//----------------------------------------------------------------------
+SearchOperator::SearchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+SearchOperator::~SearchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string SearchOperator::getName(void) const
+{
+ return "SEARCH";
+}
+
+//----------------------------------------------------------------------
+int SearchOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult SearchOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ Mailbox *mailbox = depot.getSelected();
+
+ if (command.getCharSet() != "" && command.getCharSet() != "US-ASCII") {
+ session.setLastError("The " + command.getCharSet()
+ + " charset is not supported");
+ session.setResponseCode("[BADCHARSET (\"US-ASCII\")]");
+ return NO;
+ }
+
+ bincClient << "* SEARCH";
+ bincClient.flush();
+
+ SearchNode s(command.searchkey);
+ s.order();
+
+ const unsigned int maxsqnr = mailbox->getMaxSqnr();
+ const unsigned int maxuid = mailbox->getMaxUid();
+
+ Mailbox::iterator i
+ = mailbox->begin(SequenceSet::all(), Mailbox::SKIP_EXPUNGED);
+ for (; i != mailbox->end(); ++i) {
+ Message &message = *i;
+
+ if (s.match(mailbox, &message, i.getSqnr(), maxsqnr, maxuid)) {
+ bincClient << " "
+ << (command.getUidMode() ? message.getUID() : i.getSqnr());
+ bincClient.flush();
+ }
+
+ message.close();
+ }
+
+ bincClient << endl;
+ return OK;
+}
+
+//------------------------------------------------------------------------
+Operator::ParseResult SearchOperator::parse(Request & c_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectThisString("CHARSET")) == ACCEPT) {
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after CHARSET");
+ return res;
+ }
+
+ string charset;
+ if ((res = expectAstring(charset)) != ACCEPT) {
+ session.setLastError("Expected astring after CHARSET SPACE");
+ return res;
+ }
+
+ c_in.setCharSet(charset);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after CHARSET SPACE astring");
+ return res;
+ }
+ }
+
+ BincImapParserSearchKey b;
+ if ((res = expectSearchKey(b)) != ACCEPT) {
+ session.setLastError("Expected search_key");
+ return res;
+ }
+
+ c_in.searchkey.type = BincImapParserSearchKey::KEY_AND;
+ c_in.searchkey.children.push_back(b);
+
+ while (1) {
+ if ((res = expectSPACE()) != ACCEPT)
+ break;
+
+ BincImapParserSearchKey c;
+ if ((res = expectSearchKey(c)) != ACCEPT) {
+ session.setLastError("Expected search_key after search_key SPACE");
+ return res;
+ }
+
+ c_in.searchkey.children.push_back(c);
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after search_key");
+ return res;
+ }
+
+ c_in.setName("SEARCH");
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+SearchOperator::expectSearchKey(BincImapParserSearchKey &s_in) const
+{
+ Session &session = Session::getInstance();
+ Operator::ParseResult res;
+
+ s_in.type = BincImapParserSearchKey::KEY_OTHER;
+ if ((res = expectThisString("ALL")) == ACCEPT) s_in.name = "ALL";
+ else if ((res = expectThisString("ANSWERED")) == ACCEPT) s_in.name = "ANSWERED";
+ else if ((res = expectThisString("BCC")) == ACCEPT) {
+ s_in.name = "BCC";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("BEFORE")) == ACCEPT) {
+ s_in.name = "BEFORE";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+ } else if ((res = expectThisString("BODY")) == ACCEPT) {
+ s_in.name = "BODY";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("CC")) == ACCEPT) {
+ s_in.name = "CC";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("DELETED")) == ACCEPT) s_in.name = "DELETED";
+ else if ((res = expectThisString("FLAGGED")) == ACCEPT) s_in.name = "FLAGGED";
+ else if ((res = expectThisString("FROM")) == ACCEPT) {
+ s_in.name = "FROM";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("KEYWORD")) == ACCEPT) {
+ s_in.name = "KEYWORD";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAtom(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected flag_keyword");
+ return res;
+ }
+ } else if ((res = expectThisString("NEW")) == ACCEPT) s_in.name = "NEW";
+ else if ((res = expectThisString("OLD")) == ACCEPT) s_in.name = "OLD";
+ else if ((res = expectThisString("ON")) == ACCEPT) {
+ s_in.name = "ON";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+ } else if ((res = expectThisString("RECENT")) == ACCEPT) s_in.name = "RECENT";
+ else if ((res = expectThisString("SEEN")) == ACCEPT) s_in.name = "SEEN";
+ else if ((res = expectThisString("SINCE")) == ACCEPT) {
+ s_in.name = "SINCE";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+ } else if ((res = expectThisString("SUBJECT")) == ACCEPT) {
+ s_in.name = "SUBJECT";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("TEXT")) == ACCEPT) {
+ s_in.name = "TEXT";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("TO")) == ACCEPT) {
+ s_in.name = "TO";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("UNANSWERED")) == ACCEPT)
+ s_in.name = "UNANSWERED";
+ else if ((res = expectThisString("UNDELETED")) == ACCEPT) s_in.name = "UNDELETED";
+ else if ((res = expectThisString("UNFLAGGED")) == ACCEPT) s_in.name = "UNFLAGGED";
+ else if ((res = expectThisString("UNKEYWORD")) == ACCEPT) {
+ s_in.name = "UNKEYWORD";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAtom(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected flag_keyword");
+ return res;
+ }
+ } else if ((res = expectThisString("UNSEEN")) == ACCEPT) s_in.name = "UNSEEN";
+ else if ((res = expectThisString("DRAFT")) == ACCEPT) s_in.name = "DRAFT";
+ else if ((res = expectThisString("HEADER")) == ACCEPT) {
+ s_in.name = "HEADER";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.astring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectAstring(s_in.bstring)) != ACCEPT) {
+ session.setLastError("Expected astring");
+ return res;
+ }
+ } else if ((res = expectThisString("LARGER")) == ACCEPT) {
+ s_in.name = "LARGER";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectNumber(s_in.number)) != ACCEPT) {
+ session.setLastError("Expected number");
+ return res;
+ }
+ } else if ((res = expectThisString("NOT")) == ACCEPT) {
+ s_in.name = "NOT";
+ s_in.type = BincImapParserSearchKey::KEY_NOT;
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ BincImapParserSearchKey s;
+ if ((res = expectSearchKey(s)) != ACCEPT) {
+ session.setLastError("Expected search_key");
+ return res;
+ }
+ s_in.children.push_back(s);
+ } else if ((res = expectThisString("OR")) == ACCEPT) {
+ s_in.name = "OR";
+ s_in.type = BincImapParserSearchKey::KEY_OR;
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ BincImapParserSearchKey s;
+ if ((res = expectSearchKey(s)) != ACCEPT) {
+ session.setLastError("Expected search_key");
+ return res;
+ }
+ s_in.children.push_back(s);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ BincImapParserSearchKey t;
+ if ((res = expectSearchKey(t)) != ACCEPT) {
+ session.setLastError("Expected search_key");
+ return res;
+ }
+ s_in.children.push_back(t);
+ } else if ((res = expectThisString("SENTBEFORE")) == ACCEPT) {
+ s_in.name = "SENTBEFORE";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+ } else if ((res = expectThisString("SENTON")) == ACCEPT) {
+ s_in.name = "SENTON";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+
+ } else if ((res = expectThisString("SENTSINCE")) == ACCEPT) {
+ s_in.name = "SENTSINCE";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectDate(s_in.date)) != ACCEPT) {
+ session.setLastError("Expected date");
+ return res;
+ }
+ } else if ((res = expectThisString("SMALLER")) == ACCEPT) {
+ s_in.name = "SMALLER";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectNumber(s_in.number)) != ACCEPT) {
+ session.setLastError("Expected number");
+ return res;
+ }
+ } else if ((res = expectThisString("UID")) == ACCEPT) {
+ s_in.name = "UID";
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectSet(s_in.bset)) != ACCEPT) {
+ session.setLastError("Expected number");
+ return res;
+ }
+ } else if ((res = expectThisString("UNDRAFT")) == ACCEPT)
+ s_in.name = "UNDRAFT";
+ else if ((res = expectSet(s_in.bset)) == ACCEPT) {
+ s_in.name = "";
+ s_in.type = BincImapParserSearchKey::KEY_SET;
+ } else if ((res = expectThisString("(")) == ACCEPT) {
+ s_in.type = BincImapParserSearchKey::KEY_AND;
+
+ while (1) {
+ BincImapParserSearchKey c;
+ if ((res = expectSearchKey(c)) != ACCEPT) {
+ session.setLastError("Expected search_key");
+ return res;
+ }
+
+ s_in.children.push_back(c);
+
+ if ((res = expectSPACE()) != ACCEPT) break;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+ } else
+ return REJECT;
+
+ return ACCEPT;
+}
diff --git a/src/operator-select.cc b/src/operator-select.cc
new file mode 100644
index 0000000..854783a
--- /dev/null
+++ b/src/operator-select.cc
@@ -0,0 +1,158 @@
+/** --------------------------------------------------------------------
+ * @file operator-select.cc
+ * @brief Implementation of the SELECT command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "pendingupdates.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+SelectOperator::SelectOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+SelectOperator::~SelectOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string SelectOperator::getName(void) const
+{
+ return "SELECT";
+}
+
+//----------------------------------------------------------------------
+int SelectOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult SelectOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ const bool examine = (command.getName() == "EXAMINE");
+ const string &srcmailbox = command.getMailbox();
+ const string &canonmailbox = toCanonMailbox(srcmailbox);
+
+ Mailbox *mailbox = depot.getSelected();
+ if (mailbox != 0) {
+ mailbox->closeMailbox();
+ depot.resetSelected();
+ mailbox = 0;
+ }
+
+ mailbox = depot.get(canonmailbox);
+ if (mailbox == 0) {
+ session.setLastError(depot.getLastError());
+ return NO;
+ }
+
+ mailbox->setReadOnly(examine);
+
+ if (!mailbox->selectMailbox(canonmailbox,
+ depot.mailboxToFilename(canonmailbox))) {
+ bincWarning << "selecting mailbox failed: "
+ << mailbox->getLastError() << endl;
+ session.setLastError(mailbox->getLastError());
+ return NO;
+ }
+
+ // find first unseen
+ int unseen = -1;
+ Mailbox::iterator i = mailbox->begin(SequenceSet::all(),
+ Mailbox::SKIP_EXPUNGED | Mailbox::SQNR_MODE);
+ for (; i != mailbox->end(); ++i) {
+ Message &message = *i;
+
+ if (unseen == -1 && ((message.getStdFlags() & Message::F_SEEN) == 0)) {
+ unseen = i.getSqnr();
+ break;
+ }
+ }
+
+ // show pending updates with only exists and recent response. do not
+ // re-scan.
+ pendingUpdates(mailbox, PendingUpdates::EXISTS
+ | PendingUpdates::RECENT, false, true);
+
+ // unseen
+ if (unseen != -1)
+ bincClient << "*" << " OK [UNSEEN " << unseen << "] Message "
+ << unseen << " is first unseen" << endl;
+
+ // uidvalidity
+ bincClient << "*" << " OK [UIDVALIDITY " << mailbox->getUidValidity() << "]"
+ << endl;
+
+ // uidnext
+ bincClient << "*" << " OK [UIDNEXT " << toString(mailbox->getUidNext()) << "] "
+ << toString(mailbox->getUidNext()) << " is the next UID" << endl;
+
+ // flags
+ bincClient << "*"
+ << " FLAGS (\\Answered \\Flagged \\Deleted \\Recent \\Seen \\Draft \\*)"
+ << endl;
+
+ // permanentflags
+ bincClient << "*"
+ << " OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted "
+ << "\\Seen \\Draft \\*)] Limited"
+ << endl;
+
+ session.setState(Session::SELECTED);
+ depot.setSelected(mailbox);
+
+ session.setResponseCode(examine ? "READ-ONLY" : "READ-WRITE");
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult SelectOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after" + c_in.getName());
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox after " + c_in.getName()
+ + " SPACE");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF after " + c_in.getName()
+ + " SPACE mailbox");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ return ACCEPT;
+}
diff --git a/src/operator-starttls.cc b/src/operator-starttls.cc
new file mode 100644
index 0000000..fcc7b3b
--- /dev/null
+++ b/src/operator-starttls.cc
@@ -0,0 +1,115 @@
+/** --------------------------------------------------------------------
+ * @file operator-starttls.cc
+ * @brief Implementation of the STARTTLS command - based on sslserver
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "recursivedescent.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "session.h"
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+StarttlsOperator::StarttlsOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+StarttlsOperator::~StarttlsOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string StarttlsOperator::getName(void) const
+{
+ return "STARTTLS";
+}
+
+//----------------------------------------------------------------------
+int StarttlsOperator::getState(void) const
+{
+ return Session::NONAUTHENTICATED
+ | Session::AUTHENTICATED
+ | Session::SELECTED;
+}
+
+//----------------------------------------------------------------------
+int StarttlsOperator::goStartTLS (void) const
+{
+ Session &session = Session::getInstance();
+
+ if (getenv("UCSPITLS")) {
+ string fdstr;
+ int fd;
+
+ fdstr = session.getEnv("SSLCTLFD");
+ fd = std::stoi(fdstr);
+ if (write(fd,"Y",1) < 1) return NOTHING;
+ bincClient.flush(); // flush all previous received data
+
+ fdstr = session.getEnv("SSLREADFD");
+ fd = std::stoi(fdstr);
+ if (fcntl(fd,F_GETFL,0) == -1) return NOTHING;
+ close (0);
+ if (fcntl(fd,F_DUPFD,0) == -1) return NOTHING;
+ close (fd);
+
+ fdstr = session.getEnv("SSLWRITEFD");
+ fd = std::stoi(fdstr);
+ if (fcntl(fd,F_GETFL,0) == -1) return NOTHING;
+ close (1);
+ if (fcntl(fd,F_DUPFD,1) == -1) return NOTHING;
+ close (fd);
+ }
+
+ return ACCEPT;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult StarttlsOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+ if (session.command.ssl) {
+ session.setLastError("Already in TLS mode");
+ return BAD;
+ }
+
+ bincClient << "* ENABLED StartTLS - begin negotiation now" << endl;
+ bincClient << command.getTag() << " OK STARTTLS completed" << endl;
+
+ if (goStartTLS() == ACCEPT)
+ session.command.ssl = true;
+ else
+ return NO;
+
+ return NOTHING;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult StarttlsOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode()) return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setName("STARTTLS");
+
+ return ACCEPT;
+}
diff --git a/src/operator-status.cc b/src/operator-status.cc
new file mode 100644
index 0000000..da74f16
--- /dev/null
+++ b/src/operator-status.cc
@@ -0,0 +1,152 @@
+/** --------------------------------------------------------------------
+ * @file operator-status.cc
+ * @brief Implementation of the STATUS command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "convert.h"
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "status.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+StatusOperator::StatusOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+StatusOperator::~StatusOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string StatusOperator::getName(void) const
+{
+ return "STATUS";
+}
+
+//----------------------------------------------------------------------
+int StatusOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult StatusOperator::process(Depot &depot,
+ Request &command)
+{
+ Session &session = Session::getInstance();
+
+ Status status;
+ if (!depot.getStatus(command.getMailbox(), status)) {
+ session.setLastError(depot.getLastError());
+ return NO;
+ }
+
+ bincClient << "* STATUS " << toImapString(command.getMailbox()) << " (";
+
+ string prefix;
+ for (vector<string>::const_iterator i = command.statuses.begin();
+ i != command.statuses.end(); ++i) {
+ string tmp = *i;
+ uppercase(tmp);
+ if (tmp == "UIDNEXT") {
+ bincClient << prefix << "UIDNEXT " << status.getUidNext();
+ prefix = " ";
+ } else if (tmp == "MESSAGES") {
+ bincClient << prefix << "MESSAGES " << status.getMessages();
+ prefix = " ";
+ } else if (tmp == "RECENT") {
+ bincClient << prefix << "RECENT " << status.getRecent();
+ prefix = " ";
+ } else if (tmp == "UIDVALIDITY") {
+ bincClient << prefix << "UIDVALIDITY " << status.getUidValidity();
+ prefix = " ";
+ } else if (tmp == "UNSEEN") {
+ bincClient << prefix << "UNSEEN " << status.getUnseen();
+ prefix = " ";
+ }
+ }
+ bincClient << ")" << endl;
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult StatusOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectThisString("(")) != ACCEPT) {
+ session.setLastError("Expected (");
+ return res;
+ }
+
+ while (1) {
+ if ((res = expectThisString("MESSAGES")) == ACCEPT)
+ c_in.getStatuses().push_back("MESSAGES");
+ else if ((res = expectThisString("RECENT")) == ACCEPT)
+ c_in.getStatuses().push_back("RECENT");
+ else if ((res = expectThisString("UIDNEXT")) == ACCEPT)
+ c_in.getStatuses().push_back("UIDNEXT");
+ else if ((res = expectThisString("UIDVALIDITY")) == ACCEPT)
+ c_in.getStatuses().push_back("UIDVALIDITY");
+ else if ((res = expectThisString("UNSEEN")) == ACCEPT)
+ c_in.getStatuses().push_back("UNSEEN");
+ else {
+ session.setLastError("Expected status_att");
+ return res;
+ }
+
+ if (expectSPACE() != ACCEPT) break;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return ERROR;
+ }
+
+ return ACCEPT;
+}
diff --git a/src/operator-store.cc b/src/operator-store.cc
new file mode 100644
index 0000000..efb5a48
--- /dev/null
+++ b/src/operator-store.cc
@@ -0,0 +1,194 @@
+/** --------------------------------------------------------------------
+ * @file operator-store.cc
+ * @brief Implementation of the STORE command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <string>
+#include <iostream>
+
+#include "depot.h"
+#include "imapparser.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "pendingupdates.h"
+#include "recursivedescent.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+StoreOperator::StoreOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+StoreOperator::~StoreOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string StoreOperator::getName(void) const
+{
+ return "STORE";
+}
+
+//----------------------------------------------------------------------
+int StoreOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult StoreOperator::process(Depot &depot,
+ Request &command)
+{
+ Mailbox *mailbox = depot.getSelected();
+
+ // mask all passed flags together
+ unsigned int newflags = (unsigned int) Message::F_NONE;
+ vector<string> newCustomFlags;
+ vector<string>::const_iterator f_i = command.flags.begin();
+ while (f_i != command.flags.end()) {
+ if (*f_i == "\\Deleted") newflags |= Message::F_DELETED;
+ else if (*f_i == "\\Answered") newflags |= Message::F_ANSWERED;
+ else if (*f_i == "\\Seen") newflags |= Message::F_SEEN;
+ else if (*f_i == "\\Draft") newflags |= Message::F_DRAFT;
+ else if (*f_i == "\\Flagged") newflags |= Message::F_FLAGGED;
+ else newCustomFlags.push_back(*f_i);
+
+ ++f_i;
+ }
+
+ // pass through all messages
+ unsigned int mode
+ = command.getUidMode() ? Mailbox::UID_MODE : Mailbox::SQNR_MODE;
+
+ Mailbox::iterator i
+ = mailbox->begin(command.bset, Mailbox::SKIP_EXPUNGED | mode);
+
+ for (; i != mailbox->end(); ++i) {
+ Message &message = *i;
+
+ // get and reset the old flags
+ unsigned int flags = (unsigned int) message.getStdFlags();
+
+ bool recent = (flags & Message::F_RECENT) != 0;
+ flags &= (~Message::F_RECENT);
+
+ // add, remove or set flags
+ switch (command.getMode()[0]) {
+ case '+':
+ flags |= newflags;
+ for (vector<string>::const_iterator it = newCustomFlags.begin();
+ it != newCustomFlags.end(); ++it)
+ message.setCustomFlag(*it);
+ break;
+ case '-':
+ flags &= ~newflags;
+ for (vector<string>::const_iterator it = newCustomFlags.begin();
+ it != newCustomFlags.end(); ++it)
+ message.removeCustomFlag(*it);
+ break;
+ default:
+ flags = newflags;
+ message.resetCustomFlags();
+ for (vector<string>::const_iterator it = newCustomFlags.begin();
+ it != newCustomFlags.end(); ++it)
+ message.setCustomFlag(*it);
+ break;
+ };
+
+ // set new flags, even if they weren't changed.
+ if (recent) flags |= Message::F_RECENT;
+ message.resetStdFlags();
+ message.setStdFlag(flags);
+ }
+
+ // commit flag changes to mailbox (might change mailbox)
+ mailbox->updateFlags();
+
+ // check mailbox for updates, and report them
+ if (command.getMode().find(".SILENT") != string::npos)
+ pendingUpdates(mailbox, PendingUpdates::EXISTS
+ | PendingUpdates::RECENT, false, false, false,
+ command.getUidMode());
+ else
+ pendingUpdates(mailbox, PendingUpdates::EXISTS
+ | PendingUpdates::RECENT
+ | PendingUpdates::EXPUNGE
+ | PendingUpdates::FLAGS, false, false, false,
+ command.getUidMode());
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult StoreOperator::parse(Request & c_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ if ((res = expectSet(c_in.getSet())) != ACCEPT) {
+ session.setLastError("Expected Set");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ string mode;
+ if ((res = expectThisString("+")) == ACCEPT)
+ mode = "+";
+ else if ((res = expectThisString("-")) == ACCEPT)
+ mode = "-";
+
+ if ((res = expectThisString("FLAGS")) != ACCEPT) {
+ session.setLastError("Expected FLAGS");
+ return res;
+ } else
+ mode += "FLAGS";
+
+ if ((res = expectThisString(".SILENT")) == ACCEPT) mode += ".SILENT";
+
+ c_in.setMode(mode);
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ bool paren = false;
+ if ((res = expectThisString("(")) == ACCEPT) paren = true;
+
+ if ((res = expectFlag(c_in.getFlags())) == ACCEPT)
+ while (1) {
+ if ((res = expectSPACE()) != ACCEPT) break;
+
+ if ((res = expectFlag(c_in.getFlags())) != ACCEPT) {
+ session.setLastError("Expected flag after SPACE");
+ return res;
+ }
+ }
+
+ if (paren)
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ return ACCEPT;
+}
diff --git a/src/operator-subscribe.cc b/src/operator-subscribe.cc
new file mode 100644
index 0000000..a4e77df
--- /dev/null
+++ b/src/operator-subscribe.cc
@@ -0,0 +1,84 @@
+/** --------------------------------------------------------------------
+ * @file operator-subscribe.cc
+ * @brief Implementation of the SUBSCRIBE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <iostream>
+#include "convert.h"
+#include <sys/stat.h>
+
+#include "recursivedescent.h"
+
+#include "session.h"
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+SubscribeOperator::SubscribeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+SubscribeOperator::~SubscribeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string SubscribeOperator::getName(void) const
+{
+ return "SUBSCRIBE";
+}
+
+//----------------------------------------------------------------------
+int SubscribeOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult SubscribeOperator::process(Depot &depot,
+ Request &command)
+{
+ const string &srcmailbox = command.getMailbox();
+ const string &canonmailbox = toCanonMailbox(srcmailbox);
+
+ depot.loadSubscribes();
+ depot.subscribeTo(canonmailbox);
+ depot.saveSubscribes();
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult SubscribeOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox");
+ return res;
+ }
+ c_in.setMailbox(mailbox);
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ return ACCEPT;
+}
diff --git a/src/operator-unsubscribe.cc b/src/operator-unsubscribe.cc
new file mode 100644
index 0000000..7ef2bf4
--- /dev/null
+++ b/src/operator-unsubscribe.cc
@@ -0,0 +1,91 @@
+/** --------------------------------------------------------------------
+ * @file operator-unsubscribe.cc
+ * @brief Implementation of the UNSUBSCRIBE command.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+#include <vector>
+#include <iostream>
+#include "convert.h"
+#include <sys/stat.h>
+
+#include "recursivedescent.h"
+
+#include "session.h"
+#include "depot.h"
+#include "operators.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//----------------------------------------------------------------------
+UnsubscribeOperator::UnsubscribeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+UnsubscribeOperator::~UnsubscribeOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string UnsubscribeOperator::getName(void) const
+{
+ return "UNSUBSCRIBE";
+}
+
+//----------------------------------------------------------------------
+int UnsubscribeOperator::getState(void) const
+{
+ return Session::AUTHENTICATED | Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult UnsubscribeOperator::process(Depot &depot,
+ Request &command)
+{
+ const string &mailbox = command.getMailbox();
+ const string &canonmailbox = toCanonMailbox(mailbox);
+
+ depot.loadSubscribes();
+ if (!depot.unsubscribeTo(canonmailbox)) {
+ Session &session = Session::getInstance();
+ session.setLastError("Not subscribed to " + toImapString(mailbox));
+ return NO;
+ }
+
+ depot.saveSubscribes();
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult UnsubscribeOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ if (c_in.getUidMode())
+ return REJECT;
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE");
+ return res;
+ }
+
+ string mailbox;
+ if ((res = expectMailbox(mailbox)) != ACCEPT) {
+ session.setLastError("Expected mailbox");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setMailbox(mailbox);
+
+ return ACCEPT;
+}
diff --git a/src/pendingupdates.cc b/src/pendingupdates.cc
new file mode 100644
index 0000000..337c21a
--- /dev/null
+++ b/src/pendingupdates.cc
@@ -0,0 +1,265 @@
+/** --------------------------------------------------------------------
+ * @file pendingupdates.cc
+ * @brief <--->
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "message.h"
+#include "pendingupdates.h"
+#include "session.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+PendingUpdates::PendingUpdates(void) : expunges(), flagupdates()
+{
+ recent = 0;
+ exists = 0;
+
+ newrecent = false;
+ newexists = false;
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::~PendingUpdates(void)
+{
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::addExpunged(unsigned int uid)
+{
+ expunges.push_back(uid);
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::addFlagUpdates(unsigned int sqnr, unsigned int uid,
+ unsigned int flags, const vector<string> &cflags)
+{
+ flagupdates[sqnr] = flags;
+ sqnrtouid[sqnr] = uid;
+ sqnrtocflags[sqnr] = cflags;
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::setExists(unsigned int n)
+{
+ exists = n;
+ newexists = true;
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::setRecent(unsigned int n)
+{
+ recent = n;
+ newrecent = true;
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::getExists(void) const
+{
+ return exists;
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::getRecent(void) const
+{
+ return recent;
+}
+
+//------------------------------------------------------------------------
+bool PendingUpdates::newExists(void) const
+{
+ return newexists;
+}
+
+//------------------------------------------------------------------------
+bool PendingUpdates::newRecent(void) const
+{
+ return newrecent;
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::expunged_const_iterator::expunged_const_iterator(void)
+{
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::expunged_const_iterator::expunged_const_iterator(vector<unsigned int>::iterator i) : internal(i)
+{
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::expunged_const_iterator::operator * (void) const
+{
+ return *internal;
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::expunged_const_iterator::operator ++ (void)
+{
+ ++internal;
+}
+
+//------------------------------------------------------------------------
+bool PendingUpdates::expunged_const_iterator::operator == (expunged_const_iterator i) const
+{
+ return internal == i.internal;
+}
+
+//------------------------------------------------------------------------
+bool PendingUpdates::expunged_const_iterator::operator != (expunged_const_iterator i) const
+{
+ return internal != i.internal;
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::expunged_const_iterator PendingUpdates::beginExpunged(void)
+{
+ return expunged_const_iterator(expunges.begin());
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::expunged_const_iterator PendingUpdates::endExpunged(void)
+{
+ return expunged_const_iterator(expunges.end());
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::flagupdates_const_iterator::flagupdates_const_iterator(void)
+{
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::flagupdates_const_iterator::flagupdates_const_iterator(
+ map<unsigned int, unsigned int>::iterator i,
+ map<unsigned int, vector<string> > *j,
+ map<unsigned int, unsigned int> *sqnrmap) : internal(i), sqnrtocflags(j)
+{
+ sqnrtouid = sqnrmap;
+}
+
+//------------------------------------------------------------------------
+void PendingUpdates::flagupdates_const_iterator::operator ++ (void)
+{
+ ++internal;
+}
+
+//------------------------------------------------------------------------
+bool PendingUpdates::flagupdates_const_iterator::operator != (flagupdates_const_iterator i) const
+{
+ return internal != i.internal;
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::flagupdates_const_iterator PendingUpdates::beginFlagUpdates(void)
+{
+ return flagupdates_const_iterator(flagupdates.begin(), &sqnrtocflags, &sqnrtouid);
+}
+
+//------------------------------------------------------------------------
+PendingUpdates::flagupdates_const_iterator PendingUpdates::endFlagUpdates(void)
+{
+ return flagupdates_const_iterator(flagupdates.end(), &sqnrtocflags, &sqnrtouid);
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::flagupdates_const_iterator::first(void) const
+{
+ return internal->first;
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::flagupdates_const_iterator::second(void) const
+{
+ return internal->second;
+}
+
+//------------------------------------------------------------------------
+vector<string> PendingUpdates::flagupdates_const_iterator::getCustomFlags(void) const
+{
+ return (*sqnrtocflags)[internal->first];
+}
+
+//------------------------------------------------------------------------
+unsigned int PendingUpdates::flagupdates_const_iterator::getUID(void) const
+{
+ return (*sqnrtouid)[internal->first];
+}
+
+//--------------------------------------------------------------------
+bool Binc::pendingUpdates(Mailbox *mailbox, int type, bool rescan, bool showAll,
+ bool forceScan, bool uidfetchflags)
+{
+ Session &session = Session::getInstance();
+
+ if (mailbox == 0)
+ return true;
+
+ PendingUpdates p;
+ if (!mailbox->getUpdates(rescan, type, p, forceScan)) {
+ session.setLastError(mailbox->getLastError());
+ return false;
+ }
+
+ if (type & PendingUpdates::EXPUNGE) {
+ PendingUpdates::expunged_const_iterator i = p.beginExpunged();
+ PendingUpdates::expunged_const_iterator e = p.endExpunged();
+
+ while (i != e) {
+ bincClient << "* " << *i << " EXPUNGE" << endl;
+ ++i;
+ }
+ }
+
+ if (((type & PendingUpdates::EXISTS) && p.newExists()) || showAll)
+ bincClient << "* " << p.getExists() << " EXISTS" << endl;
+
+ if (((type & PendingUpdates::RECENT) && p.newRecent() || showAll))
+ bincClient << "* " << p.getRecent() << " RECENT" << endl;
+
+ if (type & PendingUpdates::FLAGS) {
+ PendingUpdates::flagupdates_const_iterator i = p.beginFlagUpdates();
+ PendingUpdates::flagupdates_const_iterator e = p.endFlagUpdates();
+
+ while (i != e) {
+ int flags = i.second();
+
+ vector<string> flagv;
+ if (flags & Message::F_SEEN) flagv.push_back("\\Seen");
+ if (flags & Message::F_ANSWERED) flagv.push_back("\\Answered");
+ if (flags & Message::F_DELETED) flagv.push_back("\\Deleted");
+ if (flags & Message::F_DRAFT) flagv.push_back("\\Draft");
+ if (flags & Message::F_RECENT) flagv.push_back("\\Recent");
+ if (flags & Message::F_FLAGGED) flagv.push_back("\\Flagged");
+
+ bincClient << "* " << i.first() << " FETCH (FLAGS (";
+ for (vector<string>::const_iterator k
+ = flagv.begin(); k != flagv.end(); ++k) {
+ if (k != flagv.begin()) bincClient << " ";
+ bincClient << *k;
+ }
+
+ vector<string> customFlags = i.getCustomFlags();
+ for (vector<string>::const_iterator it = customFlags.begin();
+ it != customFlags.end(); ++it) {
+ if (flagv.size() > 0 || it != customFlags.begin()) bincClient << " ";
+ bincClient << *it;
+ }
+
+ bincClient << ")";
+ if (uidfetchflags) bincClient << " UID " << i.getUID();
+ bincClient << ")" << endl;
+ ++i;
+ }
+ }
+
+ return true;
+}
diff --git a/src/recursivedescent.cc b/src/recursivedescent.cc
new file mode 100644
index 0000000..be9c560
--- /dev/null
+++ b/src/recursivedescent.cc
@@ -0,0 +1,1055 @@
+/** --------------------------------------------------------------------
+ * @file recursivedescent.cc
+ * @brief Implementation of a recursive descent IMAP command parser.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "imapparser.h"
+#include "recursivedescent.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "convert.h"
+#include "session.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stack>
+#include <iostream>
+#include <iomanip>
+
+using namespace ::std;
+using namespace Binc;
+
+stack<int> Binc::inputBuffer;
+int Binc::charnr = 0;
+
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectThisString(const string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ string tmp;
+
+ bool match = true;
+ for (string::const_iterator i = s_in.begin(); i != s_in.end(); ++i) {
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout) return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ tmp += c;
+
+ if (toupper(*i) != toupper(c)) {
+ match = false;
+ break;
+ }
+ }
+
+ if (!match) {
+ bincClient.unreadStr(tmp);
+ return Operator::REJECT;
+ } else
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectDateTime(string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ if (expectThisString("\"") != Operator::ACCEPT)
+ return Operator::REJECT;
+
+ unsigned int digit1, digit2;
+ if (expectSPACE() == Operator::ACCEPT) {
+ digit1 = 0;
+ Operator::ParseResult res;
+ if ((res = expectDigit(digit2)) != Operator::ACCEPT) {
+ session.setLastError("expected digit (day) after \" and a SPACE.");
+ return res;
+ }
+ } else {
+ Operator::ParseResult res;
+ if ((res = expectDigit(digit1)) != Operator::ACCEPT) {
+ session.setLastError("expected first digit of day");
+ return res;
+ }
+ if ((res = expectDigit(digit2)) != Operator::ACCEPT) {
+ session.setLastError("expected second digit of day");
+ return res;
+ }
+ }
+
+ int day = digit1 * 10 + digit2;
+
+ BincStream daystr;
+
+ if (day < 10)
+ daystr << '0';
+ daystr << day;
+
+ s_in += daystr.str();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("-")) != Operator::ACCEPT) {
+ session.setLastError("expected -");
+ return res;
+ }
+
+ s_in += "-";
+
+ /* month */
+ if ((res = expectThisString("Jan")) == Operator::ACCEPT) s_in += "Jan";
+ else if ((res = expectThisString("Feb")) == Operator::ACCEPT) s_in += "Feb";
+ else if ((res = expectThisString("Mar")) == Operator::ACCEPT) s_in += "Mar";
+ else if ((res = expectThisString("Apr")) == Operator::ACCEPT) s_in += "Apr";
+ else if ((res = expectThisString("May")) == Operator::ACCEPT) s_in += "May";
+ else if ((res = expectThisString("Jun")) == Operator::ACCEPT) s_in += "Jun";
+ else if ((res = expectThisString("Jul")) == Operator::ACCEPT) s_in += "Jul";
+ else if ((res = expectThisString("Aug")) == Operator::ACCEPT) s_in += "Aug";
+ else if ((res = expectThisString("Sep")) == Operator::ACCEPT) s_in += "Sep";
+ else if ((res = expectThisString("Oct")) == Operator::ACCEPT) s_in += "Oct";
+ else if ((res = expectThisString("Nov")) == Operator::ACCEPT) s_in += "Nov";
+ else if ((res = expectThisString("Dec")) == Operator::ACCEPT) s_in += "Dec";
+ else {
+ session.setLastError("expected month");
+ return res;
+ }
+
+ if ((res = expectThisString("-")) != Operator::ACCEPT) {
+ session.setLastError("expected -");
+ return res;
+ }
+
+ s_in += "-";
+
+ /* year */
+ unsigned int year, c;
+ if ((res = expectDigit(year)) != Operator::ACCEPT) {
+ session.setLastError("expected digit (first digit of year)");
+ return res;
+ }
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit (second digit of year)");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit (third digit of year)");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit (last digit of year)");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ BincStream yearstr;
+
+ yearstr << year;
+
+ s_in += yearstr.str();
+
+ if ((res = expectSPACE()) != Operator::ACCEPT) {
+ session.setLastError("expected SPACE");
+ return res;
+ }
+
+ s_in += " ";
+
+ if ((res = expectTime(s_in)) != Operator::ACCEPT) {
+ session.setLastError("expected time");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != Operator::ACCEPT) {
+ session.setLastError("expected SPACE");
+ return res;
+ }
+
+ s_in += " ";
+
+ if ((res = expectZone(s_in)) != Operator::ACCEPT) {
+ session.setLastError("expected zone");
+ return res;
+ }
+
+ if ((res = expectThisString("\"")) != Operator::ACCEPT) {
+ session.setLastError("expected \"");
+ return res;
+ }
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectTime(string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ unsigned int c, t;
+ Operator::ParseResult res;
+ if ((res = expectDigit(t)) != Operator::ACCEPT)
+ return res;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ BincStream tstr;
+
+ tstr << t;
+
+ s_in += tstr.str();
+
+ if ((res = expectThisString(":")) != Operator::ACCEPT) {
+ session.setLastError("expected colon");
+ return res;
+ }
+
+ s_in += ":";
+
+ if ((res = expectDigit(t)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ tstr.clear();
+
+ tstr << t;
+
+ s_in += tstr.str();
+
+ if ((res = expectThisString(":")) != Operator::ACCEPT) {
+ session.setLastError("expected colon");
+ return res;
+ }
+
+ s_in += ":";
+
+ if ((res = expectDigit(t)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ tstr.clear();
+
+ tstr << t;
+
+ s_in += tstr.str();
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectZone(string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("-")) == Operator::ACCEPT)
+ s_in += "-";
+ else if ((res = expectThisString("+")) == Operator::ACCEPT)
+ s_in += "+";
+ else
+ return res;
+
+ unsigned int c, t;
+ if ((res = expectDigit(t)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ t = (t * 10) + c;
+
+ BincStream tstr;
+
+ tstr << t;
+
+ s_in += tstr.str();
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectListWildcards(int &c_in)
+{
+ Operator::ParseResult res;
+ if ((res = expectThisString("%")) == Operator::ACCEPT) {
+ c_in = '%';
+ return Operator::ACCEPT;
+ } else if ((res = expectThisString("*")) == Operator::ACCEPT) {
+ c_in = '*';
+ return Operator::ACCEPT;
+ } else
+ return res;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectListMailbox(string &s_in)
+{
+ Operator::ParseResult res;
+ if ((res = expectString(s_in)) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+
+ int c;
+ if ((res = expectAtomChar(c)) == Operator::ACCEPT
+ || (res = expectListWildcards(c)) == Operator::ACCEPT
+ || (res = expectThisString("]")) == Operator::ACCEPT) {
+ do {
+ s_in += (char) c;
+ if ((res = expectAtomChar(c)) != Operator::ACCEPT
+ && (res = expectListWildcards(c)) != Operator::ACCEPT
+ && (res = expectThisString("]")) != Operator::ACCEPT)
+ return Operator::ACCEPT;
+ } while (1);
+ }
+
+ bincClient.unreadStr(s_in);
+
+ return res;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectFlag(vector<string> &v_in)
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ string flag;
+ if ((res = expectThisString("\\Answered")) == Operator::ACCEPT)
+ v_in.push_back("\\Answered");
+ else if ((res = expectThisString("\\Flagged")) == Operator::ACCEPT)
+ v_in.push_back("\\Flagged");
+ else if ((res = expectThisString("\\Deleted")) == Operator::ACCEPT)
+ v_in.push_back("\\Deleted");
+ else if ((res = expectThisString("\\Seen")) == Operator::ACCEPT)
+ v_in.push_back("\\Seen");
+ else if ((res = expectThisString("\\Draft")) == Operator::ACCEPT)
+ v_in.push_back("\\Draft");
+ else if ((res = expectThisString("\\Answered")) == Operator::ACCEPT)
+ v_in.push_back("\\Answered");
+ else {
+ if ((res = expectThisString("\\")) == Operator::ACCEPT) {
+ if ((res = expectAtom(flag)) == Operator::ACCEPT)
+ v_in.push_back("\\" + flag);
+ else {
+ session.setLastError("expected atom");
+ return res;
+ }
+
+ } else if (expectAtom(flag) == Operator::ACCEPT) {
+ v_in.push_back(flag);
+ } else
+ return res;
+ }
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectDate(string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ bool quoted = false;
+ if ((res = expectThisString("\"")) == Operator::ACCEPT)
+ quoted = true;
+
+ /* day */
+ unsigned int day, c;
+ if ((res = expectDigit(c)) == Operator::ACCEPT) {
+ day = c;
+ if ((res = expectDigit(c)) == Operator::ACCEPT)
+ day = (day * 10) + c;
+
+ BincStream daystr;
+
+ daystr << day;
+
+ s_in += daystr.str();
+ } else {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ /* - */
+ if ((res = expectThisString("-")) != Operator::ACCEPT) {
+ session.setLastError("expected -");
+ return res;
+ }
+
+ s_in += '-';
+
+ /* month */
+ if ((res = expectThisString("Jan")) == Operator::ACCEPT) s_in += "Jan";
+ else if ((res = expectThisString("Feb")) == Operator::ACCEPT) s_in += "Feb";
+ else if ((res = expectThisString("Mar")) == Operator::ACCEPT) s_in += "Mar";
+ else if ((res = expectThisString("Apr")) == Operator::ACCEPT) s_in += "Apr";
+ else if ((res = expectThisString("May")) == Operator::ACCEPT) s_in += "May";
+ else if ((res = expectThisString("Jun")) == Operator::ACCEPT) s_in += "Jun";
+ else if ((res = expectThisString("Jul")) == Operator::ACCEPT) s_in += "Jul";
+ else if ((res = expectThisString("Aug")) == Operator::ACCEPT) s_in += "Aug";
+ else if ((res = expectThisString("Sep")) == Operator::ACCEPT) s_in += "Sep";
+ else if ((res = expectThisString("Oct")) == Operator::ACCEPT) s_in += "Oct";
+ else if ((res = expectThisString("Nov")) == Operator::ACCEPT) s_in += "Nov";
+ else if ((res = expectThisString("Dec")) == Operator::ACCEPT) s_in += "Dec";
+ else {
+ session.setLastError("expected month");
+ return res;
+ }
+
+ /* - */
+ if ((res = expectThisString("-")) != Operator::ACCEPT) {
+ session.setLastError("expected -");
+ return res;
+ }
+
+ s_in += '-';
+
+ /* year */
+ unsigned int year;
+ if ((res = expectDigit(year)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ if ((res = expectDigit(c)) != Operator::ACCEPT) {
+ session.setLastError("expected digit");
+ return res;
+ }
+
+ year = (year * 10) + c;
+
+ BincStream yearstr;
+
+ yearstr << year;
+
+ s_in += yearstr.str();
+
+ if (quoted)
+ if ((res = expectThisString("\"")) != Operator::ACCEPT) {
+ session.setLastError("expected \"");
+ return res;
+ }
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectCRLF(void)
+{
+ Operator::ParseResult res;
+ if ((res = expectCR()) == Operator::ACCEPT
+ && (res = expectLF()) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+ else
+ return res;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectCR(void)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (c == 0x0d)
+ return Operator::ACCEPT;
+ else {
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectLF(void)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (c == 0x0a)
+ return Operator::ACCEPT;
+ else {
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectTagChar(int &c_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ switch (c) {
+ case 041: case 043: case 044: case 046: case 047: case 054:
+ case 055: case 056: case 057: case 060: case 061: case 062:
+ case 063: case 064: case 065: case 066: case 067: case 070:
+ case 071: case 072: case 073: case 074: case 075: case 076:
+ case 077: case 0100: case 0101: case 0102: case 0103: case 0104:
+ case 0105: case 0106: case 0107: case 0110: case 0111: case 0112:
+ case 0113: case 0114: case 0115: case 0116: case 0117: case 0120:
+ case 0121: case 0122: case 0123: case 0124: case 0125: case 0126:
+ case 0127: case 0130: case 0131: case 0132: case 0133: case 0135:
+ case 0136: case 0137: case 0140: case 0141: case 0142: case 0143:
+ case 0144: case 0145: case 0146: case 0147: case 0150: case 0151:
+ case 0152: case 0153: case 0154: case 0155: case 0156: case 0157:
+ case 0160: case 0161: case 0162: case 0163: case 0164: case 0165:
+ case 0166: case 0167: case 0170: case 0171: case 0172: case 0174:
+ case 0175: case 0176:
+ c_in = c;
+ return Operator::ACCEPT;
+ default:
+ break;
+ }
+
+ bincClient.unreadChar(c);
+
+ return Operator::REJECT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectTag(string &s_in)
+{
+ string tag;
+ int tagchar;
+
+ int eres = expectTagChar(tagchar);
+ if (eres == Operator::REJECT)
+ return Operator::REJECT;
+ else if (eres == Operator::ERROR)
+ return Operator::ERROR;
+ else if (eres == Operator::TIMEOUT)
+ return Operator::TIMEOUT;
+ else {
+ tag += tagchar;
+
+ bool done = false;
+
+ while (!done) {
+ switch (expectTagChar(tagchar)) {
+ case Operator::ACCEPT:
+ tag += tagchar;
+ break;
+ case Operator::REJECT:
+ done = true;
+ break;
+ case Operator::ERROR:
+ return Operator::ERROR;
+ case Operator::TIMEOUT:
+ return Operator::TIMEOUT;
+ }
+ }
+ }
+
+ s_in = tag;
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectSPACE(void)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (c == ' ')
+ return Operator::ACCEPT;
+ else {
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+ }
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectMailbox(string &s_in)
+{
+ return expectAstring(s_in);
+}
+
+//----------------------------------------------------------------------
+// FIXME: This rule is wrong.
+Operator::ParseResult Binc::expectAstring(string &s_in)
+{
+ Operator::ParseResult res;
+ if ((res = expectAtom(s_in)) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+
+ if ((res = expectString(s_in)) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+
+ return res;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectAtomChar(int &c_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ switch (c) {
+ case 041: case 043: case 044: case 046: case 047: case 053:
+ case 054: case 055: case 056: case 057: case 060: case 061:
+ case 062: case 063: case 064: case 065: case 066: case 067:
+ case 070: case 071: case 072: case 073: case 074: case 075:
+ case 076: case 077: case 0100: case 0101: case 0102: case 0103:
+ case 0104: case 0105: case 0106: case 0107: case 0110: case 0111:
+ case 0112: case 0113: case 0114: case 0115: case 0116: case 0117:
+ case 0120: case 0121: case 0122: case 0123: case 0124: case 0125:
+ case 0126: case 0127: case 0130: case 0131: case 0132: case 0133:
+ case 0135: case 0136: case 0137: case 0140: case 0141: case 0142:
+ case 0143: case 0144: case 0145: case 0146: case 0147: case 0150:
+ case 0151: case 0152: case 0153: case 0154: case 0155: case 0156:
+ case 0157: case 0160: case 0161: case 0162: case 0163: case 0164:
+ case 0165: case 0166: case 0167: case 0170: case 0171: case 0172:
+ case 0174: case 0175: case 0176:
+ c_in = c;
+ return Operator::ACCEPT;
+ default:
+ break;
+ }
+
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectAtom(string &s_in)
+{
+ string atom;
+ int atomchar;
+
+ Operator::ParseResult res;
+ while ((res = expectAtomChar(atomchar)) == Operator::ACCEPT)
+ atom += atomchar;
+
+ if (atom == "") {
+ bincClient.unreadStr(atom);
+ return res;
+ } else
+ s_in = atom;
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectString(string &s_in)
+{
+ Operator::ParseResult res;
+ if ((res = expectQuoted(s_in)) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+
+ if ((res = expectLiteral(s_in)) == Operator::ACCEPT)
+ return Operator::ACCEPT;
+
+ return res;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectQuoted(string &s_in)
+{
+ string quoted;
+ int quotedchar;
+ Operator::ParseResult res;
+
+ if ((res = expectThisString("\"")) != Operator::ACCEPT)
+ return res;
+
+ while ((res = expectQuotedChar(quotedchar)) == Operator::ACCEPT)
+ quoted += quotedchar;
+
+ if ((res = expectThisString("\"")) != Operator::ACCEPT) {
+ bincClient.unreadStr("\"" + quoted);
+ return res;
+ }
+
+ s_in = quoted;
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectQuotedChar(int &c_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ switch (c) {
+ case 01: case 02: case 03: case 04: case 05: case 06: case 07:
+ case 010: case 011: case 013: case 014: case 016: case 017:
+ case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027:
+ case 030: case 031: case 032: case 033: case 034: case 035: case 036: case 037:
+ case 040: case 041: case 043: case 044: case 045: case 046: case 047:
+ case 050: case 051: case 052: case 053: case 054: case 055: case 056: case 057:
+ case 060: case 061: case 062: case 063: case 064: case 065: case 066: case 067:
+ case 070: case 071: case 072: case 073: case 074: case 075: case 076: case 077:
+ case 0100: case 0101: case 0102: case 0103: case 0104: case 0105: case 0106: case 0107:
+ case 0110: case 0111: case 0112: case 0113: case 0114: case 0115: case 0116: case 0117:
+ case 0120: case 0121: case 0122: case 0123: case 0124: case 0125: case 0126: case 0127:
+ case 0130: case 0131: case 0132: case 0133: case 0135: case 0136: case 0137:
+ case 0140: case 0141: case 0142: case 0143: case 0144: case 0145: case 0146: case 0147:
+ case 0150: case 0151: case 0152: case 0153: case 0154: case 0155: case 0156: case 0157:
+ case 0160: case 0161: case 0162: case 0163: case 0164: case 0165: case 0166: case 0167:
+ case 0170: case 0171: case 0172: case 0173: case 0174: case 0175: case 0176: case 0177:
+ c_in = c;
+ return Operator::ACCEPT;
+ case '\\': {
+ char d;
+ if (!bincClient.readChar(&d)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout) return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (d == '\"' || d == '\\') {
+ c_in = d;
+ return Operator::ACCEPT;
+ } else {
+ bincClient.unreadChar(d);
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+ }
+ }
+ default:
+ break;
+ }
+
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectLiteral(string &s_in)
+{
+ Session &session = Session::getInstance();
+
+ string literal;
+ Operator::ParseResult res;
+
+ if ((res = expectThisString("{")) != Operator::ACCEPT) return res;
+
+ unsigned int nchar;
+
+ if ((res = expectNumber(nchar)) != Operator::ACCEPT) {
+ session.setLastError("expected number");
+ return res;
+ }
+
+ // rfc2088 describes the non-synchronizing literal, or LITERAL+, as
+ // sent by the client with an extra '+' appended after the octet
+ // count.
+ bool literalPlus = false;
+ if ((res = expectThisString("+")) == Operator::ACCEPT)
+ literalPlus = true;
+
+ if ((res = expectThisString("}")) != Operator::ACCEPT) {
+ session.setLastError("expected }");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != Operator::ACCEPT) {
+ session.setLastError("expected CRLF");
+ return Operator::ERROR;
+ }
+
+ // Only send the reply if the client did not send a LITERAL+
+ // request.
+ if (!literalPlus) {
+ bincClient << "+ ok, send " << nchar << " bytes of data." << endl;
+ bincClient.flush();
+ }
+
+ for (unsigned int i = 0; i < nchar; ++i) {
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+ if (bincClient.getLastError() == IODevice::Timeout) return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ literal += c;
+ }
+
+ s_in = literal;
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectNumber(unsigned int &i_in)
+{
+ i_in = 0;
+ unsigned int n;
+ Operator::ParseResult res;
+
+ while ((res = expectDigit(n)) == Operator::ACCEPT) {
+ if (i_in == 0)
+ i_in = n;
+ else
+ i_in = (i_in * 10) + n;
+ }
+
+ if (res == Operator::TIMEOUT)
+ return res;
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectDigit(unsigned int &i_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (c == '0') {
+ i_in = 0;
+ return Operator::ACCEPT;
+ } else
+ bincClient.unreadChar(c);
+
+ if (expectDigitNZ(i_in) != Operator::ACCEPT)
+ return Operator::REJECT;
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectDigitNZ(unsigned int &i_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ switch (c) {
+ case '1': i_in = 1; break;
+ case '2': i_in = 2; break;
+ case '3': i_in = 3; break;
+ case '4': i_in = 4; break;
+ case '5': i_in = 5; break;
+ case '6': i_in = 6; break;
+ case '7': i_in = 7; break;
+ case '8': i_in = 8; break;
+ case '9': i_in = 9; break;
+ case -1:
+ session.setLastError(bincClient.getLastErrorString());
+ return Operator::ERROR;
+ case -2:
+ return Operator::TIMEOUT;
+ default:
+ bincClient.unreadChar(c);
+ return Operator::REJECT;
+ }
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectSet(SequenceSet &s_in)
+{
+ Session &session = Session::getInstance();
+ unsigned int seqnum = (unsigned int) -1;
+
+ Operator::ParseResult res;
+
+ /* if a set does not start with a sequencenum, then it's not a
+ * set. :-) seqnum == -1 means '*'. */
+ if ((res = expectSequenceNum(seqnum)) != Operator::ACCEPT)
+ return res;
+
+ /* the first number is always a part of the set */
+ s_in.addNumber(seqnum);
+
+ /* if _after_ a set there is a ':', then there will always be a
+ * sequencenum after the colon. if not, it's a syntax error. a
+ * colon delimits two numbers in a range. */
+ if ((res = expectThisString(":")) == Operator::ACCEPT) {
+ unsigned int seqnum2 = (unsigned int) -1;
+ if ((res = expectSequenceNum(seqnum2)) != Operator::ACCEPT) {
+ session.setLastError("expected sequencenum");
+ return res;
+ }
+
+ s_in.addRange(seqnum, seqnum2);
+ }
+
+ /* if _after_ a set there is a ',', then there will always be
+ * a set after the comma. if not, it's a syntax error. */
+ if ((res = expectThisString(",")) == Operator::ACCEPT)
+ if ((res = expectSet(s_in)) != Operator::ACCEPT) {
+ session.setLastError("expected set");
+ return res;
+ }
+
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectSequenceNum(unsigned int &i_in)
+{
+ Session &session = Session::getInstance();
+
+ char c;
+ if (!bincClient.readChar(&c)) {
+ session.setLastError(bincClient.getLastErrorString());
+
+ if (bincClient.getLastError() == IODevice::Timeout)
+ return Operator::TIMEOUT;
+ return Operator::ERROR;
+ }
+
+ if (c == '*') {
+ i_in = (unsigned int) -1;
+ return Operator::ACCEPT;
+ } else
+ bincClient.unreadChar(c);
+
+ if (expectNZNumber(i_in) != Operator::ACCEPT)
+ return Operator::REJECT;
+ else
+ return Operator::ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult Binc::expectNZNumber(unsigned int &i_in)
+{
+ unsigned int c;
+ Operator::ParseResult res;
+
+ if ((res = expectDigitNZ(c)) != Operator::ACCEPT)
+ return res;
+
+ i_in = c;
+ while ((res = expectDigit(c)) == Operator::ACCEPT)
+ i_in = (i_in * 10) + c;
+
+ if (res == Operator::TIMEOUT)
+ return res;
+
+ return Operator::ACCEPT;
+}
diff --git a/src/regmatch.cc b/src/regmatch.cc
new file mode 100644
index 0000000..c433087
--- /dev/null
+++ b/src/regmatch.cc
@@ -0,0 +1,31 @@
+/** --------------------------------------------------------------------
+ * @file regex.cc
+ * @brief Implementation of miscellaneous regexp functions
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "regmatch.h"
+#include <string>
+
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+
+using namespace ::std;
+
+//------------------------------------------------------------------------
+int Binc::regexMatch(const string &data_in, const string &p_in)
+{
+ regex_t r;
+ regmatch_t rm[2];
+
+ if (regcomp(&r, p_in.c_str(), REG_EXTENDED | REG_NOSUB) != 0)
+ return 2;
+
+ int n = regexec(&r, data_in.c_str(), data_in.length(), rm, 0);
+ regfree(&r);
+ if (n == 0)
+ return 0;
+ else
+ return 2;
+}
diff --git a/src/session-initialize-bincimap-up.cc b/src/session-initialize-bincimap-up.cc
new file mode 100644
index 0000000..e6a817f
--- /dev/null
+++ b/src/session-initialize-bincimap-up.cc
@@ -0,0 +1,137 @@
+/** --------------------------------------------------------------------
+ * @file session-initialize-bincimap-up.cc
+ * @brief bincimap-up requires sslserver
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * ----------------------------------------------------------------- **/
+#include <syslog.h>
+#include <ctype.h>
+
+#include "broker.h"
+#include "convert.h"
+#include "globals.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "multilogdevice.h"
+#include "syslogdevice.h"
+#include "stdiodevice.h"
+#include "session.h"
+#include "tools.h"
+
+#include <fcntl.h>
+#include <string>
+#include <map>
+
+using namespace ::std;
+using namespace Binc;
+
+extern char **environ;
+
+//----------------------------------------------------------------------
+bool Session::initialize(int argc, char *argv[])
+{
+ IOFactory &ioFactory = IOFactory::getInstance();
+
+ IODevice *stdioDevice = new StdIODevice(IODevice::IsEnabled);
+ stdioDevice->setFlags(IODevice::HasOutputLimit);
+ stdioDevice->setMaxOutputBufferSize(TRANSFER_BUFFER_SIZE);
+ ioFactory.addDevice(stdioDevice);
+
+ Session &session = Session::getInstance();
+
+ IOFactory::getLogger().clearFlags(IODevice::FlushesOnEndl);
+
+ // Read command line arguments
+ if (!session.parseCommandLine(argc, argv))
+ return false;
+
+ // Show help if asked for it
+ if (session.command.help) {
+ printf("%s\n", session.args.usageString().c_str());
+ return false;
+ }
+
+ // Show version if asked for it
+ if (session.command.version) {
+ printf("Binc IMAP v" BINC_VERSION"\n");
+ return false;
+ }
+
+ // Let the command line args override the global settings.
+ session.assignCommandLineArgs();
+
+ // for log input
+ string ip = getenv("TCP6REMOTEIP") ? getenv("TCP6REMOTEIP") :
+ getenv("TCPREMOTEIP") ? getenv("TCPREMOTEIP") : "?";
+ session.setIP(ip);
+
+ string logtype = session.getEnv("LOG_TYPE");
+ lowercase(logtype);
+ trim(logtype);
+ if (logtype == "" || logtype == "multilog") {
+ MultilogDevice *device = new MultilogDevice(IODevice::IsEnabled
+ | IODevice::FlushesOnEndl);
+ ioFactory.addDevice(device);
+ } else if (logtype == "syslog") {
+ const string f = session.getEnv("SYSLOG_FACILITY");
+ int facility;
+
+ if (isdigit(f[0])) {
+ facility = atoi(f);
+ } else {
+ if (f == "LOG_USER") facility = LOG_USER;
+ else if (f == "LOG_LOCAL0") facility = LOG_LOCAL0;
+ else if (f == "LOG_LOCAL1") facility = LOG_LOCAL1;
+ else if (f == "LOG_LOCAL2") facility = LOG_LOCAL2;
+ else if (f == "LOG_LOCAL3") facility = LOG_LOCAL3;
+ else if (f == "LOG_LOCAL4") facility = LOG_LOCAL4;
+ else if (f == "LOG_LOCAL5") facility = LOG_LOCAL5;
+ else if (f == "LOG_LOCAL6") facility = LOG_LOCAL6;
+ else if (f == "LOG_LOCAL7") facility = LOG_LOCAL7;
+ else facility = LOG_DAEMON;
+ }
+
+ SyslogDevice *device = new SyslogDevice(IODevice::IsEnabled
+ | IODevice::FlushesOnEndl,
+ "bincimap-up",
+ LOG_NDELAY | LOG_PID,
+ facility);
+ ioFactory.addDevice(device);
+ }
+
+ // Now that we know the log type, we can flush.
+ IOFactory::getLogger().setFlags(IODevice::FlushesOnEndl);
+ IOFactory::getLogger().setOutputLevelLimit(IODevice::InfoLevel);
+
+
+ // imaps (port 993) -- requires sslserver with option -e
+
+ int stls = 0;
+ if (getenv("SSL_SESSION_ID")) {
+ session.command.ssl = true;
+ stls = -1;
+ // else we will do starttls - requires new FDs
+ } else if (getenv("UCSPITLS")) {
+ string ucspitls = session.getEnv("UCSPITLS");
+ if (ucspitls == "+") stls = 1;
+ if (ucspitls == "-") stls = 0;
+ if (ucspitls == "!") stls = 2;
+ }
+
+ BrokerFactory &brokerfactory = BrokerFactory::getInstance();
+
+ brokerfactory.assign("AUTHENTICATE", new AuthenticateOperator());
+ brokerfactory.assign("CAPABILITY", new CapabilityOperator());
+ brokerfactory.assign("LOGIN", new LoginOperator());
+ brokerfactory.assign("LOGOUT", new LogoutOperator());
+ brokerfactory.assign("NOOP", new NoopOperator());
+ brokerfactory.assign("ID", new IdOperator());
+ if (stls > 0) brokerfactory.assign("STARTTLS", new StarttlsOperator());
+
+ bincClient.setTimeout(AUTH_TIMEOUT);
+
+ session.setState(Session::NONAUTHENTICATED);
+
+ // If the depot was not initialized properly, return false.
+ return true;
+}
diff --git a/src/session-initialize-bincimapd.cc b/src/session-initialize-bincimapd.cc
new file mode 100644
index 0000000..18d43b1
--- /dev/null
+++ b/src/session-initialize-bincimapd.cc
@@ -0,0 +1,213 @@
+/** --------------------------------------------------------------------
+ * @file session-initialize-bincimapd.cc
+ * @brief <--->
+ * @author Andreas Aardal Hanssen, Erwin Hoffmann
+ * @date 2002-2005, 2023
+ * --------------------------------------------------------------------
+ */
+#include <unistd.h>
+#include <syslog.h>
+#include <errno.h>
+
+#include "broker.h"
+#include "maildir.h"
+#include "depot.h"
+#include "globals.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "multilogdevice.h"
+#include "session.h"
+#include "stdiodevice.h"
+#include "syslogdevice.h"
+#include "tools.h"
+#include "convert.h"
+#include "imapserver.h"
+
+#include <string>
+#include <map>
+#include <signal.h>
+
+using namespace ::std;
+using namespace Binc;
+
+extern char **environ;
+
+//----------------------------------------------------------------------
+bool Session::initialize(int argc, char *argv[])
+{
+ IOFactory &ioFactory = IOFactory::getInstance();
+
+ IODevice *stdioDevice = new StdIODevice(IODevice::IsEnabled
+ | IODevice::HasInputLimit
+ | IODevice::HasTimeout);
+ stdioDevice->setFlags(IODevice::HasOutputLimit);
+ stdioDevice->setMaxOutputBufferSize(TRANSFER_BUFFER_SIZE);
+ ioFactory.addDevice(stdioDevice);
+
+ Session &session = Session::getInstance();
+
+ // Read command line arguments
+ if (!session.parseCommandLine(argc, argv))
+ return false;
+
+ // Show help if asked for it
+ if (session.command.help) {
+ printf("%s\n", session.args.usageString().c_str());
+ return false;
+ }
+
+ // Show version if asked for it
+ if (session.command.version) {
+ printf("Binc IMAP v" BINC_VERSION"\n");
+ return false;
+ }
+
+ // Assign command line arguments to global config.
+ session.assignCommandLineArgs();
+
+ // log settings
+ string ip = getenv("TCP6REMOTEIP") ? getenv("TCP6REMOTEIP") :
+ getenv("TCPREMOTEIP") ? getenv("TCPREMOTEIP") : "?";
+ session.setIP(ip);
+
+ string logtype = session.getEnv("LOG_TYPE");
+ lowercase(logtype);
+ trim(logtype);
+ if (logtype == "" || logtype == "multilog" ) {
+ ioFactory.addDevice(new MultilogDevice(IODevice::IsEnabled));
+ } else if (logtype == "syslog") {
+ const string f = session.getEnv("SYSLOG_FACILITY");
+
+ int facility;
+
+ if (isdigit(f[0])) facility = atoi(f);
+ else {
+ if (f == "LOG_USER") facility = LOG_USER;
+ else if (f == "LOG_LOCAL0") facility = LOG_LOCAL0;
+ else if (f == "LOG_LOCAL1") facility = LOG_LOCAL1;
+ else if (f == "LOG_LOCAL2") facility = LOG_LOCAL2;
+ else if (f == "LOG_LOCAL3") facility = LOG_LOCAL3;
+ else if (f == "LOG_LOCAL4") facility = LOG_LOCAL4;
+ else if (f == "LOG_LOCAL5") facility = LOG_LOCAL5;
+ else if (f == "LOG_LOCAL6") facility = LOG_LOCAL6;
+ else if (f == "LOG_LOCAL7") facility = LOG_LOCAL7;
+ else facility = LOG_DAEMON;
+ }
+
+ session.setEnv("SYSLOG_FACILITY", toString(facility));
+
+ ioFactory.addDevice(new SyslogDevice(IODevice::IsEnabled,
+ "bincimapd",
+ LOG_NDELAY | LOG_PID,
+ facility));
+ }
+
+ // Now that we know the log type, we can flush.
+ IOFactory::getLogger().flush();
+ IOFactory::getLogger().setFlags(IODevice::FlushesOnEndl);
+ IOFactory::getLogger().setOutputLevelLimit(IODevice::InfoLevel);
+
+ string pid = to_string(session.getPid());
+
+ char *logindetails = getenv("BINCIMAP_LOGIN");
+ if (logindetails == 0) {
+ bincLog
+ << "bincimapd: pid " << pid
+ << " BINCIMAP_LOGIN missing from environment (are you sure you invoked "
+ << argv[0] << " properly?)\n";
+ return false;
+ }
+
+ DepotFactory &depotfactory = DepotFactory::getInstance();
+ depotfactory.assign(new IMAPdirDepot());
+ depotfactory.assign(new MaildirPPDepot());
+
+ string depottype = session.getEnv("DEPOT");
+ if (depottype == "") depottype = "Maildir++";
+
+ if ((depot = depotfactory.get(depottype)) == 0) {
+ bincLog << "bincimapd: pid " << pid
+ << " Found no Depot for: " << depottype
+ << ". Please check your configurations file under the Mailbox section\n";
+ bincLog.flush();
+ return false;
+ }
+
+ depot->assign(new Maildir());
+ depot->setDefaultType("Maildir");
+
+ // Configurable delimiter to ease crossover from other IMAPservers
+ string delimiter = session.getEnv("DELIMITER");
+ if (delimiter != "") depot->setDelimiter(delimiter[0]);
+
+ BrokerFactory &brokerfactory = BrokerFactory::getInstance();
+
+ brokerfactory.assign("APPEND", new AppendOperator());
+ brokerfactory.assign("CAPABILITY", new CapabilityOperator());
+ brokerfactory.assign("CHECK", new CheckOperator());
+ brokerfactory.assign("CLOSE", new CloseOperator());
+ brokerfactory.assign("COPY", new CopyOperator());
+ brokerfactory.assign("CREATE", new CreateOperator());
+ brokerfactory.assign("DELETE", new DeleteOperator());
+ brokerfactory.assign("EXAMINE", new ExamineOperator());
+ brokerfactory.assign("EXPUNGE", new ExpungeOperator());
+ brokerfactory.assign("FETCH", new FetchOperator());
+ brokerfactory.assign("IDLE", new IdleOperator());
+ brokerfactory.assign("ID", new IdOperator());
+ brokerfactory.assign("LIST", new ListOperator());
+ brokerfactory.assign("LOGOUT", new LogoutOperator());
+ brokerfactory.assign("LSUB", new LsubOperator());
+ brokerfactory.assign("NAMESPACE", new NamespaceOperator());
+ brokerfactory.assign("NOOP", new NoopPendingOperator());
+ brokerfactory.assign("RENAME", new RenameOperator());
+ brokerfactory.assign("SEARCH", new SearchOperator());
+ brokerfactory.assign("SELECT", new SelectOperator());
+ brokerfactory.assign("STATUS", new StatusOperator());
+ brokerfactory.assign("STORE", new StoreOperator());
+ brokerfactory.assign("SUBSCRIBE", new SubscribeOperator());
+ brokerfactory.assign("UNSUBSCRIBE", new UnsubscribeOperator());
+
+ // automatically create depot directory if it's not there already
+ string path;
+ if (session.args.getUnqualifiedArgs().size() > 0)
+ path = session.args.getUnqualifiedArgs()[0];
+ if (path == "") path = ".";
+ else if (chdir(path.c_str()) != 0) {
+ mkdir(path.c_str(), 0777);
+ if (chdir(path.c_str()) != 0) {
+ bincLog << "bincimapd: pid" << pid
+ << " Error entering depot " + toImapString(path) + ": "
+ << strerror(errno) << endl;
+ return false;
+ }
+ }
+
+ // automatically create INBOX if it's not there already
+ if (depot->get("INBOX") == 0 && !depot->createMailbox("INBOX")) {
+ bincLog << "bincimapd: pid " << pid
+ << " " << depot->getLastError() << endl;
+ return false;
+ }
+
+ // load subscription list
+ depot->loadSubscribes();
+
+ session.setState(Session::AUTHENTICATED);
+
+ const string details = logindetails;
+ string::size_type det = details.find('+');
+ if (det == string::npos) {
+ bincLog << "bincimapd: pid " << pid
+ << " Invalid content of BINCIMAP_LOGIN - did you invoke "
+ << argv[0] << " correctly?" << endl;
+ return false;
+ }
+
+ const string tag = details.substr(det + 1);
+ const string command = details.substr(0, det);
+ bincClient << tag << " OK " << command << " completed" << endl;
+ bincClient.flush();
+ bincClient.setTimeout(IDLE_TIMEOUT);
+
+ return true;
+}
diff --git a/src/session.cc b/src/session.cc
new file mode 100644
index 0000000..849c1d6
--- /dev/null
+++ b/src/session.cc
@@ -0,0 +1,241 @@
+/** --------------------------------------------------------------------
+ * @file session.cc
+ * @brief Implementation of the Session class.
+ * ------------------------------------------------------------------ **/
+#include <unistd.h>
+#include <syslog.h>
+
+#include "argparser.h"
+#include "convert.h"
+#include "globals.h"
+#include "session.h"
+#include "tools.h"
+#include <string>
+#include <map>
+
+using namespace ::std;
+using namespace Binc;
+
+extern char **environ;
+
+//----------------------------------------------------------------------
+Session::Session(void)
+{
+ readbytes = 0;
+ writebytes = 0;
+ statements = 0;
+ bodies = 0;
+ mailboxchanges = true;
+ logfacility = LOG_DAEMON;
+}
+
+//----------------------------------------------------------------------
+Session &Session::getInstance(void)
+{
+ static Session session;
+ return session;
+}
+
+//----------------------------------------------------------------------
+const int Session::getState(void) const
+{
+ return state;
+}
+
+//----------------------------------------------------------------------
+void Session::setState(int n)
+{
+ state = n;
+}
+
+//----------------------------------------------------------------------
+const string &Session::getUserID(void) const
+{
+ return userid;
+}
+
+//----------------------------------------------------------------------
+void Session::setUserID(const string &s)
+{
+ userid = s;
+}
+
+//----------------------------------------------------------------------
+const string &Session::getIP(void) const
+{
+ return ip;
+}
+
+//----------------------------------------------------------------------
+void Session::setIP(const string &s)
+{
+ ip = s;
+}
+
+//----------------------------------------------------------------------
+void Session::setLogFacility(int facility)
+{
+ logfacility = facility;
+}
+
+//----------------------------------------------------------------------
+int Session::getLogFacility(void) const
+{
+ return logfacility;
+}
+
+//----------------------------------------------------------------------
+void Session::addBody(void)
+{
+ ++bodies;
+}
+
+//----------------------------------------------------------------------
+void Session::addStatement(void)
+{
+ ++statements;
+}
+
+//----------------------------------------------------------------------
+void Session::addReadBytes(int i)
+{
+ readbytes += i;
+}
+
+//----------------------------------------------------------------------
+void Session::addWriteBytes(int i)
+{
+ writebytes += i;
+}
+
+//----------------------------------------------------------------------
+int Session::getBodies(void) const
+{
+ return bodies;
+}
+
+//----------------------------------------------------------------------
+int Session::getStatements(void) const
+{
+ return statements;
+}
+
+//----------------------------------------------------------------------
+int Session::getWriteBytes(void) const
+{
+ return writebytes;
+}
+
+//----------------------------------------------------------------------
+int Session::getReadBytes(void) const
+{
+ return readbytes;
+}
+
+//----------------------------------------------------------------------
+bool Session::parseCommandLine(int argc, char * argv[])
+{
+ args.addOptional("h|help", "Display this help screen", true);
+ args.addOptional("version", "Display the version of Binc IMAP", true);
+ args.addOptional("a|allow-plain", "Allow authentication when not TLS protected", true);
+ args.addOptional("v|show-version", "Enable verbose IMAP greeting", false);
+ args.addOptional("l|log-type", "Sets the method used for logging", false);
+ args.addOptional("d|depot", "Sets the depot type", false);
+ args.addOptional("D|delimiter", "Sets the mailbox delimiter", false);
+
+ if (!args.parse(argc, argv)) {
+ setLastError("Command line error, " + args.errorString());
+ return false;
+ }
+
+ command.help = args["help"] == "yes" ? true : false;
+ command.version = args["version"] == "yes" ? true : false;
+
+ unparsedArgs = argv + args.argc();
+
+ return true;
+}
+
+//----------------------------------------------------------------------
+void Session::assignCommandLineArgs(void)
+{
+ if (args.hasArg("allow-plain"))
+ setEnv("ALLOW_NONSSL_PLAINTEXT_LOGINS", "yes");
+
+ if (args.hasArg("show-version"))
+ setEnv("SHOW_VERSION_IN_GREETING", "yes");
+
+ if (args.hasArg("log-type"))
+ setEnv("LOG_TYPE", args["log-type"]);
+
+ if (args.hasArg("depot"))
+ setEnv("DEPOT", args["depot"]);
+
+ if (args.hasArg("delimiter"))
+ setEnv("DELIMITER", args["delimiter"]);
+}
+
+//----------------------------------------------------------------------
+const string &Session::getLastError(void) const
+{
+ return lastError;
+}
+
+//----------------------------------------------------------------------
+void Session::setLastError(const string &error) const
+{
+ lastError = error;
+}
+
+//----------------------------------------------------------------------
+const string &Session::getResponseCode(void) const
+{
+ return responseCode;
+}
+
+//----------------------------------------------------------------------
+void Session::setResponseCode(const string &code) const
+{
+ responseCode = "[" + code + "] ";
+}
+
+//----------------------------------------------------------------------
+void Session::clearResponseCode(void) const
+{
+ responseCode = "";
+}
+
+//----------------------------------------------------------------------
+pid_t Session::getPid(void)
+{
+ if (pid == 0)
+ pid = getpid();
+
+ return pid;
+}
+
+//----------------------------------------------------------------------
+int Session::timeout() const
+{
+ return state == NONAUTHENTICATED ? AUTH_TIMEOUT : IDLE_TIMEOUT;
+}
+
+//----------------------------------------------------------------------
+bool Session::hasEnv(const string &key) const
+{
+ return getenv(key.c_str()) != 0;
+}
+
+//----------------------------------------------------------------------
+string Session::getEnv(const string &key)
+{
+ char *c = getenv(key.c_str());
+ return c ? c : "";
+}
+
+//----------------------------------------------------------------------
+void Session::setEnv(const string &key, const string &value)
+{
+ string env = key + "=" + value;
+ putenv(strdup(env.c_str()));
+}
diff --git a/src/status.cc b/src/status.cc
new file mode 100644
index 0000000..b9d4f17
--- /dev/null
+++ b/src/status.cc
@@ -0,0 +1,20 @@
+/** --------------------------------------------------------------------
+ * @file status.cc
+ * @brief Implementation of the Status class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include "status.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Status::Status(void)
+{
+}
+
+//------------------------------------------------------------------------
+Status::~Status(void)
+{
+}
diff --git a/src/stdiodevice.cc b/src/stdiodevice.cc
new file mode 100644
index 0000000..9eb5f9c
--- /dev/null
+++ b/src/stdiodevice.cc
@@ -0,0 +1,111 @@
+/** --------------------------------------------------------------------
+ * @file stdiodevice.cc
+ * @brief Implementation of the StdIODevice class
+ * @author Andreas Aardal Hanssen
+ * @date 2003/2023
+ * ---------------------------------------------------------------- **/
+#include "stdiodevice.h"
+#include <string>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+
+using namespace ::std;
+using namespace ::Binc;
+
+//------------------------------------------------------------------------
+StdIODevice::StdIODevice(int f) : IODevice(f)
+{
+}
+
+//------------------------------------------------------------------------
+StdIODevice::~StdIODevice(void)
+{
+}
+
+//------------------------------------------------------------------------
+string StdIODevice::service(void) const
+{
+ return "client";
+}
+
+//------------------------------------------------------------------------
+bool StdIODevice::canRead(void) const
+{
+ size_t bytes;
+ return ioctl(fileno(stdin), FIONREAD, (char *) &bytes) > 0;
+}
+
+//------------------------------------------------------------------------
+bool StdIODevice::waitForWrite(void) const
+{
+ fd_set writeMask;
+ FD_ZERO(&writeMask);
+ FD_SET(fileno(stdout), &writeMask);
+
+ struct timeval tv;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ int result = select(fileno(stdout) + 1, 0, &writeMask, 0, timeout ? &tv : 0);
+ if (result == 0) error = Timeout;
+ return result > 0;
+}
+
+//------------------------------------------------------------------------
+bool StdIODevice::waitForRead(void) const
+{
+ fd_set readMask;
+ FD_ZERO(&readMask);
+ FD_SET(fileno(stdin), &readMask);
+
+ struct timeval tv;
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ int result = select(fileno(stdin) + 1, &readMask, 0, 0, timeout ? &tv : 0);
+ if (result == 0) error = Timeout;
+ return result > 0;
+}
+
+//------------------------------------------------------------------------
+IODevice::WriteResult StdIODevice::write(void)
+{
+ for (;;) {
+ ssize_t wrote = ::write(fileno(stdout), outputBuffer.str().c_str(),
+ outputBuffer.getSize());
+
+ if (wrote == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ return WriteError;
+ }
+
+ outputBuffer.popString(wrote);
+
+ if (wrote == (ssize_t) outputBuffer.getSize())
+ return WriteDone;
+ return WriteWait;
+ }
+}
+
+//------------------------------------------------------------------------
+bool StdIODevice::fillInputBuffer(void)
+{
+ if (!waitForRead())
+ return false;
+
+ char buf[4096];
+ ssize_t red = read(fileno(stdin), buf, sizeof(buf) - 1);
+ if (red <= 0)
+ return false;
+
+ buf[red] = '\0';
+ inputBuffer << buf;
+ return true;
+}
diff --git a/src/syslogdevice.cc b/src/syslogdevice.cc
new file mode 100644
index 0000000..927416e
--- /dev/null
+++ b/src/syslogdevice.cc
@@ -0,0 +1,75 @@
+/** --------------------------------------------------------------------
+ * @file syslogdevice.cc
+ * @brief Implementation of the SyslogDevice class.
+ * @author Andreas Aardal Hanssen
+ * @date 2002, 2003
+ * ----------------------------------------------------------------- **/
+#include "syslogdevice.h"
+#include <string>
+
+#include <syslog.h>
+
+using namespace ::std;
+using namespace ::Binc;
+
+//------------------------------------------------------------------------
+string SyslogDevice::ident;
+
+//------------------------------------------------------------------------
+SyslogDevice::SyslogDevice(int f, const char *i, int o, int fa)
+ : IODevice(f), option(o), facility(fa), priority(LOG_INFO)
+{
+ ident = i;
+ openlog(ident.c_str(), option, facility);
+}
+
+//------------------------------------------------------------------------
+SyslogDevice::~SyslogDevice(void)
+{
+ closelog();
+}
+
+//------------------------------------------------------------------------
+string SyslogDevice::service(void) const
+{
+ return "log";
+}
+
+//------------------------------------------------------------------------
+bool SyslogDevice::waitForWrite(void) const
+{
+ return true;
+}
+
+//------------------------------------------------------------------------
+bool SyslogDevice::waitForRead(void) const
+{
+ return false;
+}
+
+//------------------------------------------------------------------------
+IODevice::WriteResult SyslogDevice::write(void)
+{
+ string out;
+ string::const_iterator i = outputBuffer.str().begin();
+ string::const_iterator ie = outputBuffer.str().end();
+
+ for (; i != ie; ++i) {
+ if (*i == '\n') {
+ syslog(priority, out.c_str(), out.size());
+ out = "";
+ } else if (*i != '\r')
+ out += *i;
+ }
+
+ if (out != "") syslog(priority, out.c_str(), out.size());
+
+ outputBuffer.clear();
+ return WriteDone;
+}
+
+//------------------------------------------------------------------------
+bool SyslogDevice::fillInputBuffer(void)
+{
+ return false;
+}
diff --git a/src/tools.cc b/src/tools.cc
new file mode 100644
index 0000000..24c3757
--- /dev/null
+++ b/src/tools.cc
@@ -0,0 +1,44 @@
+/** --------------------------------------------------------------------
+ * @file tools.cc
+ * @brief Implementation of miscellaneous tools.
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ------------------------------------------------------------------ **/
+#include <errno.h>
+#include <cstring>
+
+#include "tools.h"
+
+using namespace ::std;
+using namespace Binc;
+
+//------------------------------------------------------------------------
+Tools::Tools(void)
+{
+}
+
+//------------------------------------------------------------------------
+Tools &Tools::getInstance(void)
+{
+ static Tools tools;
+ return tools;
+}
+
+//------------------------------------------------------------------------
+void Tools::setenv(const string &key, const string &value) const
+{
+ char *c = strdup((key + "=" + value).c_str());
+ putenv(c);
+}
+
+//------------------------------------------------------------------------
+string Tools::getenv(const string &key) const
+{
+ static const string NIL = "";
+
+ const char *c = ::getenv((char *)key.c_str());
+ if (c == 0)
+ return NIL;
+ else
+ return string(c);
+}