Initial commit.
This commit is contained in:
commit
5d4ed5ed3f
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2001-2011 Eleftherios Chatzimparmpas
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,105 @@
|
||||||
|
DESTDIR =
|
||||||
|
BINDIR = /usr/local/bin
|
||||||
|
SHAREDIR = /usr/local/share/imapfilter
|
||||||
|
MANDIR = /usr/local/man
|
||||||
|
|
||||||
|
INCDIRS = -I/usr/local/include
|
||||||
|
LIBDIRS = -L/usr/local/lib
|
||||||
|
|
||||||
|
MYCFLAGS = -Wall -O
|
||||||
|
MYLDFLAGS =
|
||||||
|
|
||||||
|
DEFS = -DMAKEFILE_SHAREDIR='"$(SHAREDIR)"'
|
||||||
|
|
||||||
|
CFLAGS = $(MYCFLAGS) $(DEFS) $(INCDIRS)
|
||||||
|
LDFLAGS = $(MYLDFLAGS) $(LIBDIRS)
|
||||||
|
|
||||||
|
LIBS = -lm -llua -lpcre -lssl -lcrypto
|
||||||
|
|
||||||
|
MAN_BIN = imapfilter.1
|
||||||
|
MAN_CONFIG = imapfilter_config.5
|
||||||
|
|
||||||
|
COMMON_LUA = common.lua
|
||||||
|
SET_LUA = set.lua
|
||||||
|
REGEX_LUA = regex.lua
|
||||||
|
ACCOUNT_LUA = account.lua
|
||||||
|
MAILBOX_LUA = mailbox.lua
|
||||||
|
MESSAGE_LUA = message.lua
|
||||||
|
OPTIONS_LUA = options.lua
|
||||||
|
AUXILIARY_LUA = auxiliary.lua
|
||||||
|
DEPRECATED_LUA = deprecated.lua
|
||||||
|
|
||||||
|
BIN = imapfilter
|
||||||
|
OBJ = auth.o buffer.o cert.o core.o file.o imap.o imapfilter.o list.o log.o \
|
||||||
|
lua.o memory.o misc.o namespace.o pcre.o regexp.o request.o \
|
||||||
|
response.o session.o signal.o socket.o system.o
|
||||||
|
|
||||||
|
all: $(BIN)
|
||||||
|
|
||||||
|
$(BIN): $(OBJ)
|
||||||
|
$(CC) -o $(BIN) $(LDFLAGS) $(OBJ) $(LIBS)
|
||||||
|
|
||||||
|
$(OBJ): imapfilter.h
|
||||||
|
buffer.o imap.o imapfilter.o namespace.o request.o response.o: buffer.h
|
||||||
|
cert.o file.o imapfilter.o log.o lua.o: pathnames.h
|
||||||
|
imapfilter.o log.o session.o: list.h
|
||||||
|
imapfilter.o regexp.o response.o: regexp.h
|
||||||
|
auth.o cert.o imap.o imapfilter.o log.o request.o response.o session.o \
|
||||||
|
socket.o: session.h
|
||||||
|
imapfilter.o: version.h
|
||||||
|
|
||||||
|
install: $(BIN)
|
||||||
|
if test ! -d $(DESTDIR)$(BINDIR); then \
|
||||||
|
mkdir -p $(DESTDIR)$(BINDIR); fi
|
||||||
|
cp -f $(BIN) $(DESTDIR)$(BINDIR) && \
|
||||||
|
chmod 0755 $(DESTDIR)$(BINDIR)/$(BIN)
|
||||||
|
if test ! -d $(DESTDIR)$(SHAREDIR); then \
|
||||||
|
mkdir -p $(DESTDIR)$(SHAREDIR); fi
|
||||||
|
cp -f $(COMMON_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(COMMON_LUA)
|
||||||
|
cp -f $(SET_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(SET_LUA)
|
||||||
|
cp -f $(REGEX_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(REGEX_LUA)
|
||||||
|
cp -f $(ACCOUNT_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(ACCOUNT_LUA)
|
||||||
|
cp -f $(MAILBOX_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(MAILBOX_LUA)
|
||||||
|
cp -f $(MESSAGE_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(MESSAGE_LUA)
|
||||||
|
cp -f $(OPTIONS_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(OPTIONS_LUA)
|
||||||
|
cp -f $(AUXILIARY_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(AUXILIARY_LUA)
|
||||||
|
cp -f $(DEPRECATED_LUA) $(DESTDIR)$(SHAREDIR) && \
|
||||||
|
chmod 0644 $(DESTDIR)$(SHAREDIR)/$(DEPRECATED_LUA)
|
||||||
|
if test ! -d $(DESTDIR)$(MANDIR)/man1; then \
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man1; fi
|
||||||
|
cp -f $(MAN_BIN) $(DESTDIR)$(MANDIR)/man1 && \
|
||||||
|
chmod 0644 $(DESTDIR)$(MANDIR)/man1/$(MAN_BIN)
|
||||||
|
if test ! -d $(DESTDIR)$(MANDIR)/man5; then \
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man5; fi
|
||||||
|
cp -f $(MAN_CONFIG) $(DESTDIR)$(MANDIR)/man5 && \
|
||||||
|
chmod 0644 $(DESTDIR)$(MANDIR)/man5/$(MAN_CONFIG)
|
||||||
|
|
||||||
|
deinstall:
|
||||||
|
rm -f $(DESTDIR)$(BINDIR)/$(BIN) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(COMMON_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(SET_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(REGEX_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(ACCOUNT_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(MAILBOX_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(MESSAGE_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(OPTIONS_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(AUXILIARY_LUA) \
|
||||||
|
$(DESTDIR)$(SHAREDIR)/$(DEPRECATED_LUA) \
|
||||||
|
$(DESTDIR)$(MANDIR)/man1/$(MAN_BIN) \
|
||||||
|
$(DESTDIR)$(MANDIR)/man5/$(MAN_CONFIG)
|
||||||
|
|
||||||
|
uninstall: deinstall
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJ) $(BIN) imapfilter.core core *.orig *.BAK *~
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
@if test -f .Makefile; then mv -f .Makefile Makefile; fi
|
|
@ -0,0 +1,192 @@
|
||||||
|
IMAPFilter 2.2.3 - 6 Mar 2011
|
||||||
|
- Project moved to GitHub.
|
||||||
|
- The next UID is returned as an additional return value of check_status().
|
||||||
|
- All processing methods now return a boolean based on their success.
|
||||||
|
- Bug fix; a lost connection is now handled better by trying to reconnect.
|
||||||
|
- Bug fix; in some cases in IDLE a message had arrived but was ignored.
|
||||||
|
- Bug fix; in some servers the initial IDLE reply wasn't handled correctly.
|
||||||
|
- Bug fix; typo errors in the documentation.
|
||||||
|
|
||||||
|
IMAPFilter 2.2.2 - 23 Jan 2010
|
||||||
|
- Bug fix; a couple of errors in the extending examples file.
|
||||||
|
|
||||||
|
IMAPFilter 2.2.1 - 20 Jan 2010
|
||||||
|
- A global option for the IDLE refreshing interval was added.
|
||||||
|
- Bug fix; more detailed reporting when SSL socket errors occur.
|
||||||
|
|
||||||
|
IMAPFilter 2.2 - 30 Dec 2009
|
||||||
|
- Support for combining searching methods in multiple mailboxes at the same
|
||||||
|
or different accounts and processing of the results in bulk.
|
||||||
|
- Support for meta-searching that allows searching on the previous searching
|
||||||
|
results.
|
||||||
|
- The processing and fetching methods were enhanced to reflect the new
|
||||||
|
changes and the documentation was updated.
|
||||||
|
- Global options for the message cache and the certificates were added.
|
||||||
|
- Bug fix; questions for certificates are not asked while in daemon mode, but
|
||||||
|
instead an error is printed.
|
||||||
|
* A different format is used for the returned structures of the searching
|
||||||
|
methods, due to the introduction of multiple mailbox searching and
|
||||||
|
meta-searching, and thus any configuration files that rely on them should
|
||||||
|
be updated. Consequently, the processing and fetching methods have been
|
||||||
|
also enhanced and the relevant documentation updated, and while these
|
||||||
|
changes are backwards compatible, an update of the configuration file is
|
||||||
|
still recommended.
|
||||||
|
|
||||||
|
IMAPFilter 2.1.2 - 3 Dec 2009
|
||||||
|
- Bug fix; cache for message parts didn't work correctly.
|
||||||
|
- Bug fix; documentation error.
|
||||||
|
|
||||||
|
IMAPFilter 2.1.1 - 24 Nov 2009
|
||||||
|
- Bug fix; global option timeout and enter_idle() didn't play well together.
|
||||||
|
|
||||||
|
IMAPFilter 2.1 - 23 Nov 2009
|
||||||
|
- Support for the IMAP IDLE extension (RFC 2177) through the enter_idle()
|
||||||
|
method.
|
||||||
|
- Support for fetching of a message's body structure through the
|
||||||
|
fetch_structure() method, and of a message's specific body part through the
|
||||||
|
fetch_parts() method.
|
||||||
|
- Addition of a global option that controls the character set used for all
|
||||||
|
the searching methods.
|
||||||
|
- Bug fix; fetching of non-existent messages.
|
||||||
|
- Bug fix; no trailing end-of-line characters in the results of
|
||||||
|
fetch_fields().
|
||||||
|
|
||||||
|
IMAPFilter 2.0.11 - 20 Sep 2009
|
||||||
|
- Bug fix; fetching of messages with empty body.
|
||||||
|
- Workaround for problematic IMAP server sending non-compliant mailbox status
|
||||||
|
information.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.10 - 16 Feb 2008
|
||||||
|
- Bug fix; failed a great number (tens of thousands) of commands were
|
||||||
|
exchanged with an IMAP server.
|
||||||
|
- Bug fix; failed to fetch the body of some messages in some extremely rare
|
||||||
|
occasions.
|
||||||
|
- Bug fix; the description for the contain_header() method was clarified.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.9 - 26 Dec 2007
|
||||||
|
- Bug fix; the match_*() methods failed to match messages.
|
||||||
|
- Bug fix; the match_*() methods failed with an error when no messages
|
||||||
|
matched.
|
||||||
|
- Bug fix; note added in the documentation about the need to use double
|
||||||
|
backslashes inside of regular expression patterns.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.8 - 23 Dec 2007
|
||||||
|
- Bug fix; on some platforms it is necessary to link against the math library.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.7 - 22 Dec 2007
|
||||||
|
- Bug fix; the match_*() methods failed with an error message.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.6 - 7 Oct 2007
|
||||||
|
- Bug fix; the search query that was sent with the select_all() method had an
|
||||||
|
incorrect format and this caused an error in some mail servers.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.5 - 4 Oct 2007
|
||||||
|
- Bug fix; an error in the sample extensions file.
|
||||||
|
- Bug fix; typo errors in the manual page.
|
||||||
|
- The documentation was updated with details and examples on how to access
|
||||||
|
mailboxes inside folders.
|
||||||
|
- Examples were added on how to define composite filters that include
|
||||||
|
multiple searching rules.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.4 - 27 Sep 2007
|
||||||
|
- Bug fix; the send_query() method didn't return the special form of table
|
||||||
|
that the rest of the searching methods did.
|
||||||
|
- An additional searching method has been added to search for keyword flags
|
||||||
|
set.
|
||||||
|
- A new variable that was added to the Makefile makes it possible to set an
|
||||||
|
alternative environment for the installation path.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.3 - 27 Jul 2007
|
||||||
|
- Bug fix; part of the program's functionality didn't seem to work at all.
|
||||||
|
(did nothing), due to problem when providing the results from searching
|
||||||
|
methods to processing methods.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.2 - 30 Jun 2007
|
||||||
|
- Bug fix; message cache problem due to non-use of message UIDs.
|
||||||
|
|
||||||
|
IMAPFilter 2.0.1 - 29 Jun 2007
|
||||||
|
- Bug fix; character set problem with 1.x configuration files.
|
||||||
|
- Bug fix; typo errors in the documentation.
|
||||||
|
|
||||||
|
IMAPFilter 2.0 - 27 Jun 2007
|
||||||
|
- New, more powerful, feature rich and yet simpler configuration file.
|
||||||
|
- Easier object oriented view of accounts and mailboxes.
|
||||||
|
- Simpler approach to filters, with infix logical or/and/not operators.
|
||||||
|
- No more need to mess with server search queries.
|
||||||
|
- More and simpler functions instead of few and complicated ones.
|
||||||
|
- More feature complete interface that can now even manipulate mailboxes.
|
||||||
|
- Regular expressions integrated into the searching interface.
|
||||||
|
- Effective caching subsystem when fetching message parts.
|
||||||
|
- Can still read old version 1.x configuration files for compatibility.
|
||||||
|
- Lua 5.1 and the PCRE library are now requirements.
|
||||||
|
* The configuration file format has changed. The new format is not backwards
|
||||||
|
compatible, and thus it should not be mixed with the old format.
|
||||||
|
Nevertheless, configuration files that employ the old, and now deprecated,
|
||||||
|
format can still be read and executed as before.
|
||||||
|
|
||||||
|
IMAPFilter 1.3 - 13 Feb 2007
|
||||||
|
- Perl Compatible Regular Expression (PCRE) support.
|
||||||
|
- Compile against Lua 5.1 by default.
|
||||||
|
- Bug fix; program fault in some cases and when namespace prefix was empty.
|
||||||
|
- Bug fix; program fault on some platforms when running in verbose mode.
|
||||||
|
|
||||||
|
IMAPFilter 1.2.2 - 1 Aug 2006
|
||||||
|
- Bug fix; a mix up of connections could happen in certain circumstances,
|
||||||
|
when a hostname and/or username was a prefix of another hostname and/or
|
||||||
|
username respectively, or when the same hostname and username was used to
|
||||||
|
connect to a different port.
|
||||||
|
- Bug fix; the list()/lsub() functions parsed mailboxes/folders whose names
|
||||||
|
contained spaces incorrectly.
|
||||||
|
- The list() function now does not return the folder itself, when listing
|
||||||
|
mailboxes inside a specific folder.
|
||||||
|
- It is now possible to define new user keywords for messages inside a
|
||||||
|
mailbox, apart from the standard system flags.
|
||||||
|
|
||||||
|
IMAPFilter 1.2.1 - 9 Mar 2006
|
||||||
|
- Buf fix; program fault when using the fetch*() family of functions.
|
||||||
|
|
||||||
|
IMAPFilter 1.2 - 2 Mar 2006
|
||||||
|
- IPv6 support.
|
||||||
|
- Lua 5.1 compatibility.
|
||||||
|
- Bug fix; handle messages containing binary data.
|
||||||
|
- Bug fix; problems with CPU utilisation when the inactivity timeout timer
|
||||||
|
was set.
|
||||||
|
|
||||||
|
IMAPFilter 1.1.1 - 11 Nov 2005
|
||||||
|
- Bug fix; minor memory leak.
|
||||||
|
- Bug fix; on some systems, failure resulted while disconnecting from all the
|
||||||
|
servers, during the shutdown phase just before exiting.
|
||||||
|
|
||||||
|
IMAPFilter 1.1 - 24 Aug 2005
|
||||||
|
- Addition of the list() and lsub() commands, that make it possible to get a
|
||||||
|
list of the available mailboxes or only of those that are subscribed.
|
||||||
|
Implementation of the IMAP LIST/LSUB commands, with additional support for
|
||||||
|
the IMAP CHILDREN (RFC 3348) and IMAP NAMESPACE (RFC 2342) extensions.
|
||||||
|
- New program option to execute a string from the command line, without
|
||||||
|
loading a configuration file.
|
||||||
|
- New program option to enter interactive mode after executing the
|
||||||
|
configuration file or the command line.
|
||||||
|
- Servers that reply with multiple SEARCH responses are taken into
|
||||||
|
consideration.
|
||||||
|
- Bug fix; failure to parse the response to fetchfast() that some mail
|
||||||
|
servers sent.
|
||||||
|
- Bug fix; in some systems and when in debug mode, an empty namespace caused
|
||||||
|
program fault.
|
||||||
|
|
||||||
|
IMAPFilter 1.0.1 - 22 Aug 2004
|
||||||
|
- Bug fix; in some cases processing of messages with an empty body caused
|
||||||
|
failure.
|
||||||
|
- Bug fix; an invalid namespace prefix was inserted in mailbox names of some
|
||||||
|
mail servers.
|
||||||
|
- Unique message identifiers are now used by default, instead of message
|
||||||
|
sequence numbers, when accessing messages in a mailbox.
|
||||||
|
- Sequence set ranges are generated and sent to the mail server instead of
|
||||||
|
enumerations, when this is possible.
|
||||||
|
- The client now limits the length of the command lines it generates to
|
||||||
|
approximately 1000 octets, by splitting the request into multiple commands.
|
||||||
|
- Systems that have no limit on the number of bytes in a pathname are now
|
||||||
|
considered.
|
||||||
|
- Debug files are now written in $HOME/.imapfilter/ instead of /tmp/.
|
||||||
|
|
||||||
|
IMAPFilter 1.0 - 23 May 2004
|
||||||
|
- Initial release of IMAPFilter with extension language Lua.
|
|
@ -0,0 +1,60 @@
|
||||||
|
IMAPFilter
|
||||||
|
|
||||||
|
Description
|
||||||
|
|
||||||
|
IMAPFilter is a mail filtering utility. It connects to remote mail servers
|
||||||
|
using the Internet Message Access Protocol (IMAP), sends searching queries to
|
||||||
|
the server and processes mailboxes based on the results. It can be used to
|
||||||
|
delete, copy, move, flag, etc. messages residing in mailboxes at the same or
|
||||||
|
different mail servers. The 4rev1 and 4 versions of the IMAP protocol are
|
||||||
|
supported.
|
||||||
|
|
||||||
|
IMAPFilter uses the Lua programming language as a configuration and extension
|
||||||
|
language.
|
||||||
|
|
||||||
|
|
||||||
|
Website
|
||||||
|
|
||||||
|
http://github.com/lefcha/imapfilter
|
||||||
|
|
||||||
|
|
||||||
|
Changes
|
||||||
|
|
||||||
|
All the changes in each new release up to the latest are in the NEWS file.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
|
||||||
|
Compile time requirements are Lua (5.1 or later), the PCRE library, and
|
||||||
|
optionally the OpenSSL library (for SSL/TLS and CRAM-MD5 support).
|
||||||
|
|
||||||
|
Optionally, configure installation options using the supplied shell script.
|
||||||
|
To see usage details:
|
||||||
|
|
||||||
|
./configure -h
|
||||||
|
|
||||||
|
Compile and install the program:
|
||||||
|
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
|
||||||
|
There is detailed description of the command line options in the
|
||||||
|
imapfilter(1) manual page, and of the configuration file format in the
|
||||||
|
imapfilter_config(5) manual page.
|
||||||
|
|
||||||
|
There are also more configuration examples in the sample.config.lua file, and
|
||||||
|
some examples of extensions through Lua in the sample.extend.lua file.
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
|
||||||
|
Released under the terms and conditions of the MIT/X11 license, included in
|
||||||
|
the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
|
Authors
|
||||||
|
|
||||||
|
See AUTHORS file.
|
|
@ -0,0 +1,239 @@
|
||||||
|
-- The Account class represents an IMAP account.
|
||||||
|
|
||||||
|
Account = {}
|
||||||
|
IMAP = Account
|
||||||
|
imap = Account
|
||||||
|
|
||||||
|
Account._mt = {}
|
||||||
|
setmetatable(Account, Account._mt)
|
||||||
|
|
||||||
|
|
||||||
|
Account._mt.__call = function (self, arg)
|
||||||
|
_check_required(arg.server, 'string')
|
||||||
|
_check_required(arg.username, 'string')
|
||||||
|
_check_optional(arg.password, 'string')
|
||||||
|
_check_optional(arg.port, 'number')
|
||||||
|
_check_optional(arg.ssl, 'string')
|
||||||
|
|
||||||
|
local object = {}
|
||||||
|
|
||||||
|
object._type = 'account'
|
||||||
|
|
||||||
|
for key, value in pairs(Account) do
|
||||||
|
if (type(value) == 'function') then
|
||||||
|
object[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
object._mt = {}
|
||||||
|
object._mt.__index = object._attach_mailbox
|
||||||
|
setmetatable(object, object._mt)
|
||||||
|
|
||||||
|
object._imap = arg
|
||||||
|
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Account._login_user(self)
|
||||||
|
if (self._imap.password == nil) then
|
||||||
|
self._imap.password = get_password('Enter password for ' ..
|
||||||
|
self._imap.username .. '@' .. self._imap.server .. ': ')
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.login(self._imap)
|
||||||
|
|
||||||
|
if (r == nil) then
|
||||||
|
return true
|
||||||
|
elseif (r == true) then
|
||||||
|
self._mailbox = nil
|
||||||
|
return true
|
||||||
|
elseif (r == false) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Account._attach_mailbox(self, mailbox)
|
||||||
|
self[mailbox] = Mailbox(self, mailbox)
|
||||||
|
return self[mailbox]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account._detach_mailbox(self, mailbox)
|
||||||
|
self[mailbox] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Account.list_all(self, folder, mbox)
|
||||||
|
_check_optional(folder, 'string')
|
||||||
|
_check_optional(mbox, 'string')
|
||||||
|
|
||||||
|
if (folder == nil) then
|
||||||
|
folder = ''
|
||||||
|
else
|
||||||
|
if (type(options) == 'table' and options.namespace == true) then
|
||||||
|
if (folder == '/') then
|
||||||
|
folder = ''
|
||||||
|
end
|
||||||
|
if (folder ~= '') then
|
||||||
|
folder = folder .. '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (mbox == nil) then
|
||||||
|
mbox = '%'
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, mailboxes, folders = ifcore.list(self._imap, '', folder .. mbox)
|
||||||
|
|
||||||
|
local m = {}
|
||||||
|
for s in string.gmatch(mailboxes, '%C+') do
|
||||||
|
table.insert(m, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = {}
|
||||||
|
for s in string.gmatch(folders, '%C+') do
|
||||||
|
if s ~= folder and s ~= folder .. '/' then
|
||||||
|
table.insert(f, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m, f
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account.list_subscribed(self, folder, mbox)
|
||||||
|
_check_optional(folder, 'string')
|
||||||
|
_check_optional(mbox, 'string')
|
||||||
|
|
||||||
|
if (folder == nil) then
|
||||||
|
folder = ''
|
||||||
|
else
|
||||||
|
if (type(options) == 'table' and options.namespace == true) then
|
||||||
|
if (folder == '/') then
|
||||||
|
folder = ''
|
||||||
|
end
|
||||||
|
if (folder ~= '') then
|
||||||
|
folder = folder .. '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (mbox == nil) then
|
||||||
|
mbox = '*'
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, mailboxes, folders = ifcore.lsub(self._imap, '', folder .. mbox)
|
||||||
|
|
||||||
|
local m = {}
|
||||||
|
for s in string.gmatch(mailboxes, '%C+') do
|
||||||
|
table.insert(m, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = {}
|
||||||
|
for s in string.gmatch(folders, '%C+') do
|
||||||
|
if s ~= folder and s ~= folder .. '/' then
|
||||||
|
table.insert(f, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m, f
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Account.create_mailbox(self, name)
|
||||||
|
_check_required(name, 'string')
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.create(self._imap, name)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("Created mailbox %s@%s/%s.",
|
||||||
|
self._imap.username, self._imap.server, name))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account.delete_mailbox(self, name)
|
||||||
|
_check_required(name, 'string')
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.delete(self._imap, name)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("Deleted mailbox %s@%s/%s.",
|
||||||
|
self._imap.username, self._imap.server, name))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account.rename_mailbox(self, oldname, newname)
|
||||||
|
_check_required(oldname, 'string')
|
||||||
|
_check_required(newname, 'string')
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.rename(self._imap, oldname, newname)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("Renamed mailbox %s@%s/%s to %s@%s/%s.",
|
||||||
|
self._imap.username, self._imap.server, oldname,
|
||||||
|
self._imap.username, self._imap.server, newname))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account.subscribe_mailbox(self, name)
|
||||||
|
_check_required(name, 'string')
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.subscribe(self._imap, name)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("Subscribed mailbox %s@%s/%s.",
|
||||||
|
self._imap.username, self._imap.server, name))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Account.unsubscribe_mailbox(self, name)
|
||||||
|
_check_required(name, 'string')
|
||||||
|
|
||||||
|
if (self._login_user(self) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.unsubscribe(self._imap, name)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("Unsubscribed mailbox %s@%s/%s.",
|
||||||
|
self._imap.username, self._imap.server, name))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Account._mt.__index = function () end
|
||||||
|
Account._mt.__newindex = function () end
|
|
@ -0,0 +1,73 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Authenticate to the server with the Challenge-Response Authentication
|
||||||
|
* Mechanism (CRAM). The authentication type associated with CRAM is
|
||||||
|
* "CRAM-MD5".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
auth_cram_md5(session *ssn, const char *user, const char *pass)
|
||||||
|
{
|
||||||
|
int t;
|
||||||
|
size_t n;
|
||||||
|
unsigned int i;
|
||||||
|
unsigned char *chal, *resp, *out, *buf;
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE], mdhex[EVP_MAX_MD_SIZE * 2 + 1];
|
||||||
|
unsigned int mdlen;
|
||||||
|
HMAC_CTX hmac;
|
||||||
|
|
||||||
|
if ((t = imap_authenticate(ssn, "CRAM-MD5")) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (response_authenticate(ssn, t, &chal) ==
|
||||||
|
STATUS_RESPONSE_CONTINUE) {
|
||||||
|
n = strlen((char *)(chal)) * 3 / 4 + 1;
|
||||||
|
resp = (unsigned char *)xmalloc(n * sizeof(char));
|
||||||
|
memset(resp, 0, n);
|
||||||
|
|
||||||
|
EVP_DecodeBlock(resp, chal, strlen((char *)(chal)));
|
||||||
|
|
||||||
|
HMAC_Init(&hmac, (const unsigned char *)pass, strlen(pass),
|
||||||
|
EVP_md5());
|
||||||
|
HMAC_Update(&hmac, resp, strlen((char *)(resp)));
|
||||||
|
HMAC_Final(&hmac, md, &mdlen);
|
||||||
|
|
||||||
|
xfree(chal);
|
||||||
|
xfree(resp);
|
||||||
|
|
||||||
|
for (i = 0; i < mdlen; i++)
|
||||||
|
snprintf((char *)(mdhex) + i * 2, mdlen * 2 - i * 2 + 1,
|
||||||
|
"%02x", md[i]);
|
||||||
|
mdhex[mdlen * 2] = '\0';
|
||||||
|
|
||||||
|
n = strlen(user) + 1 + strlen((char *)(mdhex)) + 1;
|
||||||
|
buf = (unsigned char *)xmalloc(n * sizeof(unsigned char));
|
||||||
|
memset(buf, 0, n);
|
||||||
|
|
||||||
|
snprintf((char *)(buf), n, "%s %s", user, mdhex);
|
||||||
|
|
||||||
|
n = (strlen((char *)(buf)) + 3) * 4 / 3 + 1;
|
||||||
|
out = (unsigned char *)xmalloc(n * sizeof(unsigned char));
|
||||||
|
memset(out, 0, n);
|
||||||
|
|
||||||
|
EVP_EncodeBlock(out, buf, strlen((char *)(buf)));
|
||||||
|
|
||||||
|
imap_continuation(ssn, (char *)(out), strlen((char *)(out)));
|
||||||
|
|
||||||
|
xfree(buf);
|
||||||
|
xfree(out);
|
||||||
|
} else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return response_authenticate(ssn, t, NULL);
|
||||||
|
}
|
||||||
|
#endif /* NO_CRAMMD5 */
|
|
@ -0,0 +1,66 @@
|
||||||
|
-- Miscellaneous auxiliary functions.
|
||||||
|
|
||||||
|
function form_date(days)
|
||||||
|
_check_required(days, 'number')
|
||||||
|
|
||||||
|
return os.date("%d-%b-%Y", os.time() - days * 60 * 60 * 24)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function get_password(prompt)
|
||||||
|
_check_optional(prompt, 'string')
|
||||||
|
|
||||||
|
if (prompt ~= nil) then
|
||||||
|
io.write(prompt)
|
||||||
|
else
|
||||||
|
io.write('Enter password: ')
|
||||||
|
end
|
||||||
|
|
||||||
|
ifsys.noecho()
|
||||||
|
local p = io.read()
|
||||||
|
ifsys.echo()
|
||||||
|
|
||||||
|
return p
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function pipe_to(command, data)
|
||||||
|
_check_required(command, 'string')
|
||||||
|
_check_required(data, 'string')
|
||||||
|
|
||||||
|
f = ifsys.popen(command, "w")
|
||||||
|
|
||||||
|
ifsys.write(f, data)
|
||||||
|
|
||||||
|
return ifsys.pclose(f)
|
||||||
|
end
|
||||||
|
|
||||||
|
function pipe_from(command)
|
||||||
|
_check_required(command, 'string')
|
||||||
|
|
||||||
|
f = ifsys.popen(command, "r")
|
||||||
|
|
||||||
|
local string = ''
|
||||||
|
while (true) do
|
||||||
|
s = ifsys.read(f)
|
||||||
|
if (s ~= nil) then
|
||||||
|
string = string .. s
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ifsys.pclose(f), string
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function become_daemon(interval, commands)
|
||||||
|
_check_required(interval, 'number')
|
||||||
|
_check_required(commands, 'function')
|
||||||
|
|
||||||
|
ifsys.daemon()
|
||||||
|
|
||||||
|
repeat
|
||||||
|
pcall(commands)
|
||||||
|
until (ifsys.sleep(interval) ~= 0)
|
||||||
|
end
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize buffer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
buffer_init(buffer *buf, size_t n)
|
||||||
|
{
|
||||||
|
|
||||||
|
buf->data = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
*buf->data = '\0';
|
||||||
|
buf->len = 0;
|
||||||
|
buf->size = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free allocated memory of buffer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
buffer_free(buffer *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!buf->data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
xfree(buf->data);
|
||||||
|
buf->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset buffer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
buffer_reset(buffer *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
*buf->data = '\0';
|
||||||
|
buf->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the buffer has enough space to store data and reallocate memory if
|
||||||
|
* needed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
buffer_check(buffer *buf, size_t n)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (n > buf->size) {
|
||||||
|
buf->size *= 2;
|
||||||
|
buf->data = (char *)xrealloc(buf->data, buf->size + 1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef BUFFER_H
|
||||||
|
#define BUFFER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Temporary buffer. */
|
||||||
|
typedef struct buffer {
|
||||||
|
char *data; /* Text or binary data. */
|
||||||
|
size_t len; /* Length of text or binary data. */
|
||||||
|
size_t size; /* Maximum size of data. */
|
||||||
|
} buffer;
|
||||||
|
|
||||||
|
|
||||||
|
/* buffer.c */
|
||||||
|
void buffer_init(buffer *buf, size_t n);
|
||||||
|
void buffer_free(buffer *buf);
|
||||||
|
void buffer_reset(buffer *buf);
|
||||||
|
void buffer_check(buffer *buf, size_t n);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* BUFFER_H */
|
|
@ -0,0 +1,235 @@
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
|
||||||
|
#include <openssl/x509.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern environment env;
|
||||||
|
|
||||||
|
|
||||||
|
int check_cert(X509 *pcert, unsigned char *pmd, unsigned int *pmdlen);
|
||||||
|
void print_cert(X509 *cert, unsigned char *md, unsigned int *mdlen);
|
||||||
|
int write_cert(X509 *cert);
|
||||||
|
int mismatch_cert(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get SSL/TLS certificate check it, maybe ask user about it and act
|
||||||
|
* accordingly.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
get_cert(session *ssn)
|
||||||
|
{
|
||||||
|
X509 *cert;
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int mdlen;
|
||||||
|
|
||||||
|
mdlen = 0;
|
||||||
|
|
||||||
|
if (!(cert = SSL_get_peer_certificate(ssn->ssl)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!(X509_digest(cert, EVP_md5(), md, &mdlen)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (check_cert(cert, md, &mdlen)) {
|
||||||
|
case 0:
|
||||||
|
if (isatty(STDIN_FILENO) == 0)
|
||||||
|
fatal(ERROR_CERTIFICATE, "%s\n",
|
||||||
|
"can't accept certificate in non-interactive mode");
|
||||||
|
print_cert(cert, md, &mdlen);
|
||||||
|
if (write_cert(cert) == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
if (isatty(STDIN_FILENO) == 0)
|
||||||
|
fatal(ERROR_CERTIFICATE, "%s\n",
|
||||||
|
"certificate mismatch in non-interactive mode");
|
||||||
|
print_cert(cert, md, &mdlen);
|
||||||
|
if (mismatch_cert() == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_free(cert);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
X509_free(cert);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the SSL/TLS certificate exists in the certificates file.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_cert(X509 *pcert, unsigned char *pmd, unsigned int *pmdlen)
|
||||||
|
{
|
||||||
|
int n, r;
|
||||||
|
FILE *fd;
|
||||||
|
char b;
|
||||||
|
char *certf;
|
||||||
|
X509 *cert;
|
||||||
|
unsigned char md[EVP_MAX_MD_SIZE];
|
||||||
|
unsigned int mdlen;
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
cert = NULL;
|
||||||
|
|
||||||
|
n = snprintf(&b, 1, "%s/%s", env.home, PATHNAME_CERTS);
|
||||||
|
|
||||||
|
if (env.pathmax != -1 && n > env.pathmax)
|
||||||
|
fatal(ERROR_PATHNAME,
|
||||||
|
"pathname limit %ld exceeded: %d\n", env.pathmax, n);
|
||||||
|
|
||||||
|
certf = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(certf, n + 1, "%s/%s", env.home, PATHNAME_CERTS);
|
||||||
|
|
||||||
|
if (!exists_file(certf)) {
|
||||||
|
xfree(certf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = fopen(certf, "r");
|
||||||
|
|
||||||
|
xfree(certf);
|
||||||
|
|
||||||
|
if (fd == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while ((cert = PEM_read_X509(fd, &cert, NULL, NULL)) != NULL) {
|
||||||
|
if (X509_subject_name_cmp(cert, pcert) != 0 ||
|
||||||
|
X509_issuer_name_cmp(cert, pcert) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!X509_digest(cert, EVP_md5(), md, &mdlen) ||
|
||||||
|
*pmdlen != mdlen)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memcmp(pmd, md, mdlen) != 0) {
|
||||||
|
r = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
r = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fd);
|
||||||
|
X509_free(cert);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print information about the SSL/TLS certificate.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
print_cert(X509 *cert, unsigned char *md, unsigned int *mdlen)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
c = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
|
||||||
|
printf("Server certificate subject: %s\n", c);
|
||||||
|
xfree(c);
|
||||||
|
|
||||||
|
c = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
|
||||||
|
printf("Server certificate issuer: %s\n", c);
|
||||||
|
xfree(c);
|
||||||
|
|
||||||
|
printf("Server key fingerprint: ");
|
||||||
|
for (i = 0; i < *mdlen; i++)
|
||||||
|
printf(i != *mdlen - 1 ? "%02X:" : "%02X\n", md[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write the SSL/TLS certificate after asking the user to accept/reject it.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
write_cert(X509 *cert)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
FILE *fd;
|
||||||
|
char b, c, buf[64];
|
||||||
|
char *certf;
|
||||||
|
|
||||||
|
do {
|
||||||
|
printf("(R)eject, accept (t)emporarily or "
|
||||||
|
"accept (p)ermanently? ");
|
||||||
|
if (fgets(buf, sizeof(buf), stdin) == NULL)
|
||||||
|
return -1;
|
||||||
|
c = tolower((int)(*buf));
|
||||||
|
} while (c != 'r' && c != 't' && c != 'p');
|
||||||
|
|
||||||
|
if (c == 'r')
|
||||||
|
return -1;
|
||||||
|
else if (c == 't')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
n = snprintf(&b, 1, "%s/%s", env.home, PATHNAME_CERTS);
|
||||||
|
|
||||||
|
if (env.pathmax != -1 && n > env.pathmax)
|
||||||
|
fatal(ERROR_PATHNAME,
|
||||||
|
"pathname limit %ld exceeded: %d\n", env.pathmax, n);
|
||||||
|
|
||||||
|
certf = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(certf, n + 1, "%s/%s", env.home, PATHNAME_CERTS);
|
||||||
|
|
||||||
|
create_file(certf, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
|
fd = fopen(certf, "a");
|
||||||
|
|
||||||
|
xfree(certf);
|
||||||
|
|
||||||
|
if (fd == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
PEM_write_X509(fd, cert);
|
||||||
|
|
||||||
|
fclose(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ask user to proceed, while a fingerprint mismatch in the SSL/TLS certificate
|
||||||
|
* was found.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
mismatch_cert(void)
|
||||||
|
{
|
||||||
|
char c, buf[64];
|
||||||
|
|
||||||
|
do {
|
||||||
|
printf("ATTENTION: SSL/TLS certificate fingerprint mismatch.\n"
|
||||||
|
"Proceed with the connection (y/n)? ");
|
||||||
|
if (fgets(buf, sizeof(buf), stdin) == NULL)
|
||||||
|
return -1;
|
||||||
|
c = tolower((int)(*buf));
|
||||||
|
} while (c != 'y' && c != 'n');
|
||||||
|
|
||||||
|
if (c == 'y')
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* NO_SSLTLS */
|
|
@ -0,0 +1,510 @@
|
||||||
|
-- Common functions for all classes.
|
||||||
|
|
||||||
|
function _check_required(arg, argtype)
|
||||||
|
if (type(arg) == 'nil') then
|
||||||
|
error('required argument left out', 3)
|
||||||
|
else
|
||||||
|
_check_optional(arg, argtype)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _check_optional(arg, argtype)
|
||||||
|
if (type(arg) ~= 'nil') then
|
||||||
|
if (type(argtype) == 'string') then
|
||||||
|
if (type(arg) ~= argtype) then
|
||||||
|
error(argtype .. ' argument expected, got ' .. type(arg), 3)
|
||||||
|
end
|
||||||
|
elseif (type(argtype) == 'table') then
|
||||||
|
local b = false
|
||||||
|
for _, t in ipairs(argtype) do
|
||||||
|
if (type(arg) == t) then
|
||||||
|
b = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (b == false) then
|
||||||
|
error(argtype .. ' argument expected, got ' .. type(arg), 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _extract_mailboxes(messages)
|
||||||
|
local t = {}
|
||||||
|
for _, v in ipairs(messages) do
|
||||||
|
b, _ = unpack(v)
|
||||||
|
t[b] = true
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
function _extract_messages(mailbox, messages)
|
||||||
|
local t = {}
|
||||||
|
for _, v in ipairs(messages) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if mailbox == b then
|
||||||
|
table.insert(t, m)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _make_range(messages)
|
||||||
|
for _, m in ipairs(messages) do
|
||||||
|
if type(m) ~= 'number' then
|
||||||
|
return messages
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(messages)
|
||||||
|
|
||||||
|
local t = {}
|
||||||
|
local a, z
|
||||||
|
for _, m in ipairs(messages) do
|
||||||
|
if a == nil or z == nil then
|
||||||
|
a = m
|
||||||
|
z = m
|
||||||
|
else
|
||||||
|
if m == z + 1 then
|
||||||
|
z = m
|
||||||
|
else
|
||||||
|
if a == z then
|
||||||
|
table.insert(t, tostring(a))
|
||||||
|
else
|
||||||
|
table.insert(t, a .. ':' .. z)
|
||||||
|
end
|
||||||
|
a = m
|
||||||
|
z = m
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if a == z then
|
||||||
|
table.insert(t, tostring(a))
|
||||||
|
else
|
||||||
|
table.insert(t, a .. ':' .. z)
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _make_query(criteria)
|
||||||
|
local s = 'ALL '
|
||||||
|
|
||||||
|
if (criteria.invert ~= true) then
|
||||||
|
for ka, va in ipairs(criteria) do
|
||||||
|
if (type(va) == 'string') then
|
||||||
|
s = s .. '' .. '(' .. va .. ')' .. ' '
|
||||||
|
elseif (type(va) == 'table') then
|
||||||
|
for i = 1, #va - 1 do
|
||||||
|
s = s .. 'OR '
|
||||||
|
end
|
||||||
|
for ko, vo in ipairs(va) do
|
||||||
|
if (type(vo) ~= 'string') then
|
||||||
|
error('filter rule not a string', 2)
|
||||||
|
end
|
||||||
|
s = s .. '(' .. vo .. ') '
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error('filter element not a string or table', 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i = 1, #criteria - 1 do
|
||||||
|
s = s .. 'OR '
|
||||||
|
end
|
||||||
|
for ko, vo in ipairs(criteria) do
|
||||||
|
if (type(vo) == 'string') then
|
||||||
|
s = s .. '' .. '(' .. vo .. ')' .. ' '
|
||||||
|
elseif (type(vo) == 'table') then
|
||||||
|
s = s .. '('
|
||||||
|
for ka, va in ipairs(vo) do
|
||||||
|
if (type(va) ~= 'string') then
|
||||||
|
error('filter rule not a string', 2)
|
||||||
|
end
|
||||||
|
s = s .. va .. ' '
|
||||||
|
end
|
||||||
|
s = string.gsub(s, '(.+) ', '%1')
|
||||||
|
s = s .. ') '
|
||||||
|
else
|
||||||
|
error('filter rule not a string or table', 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s = string.gsub(s, '(.+) ', '%1')
|
||||||
|
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _parse_structure(b)
|
||||||
|
local bs = _parse_body(b)
|
||||||
|
if not bs then error(b.i .. ':' .. b.s) end
|
||||||
|
return _parse_normalize(bs)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_normalize(bs, key, val)
|
||||||
|
if not key or not val then
|
||||||
|
if #bs == 0 then
|
||||||
|
return { ['1'] = bs }
|
||||||
|
else
|
||||||
|
for k, v in pairs(bs) do
|
||||||
|
if type(k) ~= 'number' then bs[k] = nil end
|
||||||
|
end
|
||||||
|
for k, v in ipairs(bs) do
|
||||||
|
_parse_normalize(bs, k, v)
|
||||||
|
bs[tostring(k)] = v
|
||||||
|
bs[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return bs
|
||||||
|
else
|
||||||
|
for k, v in ipairs(val) do
|
||||||
|
local new = tostring(key) .. '.' .. tostring(k)
|
||||||
|
bs[new] = v
|
||||||
|
_parse_normalize(bs, new, v)
|
||||||
|
val[k] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_body(b)
|
||||||
|
if not _parse_lpar(b) then return end
|
||||||
|
|
||||||
|
local bp
|
||||||
|
if _parse_lpar(b, true) then
|
||||||
|
bp = _parse_mpart(b)
|
||||||
|
else
|
||||||
|
bp = _parse_1part(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not _parse_rpar(b) then return end
|
||||||
|
|
||||||
|
return bp
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_1part(b)
|
||||||
|
local i = b.i
|
||||||
|
local t = _parse_string(b)
|
||||||
|
_parse_space(b)
|
||||||
|
local s = _parse_string(b)
|
||||||
|
if t and t:lower() == 'message' and s and s:lower() == 'rfc822' then
|
||||||
|
return _parse_message(b)
|
||||||
|
else
|
||||||
|
b.i = i
|
||||||
|
return _parse_basic(b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_basic(b)
|
||||||
|
local bp = {}
|
||||||
|
local s
|
||||||
|
|
||||||
|
s = _parse_string(b)
|
||||||
|
if not s then return end
|
||||||
|
bp['type'] = s
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
|
||||||
|
s = _parse_string(b)
|
||||||
|
if not s then return end
|
||||||
|
bp['type'] = bp['type'] .. '/' .. s
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
|
||||||
|
s = _parse_param(b, 'name')
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_string(b)
|
||||||
|
_parse_space(b)
|
||||||
|
bp['size'] = _parse_number(b)
|
||||||
|
if bp['type']:sub(1, 5):lower() == 'text/' then
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_number(b)
|
||||||
|
end
|
||||||
|
if _parse_space(b) then _parse_nstring(b) end
|
||||||
|
if _parse_space(b) then
|
||||||
|
s = _parse_dsp(b)
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
end
|
||||||
|
if _parse_space(b) then _parse_lang(b) end
|
||||||
|
if _parse_space(b) then _parse_nstring(b) end
|
||||||
|
while _parse_space(b) and b.i <= #b.s do _parse_extension(b) end
|
||||||
|
|
||||||
|
return bp
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_mpart(b)
|
||||||
|
local bp = {}
|
||||||
|
local i = 1
|
||||||
|
local s
|
||||||
|
|
||||||
|
bp['type'] = 'multipart'
|
||||||
|
|
||||||
|
while _parse_lpar(b, true) and b.i <= #b.s do
|
||||||
|
bp[i] = _parse_body(b)
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
|
||||||
|
s = _parse_string(b)
|
||||||
|
if not s then return end
|
||||||
|
bp['type'] = bp['type'] .. '/' .. s
|
||||||
|
|
||||||
|
if _parse_space(b) then
|
||||||
|
s = _parse_param(b, 'name')
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
end
|
||||||
|
if _parse_space(b) then
|
||||||
|
s = _parse_dsp(b)
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
end
|
||||||
|
if _parse_space(b) then _parse_lang(b) end
|
||||||
|
if _parse_space(b) then _parse_nstring(b) end
|
||||||
|
while _parse_space(b) and b.i < #b.s do _parse_extension(b) end
|
||||||
|
|
||||||
|
return bp
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_message(b)
|
||||||
|
local bp = {}
|
||||||
|
local s
|
||||||
|
|
||||||
|
bp['type'] = 'message/rfc822'
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
|
||||||
|
s = _parse_param(b, 'name')
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_string(b)
|
||||||
|
_parse_space(b)
|
||||||
|
bp['size'] = _parse_number(b)
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_envelope(b)
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
local p = _parse_body(b)
|
||||||
|
if not p then return end
|
||||||
|
if #p == 0 then
|
||||||
|
bp[1] = p
|
||||||
|
else
|
||||||
|
for k, v in pairs(p) do
|
||||||
|
if type(k) == 'number' then
|
||||||
|
bp[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_number(b)
|
||||||
|
|
||||||
|
if _parse_space(b) then _parse_nstring(b) end
|
||||||
|
if _parse_space(b) then
|
||||||
|
s = _parse_dsp(b)
|
||||||
|
if s then bp['name'] = s end
|
||||||
|
end
|
||||||
|
if _parse_space(b) then _parse_lang(b) end
|
||||||
|
if _parse_space(b) then _parse_nstring(b) end
|
||||||
|
while _parse_space(b) and b.i <= #b.s do _parse_extension(b) end
|
||||||
|
|
||||||
|
return bp
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_envelope(b)
|
||||||
|
_parse_lpar(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_address(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_rpar(b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_address(b)
|
||||||
|
if _parse_lpar(b) then
|
||||||
|
while _parse_lpar(b) and b.i <= #b.s do
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_space(b)
|
||||||
|
_parse_nstring(b)
|
||||||
|
_parse_rpar(b)
|
||||||
|
end
|
||||||
|
_parse_rpar(b)
|
||||||
|
elseif _parse_nil(b) then
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_lang(b)
|
||||||
|
if _parse_lpar(b) then
|
||||||
|
local lang = {}
|
||||||
|
repeat
|
||||||
|
table.insert(lang, _parse_string(b))
|
||||||
|
until not _parse_space(b) or b.i > #b.s
|
||||||
|
_parse_rpar(b)
|
||||||
|
return lang
|
||||||
|
else
|
||||||
|
return _parse_nstring(b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_dsp(b)
|
||||||
|
local r
|
||||||
|
if _parse_lpar(b) then
|
||||||
|
_parse_string(b)
|
||||||
|
_parse_space(b)
|
||||||
|
r = _parse_param(b, 'filename')
|
||||||
|
_parse_rpar(b)
|
||||||
|
elseif _parse_nil(b) then
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_param(b, key)
|
||||||
|
local r
|
||||||
|
if _parse_lpar(b) then
|
||||||
|
repeat
|
||||||
|
local s = _parse_string(b)
|
||||||
|
_parse_space(b)
|
||||||
|
if s and s:lower() == key then
|
||||||
|
r = _parse_string(b)
|
||||||
|
else
|
||||||
|
_parse_string(b)
|
||||||
|
end
|
||||||
|
until not _parse_space(b) or b.i > #b.s
|
||||||
|
_parse_rpar(b)
|
||||||
|
elseif _parse_nil(b) then
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_extension(b)
|
||||||
|
if _parse_nstring(b) then
|
||||||
|
elseif _parse_number(b) then
|
||||||
|
elseif _parse_lpar(b) then
|
||||||
|
_parse_extension(b)
|
||||||
|
while _parse_space(b) and b.i <= #b.s do
|
||||||
|
_parse_extension(b)
|
||||||
|
end
|
||||||
|
_parse_rpar(b)
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_space(b, peek)
|
||||||
|
if b.s:sub(b.i, b.i) == ' ' then
|
||||||
|
if not peek then b.i = b.i + 1 end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_lpar(b, peek)
|
||||||
|
if b.s:sub(b.i, b.i) == '(' then
|
||||||
|
if not peek then b.i = b.i + 1 end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_rpar(b, peek)
|
||||||
|
if b.s:sub(b.i, b.i) == ')' then
|
||||||
|
if not peek then b.i = b.i + 1 end
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_nil(b)
|
||||||
|
if b.s:sub(b.i, b.i + 2):upper() == 'NIL' then
|
||||||
|
b.i = b.i + 3
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_string(b)
|
||||||
|
local i = b.i
|
||||||
|
|
||||||
|
if b.s:sub(i, i) == '"' then
|
||||||
|
i = i + 1
|
||||||
|
else return end
|
||||||
|
|
||||||
|
local j = i
|
||||||
|
local n = 0
|
||||||
|
while true do
|
||||||
|
n = b.s:find('"', i + n)
|
||||||
|
if not n then return end
|
||||||
|
if b.s:sub(n - 1, n - 1) ~= '\\' then
|
||||||
|
i = n + 1
|
||||||
|
b.i = i
|
||||||
|
return b.s:sub(j, n - 1)
|
||||||
|
else return end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_nstring(b)
|
||||||
|
local i = b.i
|
||||||
|
|
||||||
|
if b.s:sub(i, i) == '"' then
|
||||||
|
i = i + 1
|
||||||
|
elseif _parse_nil(b) then
|
||||||
|
return 'NIL'
|
||||||
|
else return end
|
||||||
|
|
||||||
|
local j = i
|
||||||
|
local n = 0
|
||||||
|
while true do
|
||||||
|
n = b.s:find('"', i + n)
|
||||||
|
if not n then return end
|
||||||
|
if b.s:sub(n - 1, n - 1) ~= '\\' then
|
||||||
|
i = n + 1
|
||||||
|
b.i = i
|
||||||
|
return b.s:sub(j, n - 1)
|
||||||
|
else return end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _parse_number(b)
|
||||||
|
local j = b.i
|
||||||
|
local n = b.s:find('[^0-9]', b.i)
|
||||||
|
if not n then return end
|
||||||
|
b.i = n
|
||||||
|
return tonumber(b.s:sub(j, n - 1))
|
||||||
|
end
|
|
@ -0,0 +1,251 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
|
||||||
|
prefix="/usr/local"
|
||||||
|
bindir="$prefix/bin"
|
||||||
|
sharedir="$prefix/share/imapfilter"
|
||||||
|
mandir="$prefix/man"
|
||||||
|
|
||||||
|
ssltls="yes"
|
||||||
|
crammd5="yes"
|
||||||
|
|
||||||
|
incdirs="-I/usr/local/include"
|
||||||
|
libdirs="-L/usr/local/lib"
|
||||||
|
|
||||||
|
mycflags="$CFLAGS -Wall -O"
|
||||||
|
myldflags="$LDFLAGS"
|
||||||
|
|
||||||
|
libs="-lm -llua -lpcre"
|
||||||
|
libssl="-lssl"
|
||||||
|
libcrypto="-lcrypto"
|
||||||
|
|
||||||
|
defs="-DMAKEFILE_SHAREDIR='\"\$(SHAREDIR)\"'"
|
||||||
|
|
||||||
|
bin="imapfilter"
|
||||||
|
|
||||||
|
|
||||||
|
# Get options and arguments
|
||||||
|
|
||||||
|
while getopts "d:p:b:s:m:o:h" opt
|
||||||
|
do
|
||||||
|
case $opt in
|
||||||
|
d | p)
|
||||||
|
prefix=$OPTARG
|
||||||
|
bindir=$prefix/bin
|
||||||
|
sharedir=$prefix/share/imapfilter
|
||||||
|
mandir=$prefix/man
|
||||||
|
;;
|
||||||
|
b)
|
||||||
|
bindir=$OPTARG
|
||||||
|
;;
|
||||||
|
s)
|
||||||
|
sharedir=$OPTARG
|
||||||
|
;;
|
||||||
|
m)
|
||||||
|
mandir=$OPTARG
|
||||||
|
;;
|
||||||
|
o)
|
||||||
|
head=`echo $OPTARG | cut -d= -f1`
|
||||||
|
body=`echo $OPTARG | cut -d= -f2`
|
||||||
|
if [ $head = "ssltls" ]
|
||||||
|
then
|
||||||
|
if [ $body = "yes" ]; then ssltls="yes"
|
||||||
|
elif [ $body = "no" ]; then ssltls="no"
|
||||||
|
fi
|
||||||
|
elif [ $head = "crammd5" ]
|
||||||
|
then
|
||||||
|
if [ $body = "yes" ]; then crammd5="yes"
|
||||||
|
elif [ $body = "no" ]; then crammd5="no"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
h | *)
|
||||||
|
cat << EOF
|
||||||
|
Usage:
|
||||||
|
configure [-h] [-p prefix] [-b bindir] [-s sharedir] [-m mandir]
|
||||||
|
[-o option=argument]
|
||||||
|
|
||||||
|
Description:
|
||||||
|
-h This brief usage and description message.
|
||||||
|
-p prefix Installation path for program's files [$prefix]
|
||||||
|
-b bindir Installation path for binaries [$bindir]
|
||||||
|
-s sharedir Installation path for libraries [$sharedir]
|
||||||
|
-m mandir Installation path for manual pages [$mandir]
|
||||||
|
-o option=argument Enabling/disabling of program's compilation options.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
ssltls Secure Socket Layer and Transport Layer Security \
|
||||||
|
[$ssltls]
|
||||||
|
crammd5 Challenge-Response Authentication Mechanism [$crammd5]
|
||||||
|
EOF
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Print values
|
||||||
|
|
||||||
|
cat << EOF
|
||||||
|
Installation directory: $prefix
|
||||||
|
Binaries directory: $bindir
|
||||||
|
Architecture independent libraries: $sharedir
|
||||||
|
Manual pages directory: $mandir
|
||||||
|
Secure Socket Layer and Transport Layer Security: $ssltls
|
||||||
|
Challenge-Response Authentication Mechanism: $crammd5
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# Defines
|
||||||
|
|
||||||
|
if [ $ssltls = "no" ]
|
||||||
|
then
|
||||||
|
defs="$defs -DNO_SSLTLS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $crammd5 = "no" ]
|
||||||
|
then
|
||||||
|
defs="$defs -DNO_CRAMMD5"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
|
||||||
|
if [ $ssltls = "yes" ]
|
||||||
|
then
|
||||||
|
libs="$libs $libssl $libcrypto"
|
||||||
|
elif [ $crammd5 = "yes" ]
|
||||||
|
then
|
||||||
|
libs="$libs $libcrypto"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Binary name
|
||||||
|
|
||||||
|
uname -a | grep -qi cygwin
|
||||||
|
if [ $? = 0 ]
|
||||||
|
then
|
||||||
|
bin="imapfilter.exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Backup of original Makefile
|
||||||
|
|
||||||
|
if [ ! -f .Makefile ]; then cp -f Makefile .Makefile; fi
|
||||||
|
|
||||||
|
|
||||||
|
# Write Makefile
|
||||||
|
|
||||||
|
mv -f Makefile Makefile~
|
||||||
|
|
||||||
|
cat > Makefile << EOF
|
||||||
|
DESTDIR =
|
||||||
|
BINDIR = $bindir
|
||||||
|
SHAREDIR = $sharedir
|
||||||
|
MANDIR = $mandir
|
||||||
|
|
||||||
|
INCDIRS = $incdirs
|
||||||
|
LIBDIRS = $libdirs
|
||||||
|
|
||||||
|
MYCFLAGS = $mycflags
|
||||||
|
MYLDFLAGS = $myldflags
|
||||||
|
|
||||||
|
DEFS = $defs
|
||||||
|
|
||||||
|
CFLAGS = \$(MYCFLAGS) \$(DEFS) \$(INCDIRS)
|
||||||
|
LDFLAGS = \$(MYLDFLAGS) \$(LIBDIRS)
|
||||||
|
|
||||||
|
LIBS = $libs
|
||||||
|
|
||||||
|
MAN_BIN = imapfilter.1
|
||||||
|
MAN_CONFIG = imapfilter_config.5
|
||||||
|
|
||||||
|
COMMON_LUA = common.lua
|
||||||
|
SET_LUA = set.lua
|
||||||
|
REGEX_LUA = regex.lua
|
||||||
|
ACCOUNT_LUA = account.lua
|
||||||
|
MAILBOX_LUA = mailbox.lua
|
||||||
|
MESSAGE_LUA = message.lua
|
||||||
|
OPTIONS_LUA = options.lua
|
||||||
|
AUXILIARY_LUA = auxiliary.lua
|
||||||
|
DEPRECATED_LUA = deprecated.lua
|
||||||
|
|
||||||
|
BIN = $bin
|
||||||
|
OBJ = auth.o buffer.o cert.o core.o file.o imap.o imapfilter.o list.o log.o \\
|
||||||
|
lua.o memory.o misc.o namespace.o pcre.o regexp.o request.o \\
|
||||||
|
response.o session.o signal.o socket.o system.o
|
||||||
|
|
||||||
|
all: \$(BIN)
|
||||||
|
|
||||||
|
\$(BIN): \$(OBJ)
|
||||||
|
\$(CC) -o \$(BIN) \$(LDFLAGS) \$(OBJ) \$(LIBS)
|
||||||
|
|
||||||
|
\$(OBJ): imapfilter.h
|
||||||
|
buffer.o imap.o imapfilter.o namespace.o request.o response.o: buffer.h
|
||||||
|
cert.o file.o imapfilter.o log.o lua.o: pathnames.h
|
||||||
|
imapfilter.o log.o session.o: list.h
|
||||||
|
imapfilter.o regexp.o response.o: regexp.h
|
||||||
|
auth.o cert.o imap.o imapfilter.o log.o request.o response.o session.o \\
|
||||||
|
socket.o: session.h
|
||||||
|
imapfilter.o: version.h
|
||||||
|
|
||||||
|
install: \$(BIN)
|
||||||
|
if test ! -d \$(DESTDIR)\$(BINDIR); then \\
|
||||||
|
mkdir -p \$(DESTDIR)\$(BINDIR); fi
|
||||||
|
cp -f \$(BIN) \$(DESTDIR)\$(BINDIR) && \\
|
||||||
|
chmod 0755 \$(DESTDIR)\$(BINDIR)/\$(BIN)
|
||||||
|
if test ! -d \$(DESTDIR)\$(SHAREDIR); then \\
|
||||||
|
mkdir -p \$(DESTDIR)\$(SHAREDIR); fi
|
||||||
|
cp -f \$(COMMON_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(COMMON_LUA)
|
||||||
|
cp -f \$(SET_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(SET_LUA)
|
||||||
|
cp -f \$(REGEX_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(REGEX_LUA)
|
||||||
|
cp -f \$(ACCOUNT_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(ACCOUNT_LUA)
|
||||||
|
cp -f \$(MAILBOX_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(MAILBOX_LUA)
|
||||||
|
cp -f \$(MESSAGE_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(MESSAGE_LUA)
|
||||||
|
cp -f \$(OPTIONS_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(OPTIONS_LUA)
|
||||||
|
cp -f \$(AUXILIARY_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(AUXILIARY_LUA)
|
||||||
|
cp -f \$(DEPRECATED_LUA) \$(DESTDIR)\$(SHAREDIR) && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(SHAREDIR)/\$(DEPRECATED_LUA)
|
||||||
|
if test ! -d \$(DESTDIR)\$(MANDIR)/man1; then \\
|
||||||
|
mkdir -p \$(DESTDIR)\$(MANDIR)/man1; fi
|
||||||
|
cp -f \$(MAN_BIN) \$(DESTDIR)\$(MANDIR)/man1 && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(MANDIR)/man1/\$(MAN_BIN)
|
||||||
|
if test ! -d \$(DESTDIR)\$(MANDIR)/man5; then \\
|
||||||
|
mkdir -p \$(DESTDIR)\$(MANDIR)/man5; fi
|
||||||
|
cp -f \$(MAN_CONFIG) \$(DESTDIR)\$(MANDIR)/man5 && \\
|
||||||
|
chmod 0644 \$(DESTDIR)\$(MANDIR)/man5/\$(MAN_CONFIG)
|
||||||
|
|
||||||
|
deinstall:
|
||||||
|
rm -f \$(DESTDIR)\$(BINDIR)/\$(BIN) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(COMMON_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(SET_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(REGEX_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(ACCOUNT_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(MAILBOX_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(MESSAGE_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(OPTIONS_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(AUXILIARY_LUA) \\
|
||||||
|
\$(DESTDIR)\$(SHAREDIR)/\$(DEPRECATED_LUA) \\
|
||||||
|
\$(DESTDIR)\$(MANDIR)/man1/\$(MAN_BIN) \\
|
||||||
|
\$(DESTDIR)\$(MANDIR)/man5/\$(MAN_CONFIG)
|
||||||
|
|
||||||
|
uninstall: deinstall
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f \$(OBJ) \$(BIN) imapfilter.core core *.orig *.BAK *~
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
@if test -f .Makefile; then mv -f .Makefile Makefile; fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,609 @@
|
||||||
|
-- The old and deprecated interface, provided for compatibility.
|
||||||
|
|
||||||
|
function check(account, mbox)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, exist, recent, unseen = ifcore.status(account, mbox)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true) then
|
||||||
|
print(string.format("%d messages, %d recent, %d unseen, in %s@%s/%s.",
|
||||||
|
exist, recent, unseen, account.username,
|
||||||
|
account.server, mbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
return exist, recent, unseen
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function match(account, mbox, criteria)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(criteria, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local charset = ''
|
||||||
|
if (type(options) == 'table' and type(options.charset) == 'string') then
|
||||||
|
charset = options.charset
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, results = ifcore.search(account, _make_query(criteria), charset)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (results == nil) then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local t = {}
|
||||||
|
for n in string.gmatch(results, '%d+') do
|
||||||
|
table.insert(t, tonumber(n))
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function flag(account, mbox, mode, flags, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(mode, 'string')
|
||||||
|
_check_required(flags, 'table')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
local r = flag_aux(account, mbox, mode, flags, messages)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true and
|
||||||
|
messages ~= nil and r == true) then
|
||||||
|
print(string.format("%d messages flagged in %s@%s/%s.",
|
||||||
|
#messages, account.username,
|
||||||
|
account.server, mbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function flag_aux(account, mbox, mode, flags, messages)
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (mode ~= 'add' and mode ~= 'remove' and mode ~= 'replace') then
|
||||||
|
error('"add", "remove" or "replace" expected for mode', 3)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = ''
|
||||||
|
if (#flags ~= 0) then
|
||||||
|
if (flags.keywords ~= true) then
|
||||||
|
for k, v in ipairs(flags) do
|
||||||
|
if (string.lower(v) == 'answered' or
|
||||||
|
string.lower(v) == 'deleted' or
|
||||||
|
string.lower(v) == 'draft' or
|
||||||
|
string.lower(v) == 'flagged' or
|
||||||
|
string.lower(v) == 'seen') then
|
||||||
|
f = f .. '\\' .. v .. ' '
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f = string.gsub(f, '(.+) ', '%1')
|
||||||
|
else
|
||||||
|
f = table.concat(flags, ' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local m = _make_range(messages)
|
||||||
|
n = #m
|
||||||
|
local r = false
|
||||||
|
for i = 1, n, 50 do
|
||||||
|
j = i + 49
|
||||||
|
if (n < j) then
|
||||||
|
j = n
|
||||||
|
end
|
||||||
|
r = ifcore.store(account, table.concat(m, ',', i, j), mode, f)
|
||||||
|
if r == false then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function copy(srcaccount, srcmbox, dstaccount, dstmbox, messages)
|
||||||
|
_check_required(srcaccount, 'table')
|
||||||
|
_check_required(srcmbox, 'string')
|
||||||
|
_check_required(dstaccount, 'table')
|
||||||
|
_check_required(dstmbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(srcaccount.server, 'string')
|
||||||
|
_check_required(srcaccount.username, 'string')
|
||||||
|
_check_required(srcaccount.password, 'string')
|
||||||
|
|
||||||
|
_check_required(dstaccount.server, 'string')
|
||||||
|
_check_required(dstaccount.username, 'string')
|
||||||
|
_check_required(dstaccount.password, 'string')
|
||||||
|
|
||||||
|
local r = copy_aux(srcaccount, srcmbox, dstaccount, dstmbox, messages)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true and
|
||||||
|
messages ~= nil and r == true) then
|
||||||
|
print(string.format("%d messages copied from %s@%s/%s to %s@%s/%s.",
|
||||||
|
#messages, srcaccount.username,
|
||||||
|
srcaccount.server, srcmbox, dstaccount.username,
|
||||||
|
dstaccount.server, dstmbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function copy_aux(srcaccount, srcmbox, dstaccount, dstmbox, messages)
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(srcaccount) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = false
|
||||||
|
if (srcaccount == dstaccount) then
|
||||||
|
if (_cached_select(srcaccount, srcmbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local m = _make_range(messages)
|
||||||
|
n = #m
|
||||||
|
for i = 1, n, 50 do
|
||||||
|
j = i + 49
|
||||||
|
if (n < j) then
|
||||||
|
j = n
|
||||||
|
end
|
||||||
|
r = ifcore.copy(srcaccount, table.concat(m, ',', i, j), dstmbox)
|
||||||
|
if r == false then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(srcaccount)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local fast = fetchfast(srcaccount, srcmbox, messages)
|
||||||
|
local msgs = fetchmessage(srcaccount, srcmbox, messages)
|
||||||
|
|
||||||
|
if (ifcore.login(dstaccount) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for i in pairs(fast) do
|
||||||
|
for k, v in ipairs(fast[i]['flags']) do
|
||||||
|
if (string.lower(v) == '\\recent') then
|
||||||
|
table.remove(fast[i]['flags'], k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
r = ifcore.append(dstaccount, dstmbox, msgs[i],
|
||||||
|
table.concat(fast[i]['flags'], ' '), fast[i]['date'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function delete(account, mbox, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
local r = flag_aux(account, mbox, 'add', { 'Deleted' }, messages)
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true and
|
||||||
|
messages ~= nil and r == true) then
|
||||||
|
print(string.format("%d messages deleted in %s@%s/%s.",
|
||||||
|
#messages, account.username,
|
||||||
|
account.server, mbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function move(srcaccount, srcmbox, dstaccount, dstmbox, messages)
|
||||||
|
_check_required(srcaccount, 'table')
|
||||||
|
_check_required(srcmbox, 'string')
|
||||||
|
_check_required(dstaccount, 'table')
|
||||||
|
_check_required(dstmbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(srcaccount.server, 'string')
|
||||||
|
_check_required(srcaccount.username, 'string')
|
||||||
|
_check_required(srcaccount.password, 'string')
|
||||||
|
|
||||||
|
_check_required(dstaccount.server, 'string')
|
||||||
|
_check_required(dstaccount.username, 'string')
|
||||||
|
_check_required(dstaccount.password, 'string')
|
||||||
|
|
||||||
|
local rc = copy_aux(srcaccount, srcmbox, dstaccount, dstmbox, messages)
|
||||||
|
local rf = false
|
||||||
|
if (rc == true) then
|
||||||
|
rf = flag_aux(srcaccount, srcmbox, 'add', { 'Deleted' }, messages)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.info == true and
|
||||||
|
messages ~= nil and rc == true and rf == true) then
|
||||||
|
print(string.format("%d messages moved from %s@%s/%s to %s@%s/%s.",
|
||||||
|
#messages, srcaccount.username,
|
||||||
|
srcaccount.server, srcmbox, dstaccount.username,
|
||||||
|
dstaccount.server, dstmbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
return rc == true and rf == true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function fetchheader(account, mbox, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for i, v in ipairs(messages) do
|
||||||
|
local _, header = ifcore.fetchheader(account, tostring(v))
|
||||||
|
|
||||||
|
if (header ~= nil) then
|
||||||
|
results[tonumber(v)] = header
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
function fetchbody(account, mbox, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for i, v in ipairs(messages) do
|
||||||
|
local _, body = ifcore.fetchbody(account, tostring(v))
|
||||||
|
|
||||||
|
if (body ~= nil) then
|
||||||
|
results[tonumber(v)] = body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
function fetchmessage(account, mbox, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for i, v in ipairs(messages) do
|
||||||
|
local _, header = ifcore.fetchheader(account, tostring(v))
|
||||||
|
local _, body = ifcore.fetchbody(account, tostring(v))
|
||||||
|
|
||||||
|
if (header ~= nil and body ~= nil) then
|
||||||
|
results[tonumber(v)] = header .. body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
fetchtext = fetchmessage
|
||||||
|
|
||||||
|
function fetchfields(account, mbox, fields, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(fields, 'table')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for i, v in ipairs(messages) do
|
||||||
|
local _, headerfields = ifcore.fetchfields(account, tostring(v),
|
||||||
|
table.concat(fields, ' '))
|
||||||
|
|
||||||
|
if (headerfields ~= nil) then
|
||||||
|
results[tonumber(v)] = headerfields
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
fetchheaders = fetchfields
|
||||||
|
|
||||||
|
function fetchfast(account, mbox, messages)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_required(mbox, 'string')
|
||||||
|
_check_required(messages, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (#messages == 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (_cached_select(account, mbox) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for i, v in ipairs(messages) do
|
||||||
|
local _, flags, date, size = ifcore.fetchfast(account, tostring(v))
|
||||||
|
if (flags ~= nil and date ~= nil and size ~= nil ) then
|
||||||
|
local f = {}
|
||||||
|
for s in string.gmatch(flags, '%S+') do
|
||||||
|
table.insert(f, s)
|
||||||
|
end
|
||||||
|
results[tonumber(v)] = {}
|
||||||
|
results[tonumber(v)]['flags'] = f
|
||||||
|
results[tonumber(v)]['date'] = date
|
||||||
|
results[tonumber(v)]['size'] = size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(options) == 'table' and options.close == true) then
|
||||||
|
_cached_close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function list(account, name)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_optional(name, 'string')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (name == nil) then
|
||||||
|
name = ''
|
||||||
|
else
|
||||||
|
if (type(options) == 'table' and options.namespace == true) then
|
||||||
|
if (name == '/') then
|
||||||
|
name = ''
|
||||||
|
end
|
||||||
|
if (name ~= '') then
|
||||||
|
name = name .. '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, mboxs, folders = ifcore.list(account, '', name .. '%')
|
||||||
|
|
||||||
|
local m = {}
|
||||||
|
for s in string.gmatch(mboxs, '%C+') do
|
||||||
|
table.insert(m, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = {}
|
||||||
|
for s in string.gmatch(folders, '%C+') do
|
||||||
|
if s ~= name and s ~= name .. '/' then
|
||||||
|
table.insert(f, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m, f
|
||||||
|
end
|
||||||
|
|
||||||
|
function lsub(account, name)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
_check_optional(name, 'string')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (name == nil) then
|
||||||
|
name = ''
|
||||||
|
else
|
||||||
|
if (type(options) == 'table' and options.namespace == true) then
|
||||||
|
if (name == '/') then
|
||||||
|
name = ''
|
||||||
|
end
|
||||||
|
if (name ~= '') then
|
||||||
|
name = name .. '/'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, mboxs, folders = ifcore.lsub(account, '', name .. '%')
|
||||||
|
|
||||||
|
local m = {}
|
||||||
|
for s in string.gmatch(mboxs, '%C+') do
|
||||||
|
table.insert(m, s)
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = {}
|
||||||
|
for s in string.gmatch(folders, '%C+') do
|
||||||
|
if s ~= name and s ~= name .. '/' then
|
||||||
|
table.insert(f, s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return m, f
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ping(account)
|
||||||
|
_check_required(account, 'table')
|
||||||
|
|
||||||
|
_check_required(account.server, 'string')
|
||||||
|
_check_required(account.username, 'string')
|
||||||
|
_check_required(account.password, 'string')
|
||||||
|
|
||||||
|
if (ifcore.login(account) ~= true) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local r = ifcore.noop(account)
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _cached_select(account, mbox)
|
||||||
|
if (account.mailbox == nil or account.mailbox ~= mbox) then
|
||||||
|
if (ifcore.select(account, mbox) == true) then
|
||||||
|
account.mailbox = mbox
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _cached_close(account)
|
||||||
|
account.mailbox = nil
|
||||||
|
return ifcore.close(account)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
date_before = form_date
|
||||||
|
get_pass = get_password
|
||||||
|
daemon_mode = become_daemon
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern environment env;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create imapfilter's home directory.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
create_homedir(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char b;
|
||||||
|
char *hd;
|
||||||
|
|
||||||
|
n = snprintf(&b, 1, "%s/%s", env.home, PATHNAME_HOME);
|
||||||
|
|
||||||
|
if (env.pathmax != -1 && n > env.pathmax)
|
||||||
|
fatal(ERROR_PATHNAME,
|
||||||
|
"pathname limit %ld exceeded: %d\n", env.pathmax, n);
|
||||||
|
|
||||||
|
hd = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(hd, n + 1, "%s/%s", env.home, PATHNAME_HOME);
|
||||||
|
|
||||||
|
if (!exists_dir(hd)) {
|
||||||
|
if (mkdir(hd, S_IRUSR | S_IWUSR | S_IXUSR))
|
||||||
|
error("could not create directory %s; %s\n", hd,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
xfree(hd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a file exists.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
exists_file(char *fname)
|
||||||
|
{
|
||||||
|
struct stat fs;
|
||||||
|
|
||||||
|
if (access(fname, F_OK))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
stat(fname, &fs);
|
||||||
|
if (!S_ISREG(fs.st_mode)) {
|
||||||
|
error("file %s not a regular file\n", fname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a directory exists.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
exists_dir(char *dname)
|
||||||
|
{
|
||||||
|
struct stat ds;
|
||||||
|
|
||||||
|
if (access(dname, F_OK))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
stat(dname, &ds);
|
||||||
|
if (!S_ISDIR(ds.st_mode)) {
|
||||||
|
error("file %s not a directory\n", dname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a file with the specified permissions.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
create_file(char *fname, mode_t mode)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = 0;
|
||||||
|
|
||||||
|
if (!exists_file(fname)) {
|
||||||
|
fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||||
|
if (fd == -1) {
|
||||||
|
error("could not create file %s; %s\n", fname,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the system's maximum number of bytes in a pathname.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
get_pathmax(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
n = pathconf("/", _PC_PATH_MAX);
|
||||||
|
|
||||||
|
if (n == -1 && errno != 0) {
|
||||||
|
error("getting PATH_MAX limit; %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
env.pathmax = n;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,484 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern options opts;
|
||||||
|
|
||||||
|
buffer obuf; /* Output buffer. */
|
||||||
|
|
||||||
|
static int tag = 0x1000; /* Every IMAP command is prefixed with a
|
||||||
|
* unique [:alnum:] string. */
|
||||||
|
|
||||||
|
|
||||||
|
int send_command(session *ssn, char *cmd, char *alt);
|
||||||
|
void prepare_command(const char *fmt,...);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends to server data; a command.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
send_command(session *ssn, char *cmd, char *alt)
|
||||||
|
{
|
||||||
|
int t = tag;
|
||||||
|
|
||||||
|
if (ssn->socket == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
debug("sending command (%d):\n\n%s\n", ssn->socket,
|
||||||
|
(opts.debug == 1 && alt ? alt : cmd));
|
||||||
|
|
||||||
|
verbose("C (%d): %s", ssn->socket, (alt ? alt : cmd));
|
||||||
|
|
||||||
|
if (socket_write(ssn, cmd, strlen(cmd)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (tag == 0xFFFF) /* Tag always between 0x1000 and 0xFFFF. */
|
||||||
|
tag = 0x0FFF;
|
||||||
|
tag++;
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepares data for sending and check that the output buffer size is
|
||||||
|
* sufficient.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
prepare_command(const char *fmt,...)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
buffer_reset(&obuf);
|
||||||
|
n = vsnprintf(obuf.data, obuf.size + 1, fmt, args);
|
||||||
|
if (n > (int)obuf.size) {
|
||||||
|
buffer_check(&obuf, n);
|
||||||
|
vsnprintf(obuf.data, obuf.size + 1, fmt, args);
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends a response to a command continuation request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_continuation(session *ssn, const char *cont, size_t len)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ssn->socket == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
if (socket_write(ssn, cont, len) == -1 ||
|
||||||
|
socket_write(ssn, "\r\n", strlen("\r\n")) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (opts.debug > 0) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
debug("sending continuation data (%d):\n\n", ssn->socket);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
debugc(cont[i]);
|
||||||
|
|
||||||
|
debug("\r\n\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP CAPABILITY: requests listing of capabilities that the server supports.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_capability(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X CAPABILITY\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP NOOP: does nothing always succeeds.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_noop(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X NOOP\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP LOGOUT: informs server that client is done.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_logout(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X LOGOUT\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP STARTTLS: begins TLS negotiation.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_starttls(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X STARTTLS\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
/*
|
||||||
|
* IMAP AUTHENTICATE: indicates authentication mechanism and performs an
|
||||||
|
* authentication protocol exchange.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_authenticate(session *ssn, const char *auth)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X AUTHENTICATE %s\r\n", tag, auth);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP LOGIN: identifies client to server.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_login(session *ssn, const char *user, const char *pass)
|
||||||
|
{
|
||||||
|
int n, r;
|
||||||
|
char c;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
/* Command to send to server. */
|
||||||
|
prepare_command("%04X LOGIN \"%s\" \"%s\"\r\n", tag, user, pass);
|
||||||
|
|
||||||
|
/* Alternate command with password shrouded for safe printing. */
|
||||||
|
n = snprintf(&c, 1, "%04X LOGIN \"%s\" *\r\n", tag, user);
|
||||||
|
s = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(s, n + 1, "%04X LOGIN \"%s\" *\r\n", tag, user);
|
||||||
|
|
||||||
|
r = send_command(ssn, obuf.data, s);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP SELECT: accesses a mailbox in READ-WRITE mode.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_select(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X SELECT \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP EXAMINE: accesses a mailbox in READ-ONLY mode.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_examine(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X EXAMINE \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP CREATE: creates mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_create(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X CREATE \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP DELETE: deletes mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_delete(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X DELETE \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP RENAME: renames mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_rename(session *ssn, const char *oldmbox, const char *newmbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X RENAME \"%s\" \"%s\"\r\n", tag, oldmbox, newmbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP SUBSCRIBE: adds the specified mailbox name to the server's set of
|
||||||
|
* "active" or "subscribed" mailboxes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_subscribe(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X SUBSCRIBE \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP UNSUBSCRIBE: removes the specified mailbox name to the server's set of
|
||||||
|
* "active" or "subscribed" mailboxes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_unsubscribe(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X UNSUBSCRIBE \"%s\"\r\n", tag, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP LIST: returns a subset of names from the complete set of all names
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_list(session *ssn, const char *refer, const char *name)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X LIST \"%s\" \"%s\"\r\n", tag, refer, name);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP LSUB: returns a subset of names from the set of names that the user has
|
||||||
|
* declared as being "active" or "subscribed".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_lsub(session *ssn, const char *refer, const char *name)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X LSUB \"%s\" \"%s\"\r\n", tag, refer, name);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP STATUS: requests status of the indicated mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_status(session *ssn, const char *mbox, const char *items)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X STATUS \"%s\" (%s)\r\n", tag, mbox, items);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP APPEND: append message to the end of a mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_append(session *ssn, const char *mbox, const char *flags,
|
||||||
|
const char *date, unsigned int size)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X APPEND \"%s\"%s%s%s%s%s%s {%d}\r\n", tag, mbox,
|
||||||
|
(flags ? " (" : ""), (flags ? flags : ""), (flags ? ")" : ""),
|
||||||
|
(date ? " \"" : ""), (date ? date : ""), (date ? "\"" : ""), size);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP CHECK: requests a checkpoint of the currently selected mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_check(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X CHECK\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP CLOSE: deletes messages and returns to authenticated state.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_close(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X CLOSE\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP EXPUNGE: permanently removes any messages with the \Deleted flag set.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_expunge(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X EXPUNGE\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP SEARCH: searches the mailbox for messages that match certain criteria.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_search(session *ssn, const char *charset, const char *criteria)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (charset != NULL && *charset != '\0')
|
||||||
|
prepare_command("%04X UID SEARCH CHARSET \"%s\" %s\r\n", tag,
|
||||||
|
charset, criteria);
|
||||||
|
else
|
||||||
|
prepare_command("%04X UID SEARCH %s\r\n", tag,
|
||||||
|
criteria);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP FETCH: retrieves data associated with a message.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_fetch(session *ssn, const char *mesg, const char *items)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X UID FETCH %s %s\r\n", tag, mesg, items);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP STORE: alters data associated with a message.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_store(session *ssn, const char *mesg, const char *mode,
|
||||||
|
const char *flags)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X UID STORE %s %sFLAGS.SILENT (%s)\r\n", tag,
|
||||||
|
mesg, (!strncasecmp(mode, "add", 3) ? "+" :
|
||||||
|
!strncasecmp(mode, "remove", 6) ? "-" : ""), flags);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP COPY: copy messages to mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_copy(session *ssn, const char *mesg, const char *mbox)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X UID COPY %s \"%s\"\r\n", tag, mesg, mbox);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP NAMESPACE: discovers the prefix and delimeter of namespaces used by the
|
||||||
|
* server for mailboxes (RFC 2342).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_namespace(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
prepare_command("%04X NAMESPACE\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP IDLE: enter the idle state and waits for mailbox updates, as specified
|
||||||
|
* in the IMAP IDLE extension (RFC 2177).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_idle(session *ssn)
|
||||||
|
{
|
||||||
|
prepare_command("%04X IDLE\r\n", tag);
|
||||||
|
|
||||||
|
return send_command(ssn, obuf.data, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAP DONE: ends the idle state entered through the IMAP IDLE command
|
||||||
|
* (RFC 2177).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
imap_done(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
return imap_continuation(ssn, "DONE", strlen("DONE"));
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
.Dd February 28, 2011
|
||||||
|
.Dt IMAPFILTER 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm imapfilter
|
||||||
|
.Nd mail filter
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl Vdiv
|
||||||
|
.Op Fl c Ar configfile
|
||||||
|
.Op Fl e Ar 'command'
|
||||||
|
.Op Fl l Ar logfile
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a mail filtering utility. It connects to remote mail servers using the
|
||||||
|
Internet Message Access Protocol (IMAP), sends searching queries to the server
|
||||||
|
and processes mailboxes based on the results. It can be used to delete, copy,
|
||||||
|
move, flag, etc. messages residing in mailboxes at the same or different mail
|
||||||
|
servers. The 4rev1 and 4 versions of the IMAP protocol are supported.
|
||||||
|
.Pp
|
||||||
|
The command line options of
|
||||||
|
.Xr imapfilter 1
|
||||||
|
are as follows:
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl V
|
||||||
|
Displays program's version.
|
||||||
|
.It Fl c Ar configfile
|
||||||
|
Path to the configuration file. The default is
|
||||||
|
.Pa $HOME/.imapfilter/config.lua .
|
||||||
|
.It Fl d
|
||||||
|
Debug mode; creates a temporary file name in
|
||||||
|
.Pa $HOME/.imapfilter/debug.XXXXXX ,
|
||||||
|
where debugging messages about the program's progress are printed.
|
||||||
|
.It Fl e Ar 'command'
|
||||||
|
May be used to enter one line of program (configuration).
|
||||||
|
When this options is used,
|
||||||
|
.Nm
|
||||||
|
will not look for any configuration file.
|
||||||
|
.It Fl i
|
||||||
|
Enters interactive mode after executing the configuration file or the command
|
||||||
|
line.
|
||||||
|
.It Fl l Ar logfile
|
||||||
|
File that contains logs of error messages
|
||||||
|
.Nm
|
||||||
|
produces.
|
||||||
|
.It Fl v
|
||||||
|
Verbose mode; prints detailed information about the program's actions.
|
||||||
|
.El
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Ev HOME
|
||||||
|
User's home directory.
|
||||||
|
.El
|
||||||
|
.Sh FILES
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Pa $HOME/.imapfilter/config.lua
|
||||||
|
Default configuration file. Because this file may contain sensitive data such
|
||||||
|
as user passwords, the recommended permissions are read/write for the user, and
|
||||||
|
not accessible by others.
|
||||||
|
.It Pa $HOME/.imapfilter/certificates
|
||||||
|
File where the SSL/TLS certificates are stored.
|
||||||
|
.It Pa $HOME/.imapfilter/debug.XXXXXX
|
||||||
|
Debug files.
|
||||||
|
.El
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr imapfilter_config 5
|
||||||
|
.Sh CONFORMING TO
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It IMAP4rev1:
|
||||||
|
RFC 3501, RFC 3348, RFC 2683, RFC 2595, RFC 2342, RFC 2195,
|
||||||
|
RFC 2177
|
||||||
|
.It IMAP4:
|
||||||
|
RFC 1730
|
||||||
|
.El
|
|
@ -0,0 +1,186 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "version.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
#include "regexp.h"
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
extern buffer ibuf, obuf, nbuf;
|
||||||
|
extern regexp responses[];
|
||||||
|
|
||||||
|
options opts; /* Program options. */
|
||||||
|
environment env; /* Environment variables. */
|
||||||
|
|
||||||
|
list *sessions = NULL; /* Active IMAP sessions. */
|
||||||
|
|
||||||
|
void usage(void);
|
||||||
|
void version(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IMAPFilter: an IMAP mail filtering utility.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
|
||||||
|
opts.debug = 0;
|
||||||
|
opts.verbose = 0;
|
||||||
|
opts.interactive = 0;
|
||||||
|
opts.log = NULL;
|
||||||
|
opts.config = NULL;
|
||||||
|
opts.oneline = NULL;
|
||||||
|
|
||||||
|
env.home = getenv("HOME");
|
||||||
|
env.pathmax = -1;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "Vc:de:il:v?")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'V':
|
||||||
|
version();
|
||||||
|
/* NOTREACHED */
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
opts.config = optarg;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (opts.debug < 2)
|
||||||
|
opts.debug++;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
opts.oneline = optarg;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
opts.interactive = 1;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
opts.log = optarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
opts.verbose = 1;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
/* NOTREACHED */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_pathmax();
|
||||||
|
|
||||||
|
open_debug();
|
||||||
|
|
||||||
|
create_homedir();
|
||||||
|
|
||||||
|
catch_signals();
|
||||||
|
|
||||||
|
open_log();
|
||||||
|
|
||||||
|
buffer_init(&ibuf, INPUT_BUF);
|
||||||
|
buffer_init(&obuf, OUTPUT_BUF);
|
||||||
|
buffer_init(&nbuf, NAMESPACE_BUF);
|
||||||
|
|
||||||
|
if (opts.config == NULL) {
|
||||||
|
int n;
|
||||||
|
char b;
|
||||||
|
|
||||||
|
n = snprintf(&b, 1, "%s/%s", env.home, PATHNAME_CONFIG);
|
||||||
|
|
||||||
|
if (env.pathmax != -1 && n > env.pathmax)
|
||||||
|
fatal(ERROR_PATHNAME,
|
||||||
|
"pathname limit %ld exceeded: %d\n", env.pathmax,
|
||||||
|
n);
|
||||||
|
|
||||||
|
opts.config = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(opts.config, n + 1, "%s/%s", env.home,
|
||||||
|
PATHNAME_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
regexp_compile(responses);
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
SSL_library_init();
|
||||||
|
SSL_load_error_strings();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
start_lua();
|
||||||
|
|
||||||
|
{
|
||||||
|
list *l;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
l = sessions;
|
||||||
|
while (l != NULL) {
|
||||||
|
s = l->data;
|
||||||
|
l = l->next;
|
||||||
|
|
||||||
|
response_generic(s, imap_logout(s));
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_lua();
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
ERR_free_strings();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
regexp_free(responses);
|
||||||
|
|
||||||
|
buffer_free(&ibuf);
|
||||||
|
buffer_free(&obuf);
|
||||||
|
buffer_free(&nbuf);
|
||||||
|
|
||||||
|
close_log();
|
||||||
|
close_debug();
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a very brief usage message.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf(stderr, "usage: imapfilter [-Vdiv] [-c configfile] "
|
||||||
|
"[-e 'command'] [-l logfile]\n");
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print program's version and copyright.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
version(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
fprintf(stderr, "IMAPFilter %s %s\n", VERSION, COPYRIGHT);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
#ifndef IMAPFILTER_H
|
||||||
|
#define IMAPFILTER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Fatal error exit codes. */
|
||||||
|
#define ERROR_SIGNAL 1
|
||||||
|
#define ERROR_CONFIG 2
|
||||||
|
#define ERROR_MEMALLOC 3
|
||||||
|
#define ERROR_PATHNAME 4
|
||||||
|
#define ERROR_CERTIFICATE 5
|
||||||
|
|
||||||
|
/* IMAP protocol supported by the server. */
|
||||||
|
#define PROTOCOL_NONE 0
|
||||||
|
#define PROTOCOL_IMAP4REV1 1
|
||||||
|
#define PROTOCOL_IMAP4 2
|
||||||
|
|
||||||
|
/* Capabilities of mail server. */
|
||||||
|
#define CAPABILITY_NONE 0x00
|
||||||
|
#define CAPABILITY_NAMESPACE 0x01
|
||||||
|
#define CAPABILITY_CRAMMD5 0x02
|
||||||
|
#define CAPABILITY_STARTTLS 0x04
|
||||||
|
#define CAPABILITY_CHILDREN 0x08
|
||||||
|
#define CAPABILITY_IDLE 0x10
|
||||||
|
|
||||||
|
/* Status responses and response codes. */
|
||||||
|
#define STATUS_RESPONSE_NONE 0
|
||||||
|
#define STATUS_RESPONSE_OK 1
|
||||||
|
#define STATUS_RESPONSE_NO 2
|
||||||
|
#define STATUS_RESPONSE_BAD 3
|
||||||
|
#define STATUS_RESPONSE_UNTAGGED 4
|
||||||
|
#define STATUS_RESPONSE_CONTINUE 5
|
||||||
|
#define STATUS_RESPONSE_BYE 6
|
||||||
|
#define STATUS_RESPONSE_PREAUTH 7
|
||||||
|
#define STATUS_RESPONSE_READONLY 8
|
||||||
|
#define STATUS_RESPONSE_TRYCREATE 9
|
||||||
|
#define STATUS_RESPONSE_TIMEOUT 10
|
||||||
|
|
||||||
|
|
||||||
|
/* Initial buffer size for input, output and namespace buffers. */
|
||||||
|
#define INPUT_BUF 4096
|
||||||
|
#define OUTPUT_BUF 1024
|
||||||
|
#define NAMESPACE_BUF 128
|
||||||
|
|
||||||
|
/* Maximum length, in bytes, of a utility's input line. */
|
||||||
|
#ifndef LINE_MAX
|
||||||
|
#define LINE_MAX 2048
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Program's options. */
|
||||||
|
typedef struct options {
|
||||||
|
int debug; /* Debugging level (0..2). */
|
||||||
|
int verbose; /* Verbose mode. */
|
||||||
|
int interactive; /* Act as an interpreter. */
|
||||||
|
char *log; /* Log file for error messages. */
|
||||||
|
char *config; /* Configuration file. */
|
||||||
|
char *oneline; /* One line of program/configuration. */
|
||||||
|
} options;
|
||||||
|
|
||||||
|
/* Environment variables. */
|
||||||
|
typedef struct environment {
|
||||||
|
char *home; /* User's home directory. */
|
||||||
|
long pathmax; /* Maximum pathname. */
|
||||||
|
} environment;
|
||||||
|
|
||||||
|
|
||||||
|
/* auth.c */
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
int auth_cram_md5(session *ssn, const char *user, const char *pass);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* cert.c */
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
int get_cert(session *ssn);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* core.c */
|
||||||
|
LUALIB_API int luaopen_ifcore(lua_State *lua);
|
||||||
|
|
||||||
|
/* file.c */
|
||||||
|
int create_homedir(void);
|
||||||
|
int exists_file(char *fname);
|
||||||
|
int exists_dir(char *fname);
|
||||||
|
int create_file(char *fname, mode_t mode);
|
||||||
|
int get_pathmax(void);
|
||||||
|
|
||||||
|
/* imap.c */
|
||||||
|
int imap_continuation(session *ssn, const char *cont, size_t len);
|
||||||
|
int imap_capability(session *ssn);
|
||||||
|
int imap_noop(session *ssn);
|
||||||
|
int imap_logout(session *ssn);
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
int imap_starttls(session *ssn);
|
||||||
|
#endif
|
||||||
|
int imap_authenticate(session *ssn, const char *auth);
|
||||||
|
int imap_login(session *ssn, const char *user, const char *pass);
|
||||||
|
int imap_select(session *ssn, const char *mbox);
|
||||||
|
int imap_examine(session *ssn, const char *mbox);
|
||||||
|
int imap_create(session *ssn, const char *mbox);
|
||||||
|
int imap_delete(session *ssn, const char *mbox);
|
||||||
|
int imap_rename(session *ssn, const char *oldmbox, const char *newmbox);
|
||||||
|
int imap_subscribe(session *ssn, const char *mbox);
|
||||||
|
int imap_unsubscribe(session *ssn, const char *mbox);
|
||||||
|
int imap_list(session *ssn, const char *refer, const char *name);
|
||||||
|
int imap_lsub(session *ssn, const char *refer, const char *name);
|
||||||
|
int imap_status(session *ssn, const char *mbox, const char *items);
|
||||||
|
int imap_append(session *ssn, const char *mbox, const char *flags,
|
||||||
|
const char *date, unsigned int size);
|
||||||
|
int imap_check(session *ssn);
|
||||||
|
int imap_close(session *ssn);
|
||||||
|
int imap_expunge(session *ssn);
|
||||||
|
int imap_search(session *ssn, const char *charset, const char *criteria);
|
||||||
|
int imap_fetch(session *ssn, const char *mesg, const char *items);
|
||||||
|
int imap_store(session *ssn, const char *mesg, const char *mode,
|
||||||
|
const char *flags);
|
||||||
|
int imap_copy(session *ssn, const char *mesg, const char *mbox);
|
||||||
|
int imap_namespace(session *ssn);
|
||||||
|
int imap_idle(session *ssn);
|
||||||
|
int imap_done(session *ssn);
|
||||||
|
|
||||||
|
/* log.c */
|
||||||
|
void verbose(const char *info,...);
|
||||||
|
void debug(const char *debug,...);
|
||||||
|
void debugc(char c);
|
||||||
|
void error(const char *errmsg,...);
|
||||||
|
void fatal(unsigned int errnum, const char *fatal,...);
|
||||||
|
|
||||||
|
int open_debug(void);
|
||||||
|
int close_debug(void);
|
||||||
|
|
||||||
|
int open_log(void);
|
||||||
|
int close_log(void);
|
||||||
|
|
||||||
|
/* lua.c */
|
||||||
|
void start_lua(void);
|
||||||
|
void stop_lua(void);
|
||||||
|
|
||||||
|
int get_option_boolean(const char *opt);
|
||||||
|
lua_Number get_option_number(const char *opt);
|
||||||
|
const char *get_option_string(const char *opt);
|
||||||
|
|
||||||
|
int get_table_type(const char *key);
|
||||||
|
lua_Number get_table_number(const char *key);
|
||||||
|
const char *get_table_string(const char *key);
|
||||||
|
|
||||||
|
int set_table_nil(const char *key);
|
||||||
|
int set_table_boolean(const char *key, int value);
|
||||||
|
int set_table_number(const char *key, lua_Number value);
|
||||||
|
int set_table_string(const char *key, const char *value);
|
||||||
|
|
||||||
|
/* memory.c */
|
||||||
|
void *xmalloc(size_t size);
|
||||||
|
void *xrealloc(void *ptr, size_t size);
|
||||||
|
void xfree(void *ptr);
|
||||||
|
char *xstrdup(const char *str);
|
||||||
|
char *xstrndup(const char *str, size_t len);
|
||||||
|
|
||||||
|
/* misc.c */
|
||||||
|
const char *xstrcasestr(const char *haystack, const char *needle);
|
||||||
|
char *xstrncpy(char *dest, const char *src, size_t size);
|
||||||
|
|
||||||
|
/* namespace.c */
|
||||||
|
const char *apply_namespace(const char *mbox, char *prefix, char delim);
|
||||||
|
const char *reverse_namespace(const char *mbox, char *prefix, char delim);
|
||||||
|
|
||||||
|
/* pcre.c */
|
||||||
|
LUALIB_API int luaopen_ifre(lua_State *lua);
|
||||||
|
|
||||||
|
/* request.c */
|
||||||
|
int request_noop(const char *server, const char *port, const char *user);
|
||||||
|
int request_login(const char *server, const char *port, const char *ssl,
|
||||||
|
const char *user, const char *pass);
|
||||||
|
int request_logout(const char *server, const char *port, const char *user);
|
||||||
|
int request_status(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox, unsigned int *exist, unsigned int *recent,
|
||||||
|
unsigned int *unseen, unsigned int *uidnext);
|
||||||
|
int request_select(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox);
|
||||||
|
int request_close(const char *server, const char *port, const char *user);
|
||||||
|
int request_expunge(const char *server, const char *port, const char *user);
|
||||||
|
int request_list(const char *server, const char *port, const char *user,
|
||||||
|
const char *refer, const char *name, char **mboxs, char **folders);
|
||||||
|
int request_lsub(const char *server, const char *port, const char *user,
|
||||||
|
const char *refer, const char *name, char **mboxs, char **folders);
|
||||||
|
int request_search(const char *server, const char *port, const char *user,
|
||||||
|
const char *criteria, const char *charset, char **mesgs);
|
||||||
|
int request_fetchfast(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **flags, char **date, char **size);
|
||||||
|
int request_fetchflags(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **flags);
|
||||||
|
int request_fetchdate(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **date);
|
||||||
|
int request_fetchsize(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **size);
|
||||||
|
int request_fetchstructure(const char *server, const char *port,
|
||||||
|
const char *user, const char *mesg, char **structure);
|
||||||
|
int request_fetchheader(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **header, size_t *len);
|
||||||
|
int request_fetchtext(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **text, size_t *len);
|
||||||
|
int request_fetchfields(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *headerfields, char **fields, size_t *len);
|
||||||
|
int request_fetchpart(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *bodypart, char **part, size_t *len);
|
||||||
|
int request_store(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *mode, const char *flags);
|
||||||
|
int request_copy(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *mbox);
|
||||||
|
int request_append(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox, const char *mesg, size_t mesglen, const char *flags,
|
||||||
|
const char *date);
|
||||||
|
int request_create(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox);
|
||||||
|
int request_delete(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox);
|
||||||
|
int request_rename(const char *server, const char *port, const char *user,
|
||||||
|
const char *oldmbox, const char *newmbox);
|
||||||
|
int request_subscribe(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox);
|
||||||
|
int request_unsubscribe(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox);
|
||||||
|
int request_idle(const char *server, const char *port, const char *user);
|
||||||
|
|
||||||
|
|
||||||
|
/* response.c */
|
||||||
|
int response_generic(session *ssn, int tag);
|
||||||
|
int response_continuation(session *ssn);
|
||||||
|
int response_greeting(session *ssn);
|
||||||
|
int response_capability(session *ssn, int tag);
|
||||||
|
int response_authenticate(session *ssn, int tag, unsigned char **cont);
|
||||||
|
int response_namespace(session *ssn, int tag);
|
||||||
|
int response_status(session *ssn, int tag, unsigned int *exist,
|
||||||
|
unsigned int *recent, unsigned int *unseen, unsigned int *uidnext);
|
||||||
|
int response_examine(session *ssn, int tag, unsigned int *exist,
|
||||||
|
unsigned int *recent);
|
||||||
|
int response_select(session *ssn, int tag);
|
||||||
|
int response_list(session *ssn, int tag, char **mboxs, char **folders);
|
||||||
|
int response_search(session *ssn, int tag, char **mesgs);
|
||||||
|
int response_fetchfast(session *ssn, int tag, char **flags, char **date,
|
||||||
|
char **size);
|
||||||
|
int response_fetchflags(session *ssn, int tag, char **flags);
|
||||||
|
int response_fetchdate(session *ssn, int tag, char **date);
|
||||||
|
int response_fetchsize(session *ssn, int tag, char **size);
|
||||||
|
int response_fetchstructure(session *ssn, int tag, char **structure);
|
||||||
|
int response_fetchbody(session *ssn, int tag, char **body, size_t *len);
|
||||||
|
int response_idle(session *ssn, int tag);
|
||||||
|
|
||||||
|
/* signal.c */
|
||||||
|
void catch_signals(void);
|
||||||
|
void release_signals(void);
|
||||||
|
|
||||||
|
/* socket.c */
|
||||||
|
int open_connection(session *ssn, const char *server, const char *port,
|
||||||
|
const char *protocol);
|
||||||
|
int close_connection(session *ssn);
|
||||||
|
ssize_t socket_read(session *ssn, char *buf, size_t len, long timeout,
|
||||||
|
int timeoutfail);
|
||||||
|
ssize_t socket_write(session *ssn, const char *buf, size_t len);
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
int open_secure_connection(session *ssn, const char *server, const char *port,
|
||||||
|
const char *protocol);
|
||||||
|
int close_secure_connection(session *ssn);
|
||||||
|
ssize_t socket_secure_read(session *ssn, char *buf, size_t len);
|
||||||
|
ssize_t socket_secure_write(session *ssn, const char *buf, size_t len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* system.c */
|
||||||
|
LUALIB_API int luaopen_ifsys(lua_State *lua);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* IMAPFILTER_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,59 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a new element at the end of the list.
|
||||||
|
*/
|
||||||
|
list *
|
||||||
|
list_append(list *lst, void *data)
|
||||||
|
{
|
||||||
|
list *l, *nl;
|
||||||
|
|
||||||
|
nl = (list *)xmalloc(sizeof(list));
|
||||||
|
nl->data = data;
|
||||||
|
nl->prev = nl->next = NULL;
|
||||||
|
|
||||||
|
if (lst != NULL) {
|
||||||
|
for (l = lst; l->next != NULL; l = l->next);
|
||||||
|
l->next = nl;
|
||||||
|
nl->prev = l;
|
||||||
|
|
||||||
|
return lst;
|
||||||
|
} else {
|
||||||
|
return nl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove an element from the list.
|
||||||
|
*/
|
||||||
|
list *
|
||||||
|
list_remove(list *lst, void *data)
|
||||||
|
{
|
||||||
|
list *l;
|
||||||
|
|
||||||
|
l = lst;
|
||||||
|
while (l != NULL) {
|
||||||
|
if (l->data != data)
|
||||||
|
l = l->next;
|
||||||
|
else {
|
||||||
|
if (l->prev)
|
||||||
|
l->prev->next = l->next;
|
||||||
|
if (l->next)
|
||||||
|
l->next->prev = l->prev;
|
||||||
|
if (lst == l)
|
||||||
|
lst = lst->next;
|
||||||
|
|
||||||
|
xfree(l);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lst;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef LIST_H
|
||||||
|
#define LIST_H
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct list {
|
||||||
|
void *data;
|
||||||
|
struct list *next, *prev;
|
||||||
|
} list;
|
||||||
|
|
||||||
|
|
||||||
|
/* list.h */
|
||||||
|
list *list_append(list *lst, void *data);
|
||||||
|
list *list_remove(list *lst, void *data);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LIST_H */
|
|
@ -0,0 +1,240 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern options opts;
|
||||||
|
extern environment env;
|
||||||
|
extern unsigned int flags;
|
||||||
|
extern list *sessions;
|
||||||
|
|
||||||
|
static FILE *debugfp = NULL; /* Pointer to debug file. */
|
||||||
|
static FILE *logfp = NULL; /* Pointer to log file. */
|
||||||
|
|
||||||
|
|
||||||
|
char *log_time(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print message if in verbose mode.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
verbose(const char *fmt,...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (!opts.verbose)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vprintf(fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write message to debug file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
debug(const char *fmt,...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
if (opts.debug <= 0 || !debugfp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(debugfp, fmt, args);
|
||||||
|
fflush(debugfp);
|
||||||
|
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write character to debug file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
debugc(char c)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (opts.debug <= 0 || !debugfp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fputc(c, debugfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print error message and write it into log file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
error(const char *fmt,...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
fprintf(stderr, "imapfilter: ");
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
if (logfp) {
|
||||||
|
va_start(args, fmt);
|
||||||
|
fprintf(logfp, "%s: ", log_time());
|
||||||
|
vfprintf(logfp, fmt, args);
|
||||||
|
fflush(logfp);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print error message and exit program.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
fatal(unsigned int errnum, const char *fmt,...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
list *l;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
fprintf(stderr, "imapfilter: ");
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (logfp) {
|
||||||
|
va_start(args, fmt);
|
||||||
|
fprintf(logfp, "%s: ", log_time());
|
||||||
|
vfprintf(logfp, fmt, args);
|
||||||
|
fflush(logfp);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (l = sessions; l; l = l->next) {
|
||||||
|
s = l->data;
|
||||||
|
close_connection(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_log();
|
||||||
|
close_debug();
|
||||||
|
|
||||||
|
exit(errnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open temporary debug file and associate a stream with the returned file
|
||||||
|
* descriptor.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
open_debug(void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char b;
|
||||||
|
char *dt;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!opts.debug)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
n = snprintf(&b, 1, "%s/%s", env.home, PATHNAME_DEBUG);
|
||||||
|
|
||||||
|
if (env.pathmax != -1 && n > env.pathmax)
|
||||||
|
fatal(ERROR_PATHNAME,
|
||||||
|
"pathname limit %ld exceeded: %d\n", env.pathmax, n);
|
||||||
|
|
||||||
|
dt = (char *)xmalloc((n + 1) * sizeof(char));
|
||||||
|
snprintf(dt, n + 1, "%s/%s", env.home, PATHNAME_DEBUG);
|
||||||
|
|
||||||
|
fd = mkstemp(dt);
|
||||||
|
|
||||||
|
if (fd != -1) {
|
||||||
|
debugfp = fdopen(fd, "w");
|
||||||
|
if (debugfp == NULL) {
|
||||||
|
error("opening debug file %s: %s\n", dt,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close temporary debug file.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
close_debug(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (debugfp == NULL)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return fclose(debugfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the file for saving of logging information.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
open_log(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (opts.log == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
debug("log file: '%s'\n", opts.log);
|
||||||
|
|
||||||
|
if (create_file(opts.log, S_IRUSR | S_IWUSR))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
logfp = fopen(opts.log, "a");
|
||||||
|
if (logfp == NULL) {
|
||||||
|
error("opening log file %s: %s\n", opts.log, strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the log file.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
close_log(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (logfp == NULL)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return fclose(logfp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return current local time and date.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
log_time(void)
|
||||||
|
{
|
||||||
|
char *ct;
|
||||||
|
time_t t;
|
||||||
|
|
||||||
|
t = time(NULL);
|
||||||
|
|
||||||
|
ct = ctime(&t);
|
||||||
|
*(strchr(ct, '\n')) = '\0';
|
||||||
|
|
||||||
|
return ct;
|
||||||
|
}
|
|
@ -0,0 +1,343 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "pathnames.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern options opts;
|
||||||
|
extern struct sessionhead sessions;
|
||||||
|
|
||||||
|
static lua_State *lua; /* Lua interpreter state. */
|
||||||
|
|
||||||
|
|
||||||
|
void init_options(void);
|
||||||
|
void interactive_mode(void);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the Lua interpreter, export IMAP core and system functions, load the
|
||||||
|
* Lua interface functions, load and execute imapfilter's configuration file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
start_lua()
|
||||||
|
{
|
||||||
|
|
||||||
|
lua = luaL_newstate();
|
||||||
|
|
||||||
|
luaL_openlibs(lua);
|
||||||
|
|
||||||
|
luaopen_ifcore(lua);
|
||||||
|
luaopen_ifsys(lua);
|
||||||
|
luaopen_ifre(lua);
|
||||||
|
|
||||||
|
lua_settop(lua, 0);
|
||||||
|
|
||||||
|
init_options();
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_COMMON) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_SET) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_REGEX) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_ACCOUNT) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_MAILBOX) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_MESSAGE) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_OPTIONS) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_AUXILIARY) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (luaL_loadfile(lua, PATHNAME_DEPRECATED) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
|
||||||
|
if (opts.oneline != NULL) {
|
||||||
|
if (luaL_loadbuffer(lua, opts.oneline, strlen(opts.oneline),
|
||||||
|
"=<command line>") || lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
} else {
|
||||||
|
if (luaL_loadfile(lua, opts.config) ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0))
|
||||||
|
fatal(ERROR_CONFIG, "%s\n", lua_tostring(lua, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.interactive)
|
||||||
|
interactive_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the Lua interpreter.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
stop_lua(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_close(lua);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set default values to program's options.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
init_options(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_newtable(lua);
|
||||||
|
|
||||||
|
set_table_boolean("certificates", 1);
|
||||||
|
set_table_boolean("crammd5", 1);
|
||||||
|
set_table_boolean("create", 0);
|
||||||
|
set_table_boolean("expunge", 1);
|
||||||
|
set_table_number("keepalive", 29);
|
||||||
|
set_table_boolean("namespace", 1);
|
||||||
|
set_table_boolean("starttls", 1);
|
||||||
|
set_table_boolean("subscribe", 0);
|
||||||
|
set_table_number("timeout", 0);
|
||||||
|
|
||||||
|
lua_setglobal(lua, "options");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interactive mode.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
interactive_mode(void)
|
||||||
|
{
|
||||||
|
char buf[LINE_MAX];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
printf("> ");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (fgets(buf, sizeof(buf), stdin) == NULL) {
|
||||||
|
printf("\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (luaL_loadbuffer(lua, buf, strlen(buf), "=<line>") ||
|
||||||
|
lua_pcall(lua, 0, LUA_MULTRET, 0)) {
|
||||||
|
error("%s\n", lua_tostring(lua, -1));
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get from the configuration file the value of a boolean option variable.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
get_option_boolean(const char *opt)
|
||||||
|
{
|
||||||
|
int b;
|
||||||
|
|
||||||
|
lua_pushstring(lua, "options");
|
||||||
|
lua_gettable(lua, LUA_GLOBALSINDEX);
|
||||||
|
if (!lua_istable(lua, -1)) {
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lua_pushstring(lua, opt);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
b = lua_toboolean(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 2);
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get from the configuration file the value of a number option variable.
|
||||||
|
*/
|
||||||
|
lua_Number
|
||||||
|
get_option_number(const char *opt)
|
||||||
|
{
|
||||||
|
lua_Number n;
|
||||||
|
|
||||||
|
lua_pushstring(lua, "options");
|
||||||
|
lua_gettable(lua, LUA_GLOBALSINDEX);
|
||||||
|
if (!lua_istable(lua, -1)) {
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lua_pushstring(lua, opt);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
n = lua_tonumber(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 2);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get from the configuration file the value of a string option variable.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
get_option_string(const char *opt)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
lua_pushstring(lua, "options");
|
||||||
|
lua_gettable(lua, LUA_GLOBALSINDEX);
|
||||||
|
if (!lua_istable(lua, -1)) {
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lua_pushstring(lua, opt);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
s = lua_tostring(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 2);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the type of a table's element.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
get_table_type(const char *key)
|
||||||
|
{
|
||||||
|
int t;
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
t = lua_type(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the value of a table's element of type number.
|
||||||
|
*/
|
||||||
|
lua_Number
|
||||||
|
get_table_number(const char *key)
|
||||||
|
{
|
||||||
|
lua_Number n;
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
n = lua_tonumber(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the value of a table's element of type string.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
get_table_string(const char *key)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_gettable(lua, -2);
|
||||||
|
|
||||||
|
s = lua_tostring(lua, -1);
|
||||||
|
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a table's element value to nil.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
set_table_nil(const char *key)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_pushnil(lua);
|
||||||
|
lua_settable(lua, -3);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a table's element value to the specified boolean.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
set_table_boolean(const char *key, int value)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_pushboolean(lua, value);
|
||||||
|
lua_settable(lua, -3);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a table's element value to the specified number.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
set_table_number(const char *key, lua_Number value)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_pushnumber(lua, value);
|
||||||
|
lua_settable(lua, -3);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a table's element value to the specified string.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
set_table_string(const char *key, const char *value)
|
||||||
|
{
|
||||||
|
|
||||||
|
lua_pushstring(lua, key);
|
||||||
|
lua_pushstring(lua, value);
|
||||||
|
lua_settable(lua, -3);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,92 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A malloc() that checks the results and dies in case of error.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
ptr = (void *)malloc(size);
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
fatal(ERROR_MEMALLOC,
|
||||||
|
"allocating memory; %s\n", strerror(errno));
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A realloc() that checks the results and dies in case of error.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
xrealloc(void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
|
||||||
|
ptr = (void *)realloc(ptr, size);
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
fatal(ERROR_MEMALLOC,
|
||||||
|
"allocating memory; %s\n", strerror(errno));
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A free() that dies if fed with NULL pointer.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
xfree(void *ptr)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
fatal(ERROR_MEMALLOC,
|
||||||
|
"NULL pointer given as argument\n");
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A strdup() that checks the results and dies in case of error.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
xstrdup(const char *str)
|
||||||
|
{
|
||||||
|
char *dup;
|
||||||
|
|
||||||
|
dup = strdup(str);
|
||||||
|
|
||||||
|
if (dup == NULL)
|
||||||
|
fatal(ERROR_MEMALLOC, "allocating memory; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
|
||||||
|
return dup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A strndup() implementation that also checks the results and dies in case of
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
xstrndup(const char *str, size_t len)
|
||||||
|
{
|
||||||
|
char *dup;
|
||||||
|
|
||||||
|
dup = (char *)xmalloc((len + 1) * sizeof(char));
|
||||||
|
|
||||||
|
memcpy(dup, str, len);
|
||||||
|
|
||||||
|
dup[len] = '\0';
|
||||||
|
|
||||||
|
return dup;
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
-- The Message class that represents messages inside a mailbox.
|
||||||
|
|
||||||
|
Message = {}
|
||||||
|
|
||||||
|
Message._mt = {}
|
||||||
|
setmetatable(Message, Message._mt)
|
||||||
|
|
||||||
|
|
||||||
|
Message._mt.__call = function (self, account, mailbox, uid)
|
||||||
|
local object = {}
|
||||||
|
|
||||||
|
object._type = 'message'
|
||||||
|
|
||||||
|
for key, value in pairs(Message) do
|
||||||
|
if (type(value) == 'function') then
|
||||||
|
object[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
object._mt = {}
|
||||||
|
setmetatable(object, object._mt)
|
||||||
|
|
||||||
|
object._account = account
|
||||||
|
object._mailbox = mailbox
|
||||||
|
|
||||||
|
object._uid = uid
|
||||||
|
|
||||||
|
object._structure = nil
|
||||||
|
object._header = nil
|
||||||
|
object._body = nil
|
||||||
|
object._fields = {}
|
||||||
|
object._parts = {}
|
||||||
|
object._size = nil
|
||||||
|
object._date = nil
|
||||||
|
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Message.fetch_structure(self)
|
||||||
|
local r = self._mailbox._fetch_structure(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_header(self)
|
||||||
|
local r = self._mailbox._fetch_header(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_body(self)
|
||||||
|
local r = self._mailbox._fetch_body(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_message(self)
|
||||||
|
local r = self._mailbox._fetch_message(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_field(self, field)
|
||||||
|
local r = self._mailbox._fetch_fields(self._mailbox, { field },
|
||||||
|
{ self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_fields(self, fields)
|
||||||
|
local r = self._mailbox._fetch_fields(self._mailbox, fields, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_part(self, part)
|
||||||
|
local r = self._mailbox._fetch_parts(self._mailbox, { part }, self._uid)
|
||||||
|
return r[part]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_size(self)
|
||||||
|
local r = self._mailbox._fetch_size(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_date(self)
|
||||||
|
local r = self._mailbox._fetch_date(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Message.fetch_flags(self)
|
||||||
|
local r = self._mailbox._fetch_flags(self._mailbox, { self._uid })
|
||||||
|
return r[self._uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Message._mt.__index = function () end
|
||||||
|
Message._mt.__newindex = function () end
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An implementation of strstr() with case-insensitivity.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
xstrcasestr(const char *haystack, const char *needle)
|
||||||
|
{
|
||||||
|
const char *h, *n, *c;
|
||||||
|
size_t hl, nl;
|
||||||
|
|
||||||
|
c = haystack;
|
||||||
|
n = needle;
|
||||||
|
hl = strlen(haystack);
|
||||||
|
nl = strlen(needle);
|
||||||
|
|
||||||
|
while (hl >= nl) {
|
||||||
|
while (tolower((int)(*c)) != tolower((int)(*needle))) {
|
||||||
|
c++;
|
||||||
|
hl--;
|
||||||
|
if (hl < nl)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = c;
|
||||||
|
n = needle;
|
||||||
|
|
||||||
|
while (tolower((int)(*h)) == tolower((int)(*n))) {
|
||||||
|
h++;
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if (*n == '\0')
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
c++;
|
||||||
|
hl--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copies at most size characters from the string pointed by src to the array
|
||||||
|
* pointed by dest, always NULL terminating (unless size == 0). Returns
|
||||||
|
* pointer to dest.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
xstrncpy(char *dst, const char *src, size_t len)
|
||||||
|
{
|
||||||
|
char *d;
|
||||||
|
const char *s;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
d = dst;
|
||||||
|
s = src;
|
||||||
|
n = len;
|
||||||
|
|
||||||
|
while (n != 0) {
|
||||||
|
if ((*d++ = *s++) == '\0')
|
||||||
|
break;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0 && len != 0)
|
||||||
|
*d = '\0';
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
buffer nbuf; /* Namespace buffer. */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the names of personal mailboxes, using the namespace specified
|
||||||
|
* by the mail server, from internal to mail server format.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
apply_namespace(const char *mbox, char *prefix, char delim)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
if ((prefix == NULL && delim == '\0') ||
|
||||||
|
(prefix == NULL && delim == '/') ||
|
||||||
|
!strcasecmp(mbox, "INBOX"))
|
||||||
|
return mbox;
|
||||||
|
|
||||||
|
buffer_reset(&nbuf);
|
||||||
|
|
||||||
|
n = snprintf(nbuf.data, nbuf.size + 1, "%s%s", (prefix ? prefix : ""),
|
||||||
|
mbox);
|
||||||
|
if (n > (int)nbuf.size) {
|
||||||
|
buffer_check(&nbuf, n);
|
||||||
|
snprintf(nbuf.data, nbuf.size + 1, "%s%s",
|
||||||
|
(prefix ? prefix : ""), mbox);
|
||||||
|
}
|
||||||
|
c = nbuf.data;
|
||||||
|
while ((c = strchr(c, '/')))
|
||||||
|
*(c++) = delim;
|
||||||
|
|
||||||
|
debug("namespace: '%s' -> '%s'\n", mbox, nbuf.data);
|
||||||
|
|
||||||
|
return nbuf.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert the names of personal mailboxes, using the namespace specified by
|
||||||
|
* the mail server, from mail server format to internal format.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
reverse_namespace(const char *mbox, char *prefix, char delim)
|
||||||
|
{
|
||||||
|
int n, o;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
if ((prefix == NULL && delim == '\0') ||
|
||||||
|
(prefix == NULL && delim == '/') ||
|
||||||
|
!strcasecmp(mbox, "INBOX"))
|
||||||
|
return mbox;
|
||||||
|
|
||||||
|
buffer_reset(&nbuf);
|
||||||
|
|
||||||
|
o = strlen(prefix ? prefix : "");
|
||||||
|
if (strncasecmp(mbox, (prefix ? prefix : ""), o))
|
||||||
|
o = 0;
|
||||||
|
|
||||||
|
n = snprintf(nbuf.data, nbuf.size + 1, "%s", mbox + o);
|
||||||
|
if (n > (int)nbuf.size) {
|
||||||
|
buffer_check(&nbuf, n);
|
||||||
|
snprintf(nbuf.data, nbuf.size + 1, "%s", mbox + o);
|
||||||
|
}
|
||||||
|
c = nbuf.data;
|
||||||
|
while ((c = strchr(c, delim)))
|
||||||
|
*(c++) = '/';
|
||||||
|
|
||||||
|
debug("namespace: '%s' <- '%s'\n", mbox, nbuf.data);
|
||||||
|
|
||||||
|
return nbuf.data;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- Options related to the interface implementation.
|
||||||
|
|
||||||
|
options.cache = true
|
||||||
|
options.close = false
|
||||||
|
options.info = true
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef PATHNAMES_H
|
||||||
|
#define PATHNAMES_H
|
||||||
|
|
||||||
|
|
||||||
|
/* Program's home directory. */
|
||||||
|
#define PATHNAME_HOME ".imapfilter"
|
||||||
|
|
||||||
|
/* Program's configuration file. */
|
||||||
|
#define PATHNAME_CONFIG PATHNAME_HOME "/config.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter set functions file. */
|
||||||
|
#define PATHNAME_COMMON MAKEFILE_SHAREDIR "/common.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter set functions file. */
|
||||||
|
#define PATHNAME_SET MAKEFILE_SHAREDIR "/set.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter account functions file. */
|
||||||
|
#define PATHNAME_ACCOUNT MAKEFILE_SHAREDIR "/account.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter mailbox functions file. */
|
||||||
|
#define PATHNAME_MAILBOX MAKEFILE_SHAREDIR "/mailbox.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter message functions file. */
|
||||||
|
#define PATHNAME_MESSAGE MAKEFILE_SHAREDIR "/message.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter message functions file. */
|
||||||
|
#define PATHNAME_OPTIONS MAKEFILE_SHAREDIR "/options.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter regex functions file. */
|
||||||
|
#define PATHNAME_REGEX MAKEFILE_SHAREDIR "/regex.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter auxiliary functions file. */
|
||||||
|
#define PATHNAME_AUXILIARY MAKEFILE_SHAREDIR "/auxiliary.lua"
|
||||||
|
|
||||||
|
/* Lua imapfilter old interface functions file. */
|
||||||
|
#define PATHNAME_DEPRECATED MAKEFILE_SHAREDIR "/deprecated.lua"
|
||||||
|
|
||||||
|
/* SSL/TLS certificates file. */
|
||||||
|
#define PATHNAME_CERTS PATHNAME_HOME "/certificates"
|
||||||
|
|
||||||
|
/* Debug temporary file template. */
|
||||||
|
#define PATHNAME_DEBUG PATHNAME_HOME "/debug.XXXXXX"
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PATHNAMES_H */
|
|
@ -0,0 +1,268 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
|
||||||
|
#include <pcre.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int ifre_flags(lua_State *lua);
|
||||||
|
static int ifre_compile(lua_State *lua);
|
||||||
|
static int ifre_exec(lua_State *lua);
|
||||||
|
static int ifre_free(lua_State *lua);
|
||||||
|
|
||||||
|
/* Lua imapfilter library of PCRE related functions. */
|
||||||
|
static const luaL_reg ifrelib[] = {
|
||||||
|
{ "flags", ifre_flags },
|
||||||
|
{ "compile", ifre_compile },
|
||||||
|
{ "exec", ifre_exec },
|
||||||
|
{ "free", ifre_free },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return PCRE available compile and exec flags.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifre_flags(lua_State *lua)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 0)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
lua_newtable(lua);
|
||||||
|
|
||||||
|
#ifdef PCRE_CASELESS
|
||||||
|
set_table_number("CASELESS", PCRE_CASELESS);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_MULTILINE
|
||||||
|
set_table_number("MULTILINE", PCRE_MULTILINE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_DOTALL
|
||||||
|
set_table_number("DOTALL", PCRE_DOTALL);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_EXTENDED
|
||||||
|
set_table_number("EXTENDED", PCRE_EXTENDED);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_ANCHORED
|
||||||
|
set_table_number("ANCHORED", PCRE_ANCHORED);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_DOLLAR_ENDONLY
|
||||||
|
set_table_number("DOLLAR_ENDONLY", PCRE_DOLLAR_ENDONLY);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_EXTRA
|
||||||
|
set_table_number("EXTRA", PCRE_EXTRA);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NOTBOL
|
||||||
|
set_table_number("NOTBOL", PCRE_NOTBOL);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NOTEOL
|
||||||
|
set_table_number("NOTEOL", PCRE_NOTEOL);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_UNGREEDY
|
||||||
|
set_table_number("UNGREEDY", PCRE_UNGREEDY);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NOTEMPTY
|
||||||
|
set_table_number("NOTEMPTY", PCRE_NOTEMPTY);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_UTF8
|
||||||
|
set_table_number("UTF8", PCRE_UTF8);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NO_AUTO_CAPTURE
|
||||||
|
set_table_number("NO_AUTO_CAPTURE", PCRE_NO_AUTO_CAPTURE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NO_UTF8_CHECK
|
||||||
|
set_table_number("NO_UTF8_CHECK", PCRE_NO_UTF8_CHECK);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_FIRSTLINE
|
||||||
|
set_table_number("FIRSTLINE", PCRE_FIRSTLINE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_AUTO_CALLOUT
|
||||||
|
set_table_number("AUTO_CALLOUT", PCRE_AUTO_CALLOUT);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_PARTIAL
|
||||||
|
set_table_number("PARTIAL", PCRE_PARTIAL);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_DFA_SHORTEST
|
||||||
|
set_table_number("DFA_SHORTEST", PCRE_DFA_SHORTEST);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_DFA_RESTART
|
||||||
|
set_table_number("DFA_RESTART", PCRE_DFA_RESTART);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_FIRSTLINE
|
||||||
|
set_table_number("FIRSTLINE", PCRE_FIRSTLINE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_DUPNAMES
|
||||||
|
set_table_number("DUPNAMES", PCRE_DUPNAMES);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NEWLINE_CR
|
||||||
|
set_table_number("NEWLINE_CR)", PCRE_NEWLINE_CR);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NEWLINE_LF
|
||||||
|
set_table_number("NEWLINE_LF", PCRE_NEWLINE_LF);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NEWLINE_CRLF
|
||||||
|
set_table_number("NEWLINE_CRLF", PCRE_NEWLINE_CRLF);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NEWLINE_ANY
|
||||||
|
set_table_number("NEWLINE_ANY", PCRE_NEWLINE_ANY);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NEWLINE_ANYCRLF
|
||||||
|
set_table_number("NEWLINE_ANYCRLF", PCRE_NEWLINE_ANYCRLF);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_BSR_ANYCRLF
|
||||||
|
set_table_number("PCRE_BSR_ANYCRLF", PCRE_BSR_ANYCRLF);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_BSR_UNICODE
|
||||||
|
set_table_number("PCRE_BSR_UNICODE", PCRE_BSR_UNICODE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_JAVASCRIPT_COMPAT
|
||||||
|
set_table_number("PCRE_JAVASCRIPT_COMPAT", PCRE_JAVASCRIPT_COMPAT);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NO_START_OPTIMIZE
|
||||||
|
set_table_number("PCRE_NO_START_OPTIMIZE", PCRE_NO_START_OPTIMIZE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NO_START_OPTIMISE
|
||||||
|
set_table_number("PCRE_NO_START_OPTIMISE", PCRE_NO_START_OPTIMISE);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_PARTIAL_HARD
|
||||||
|
set_table_number("PCRE_PARTIAL_HARD", PCRE_PARTIAL_HARD);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_NOTEMPTY_ATSTART
|
||||||
|
set_table_number("PCRE_NOTEMPTY_ATSTART", PCRE_NOTEMPTY_ATSTART);
|
||||||
|
#endif
|
||||||
|
#ifdef PCRE_UCP
|
||||||
|
set_table_number("PCRE_UCP", PCRE_UCP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the PCRE compile function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifre_compile(lua_State *lua)
|
||||||
|
{
|
||||||
|
pcre **re;
|
||||||
|
const char *error;
|
||||||
|
int erroffset;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 2)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TSTRING);
|
||||||
|
luaL_checktype(lua, 2, LUA_TNUMBER);
|
||||||
|
|
||||||
|
re = (pcre **)(lua_newuserdata(lua, sizeof(pcre *)));
|
||||||
|
|
||||||
|
*re = pcre_compile(lua_tostring(lua, 1), lua_tonumber(lua, 2), &error,
|
||||||
|
&erroffset, NULL);
|
||||||
|
|
||||||
|
if (*re == NULL) {
|
||||||
|
fprintf(stderr, "RE failed at offset %d: %s\n", erroffset,
|
||||||
|
error);
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
|
||||||
|
lua_pushboolean(lua, (*re != NULL));
|
||||||
|
lua_insert(lua, 1);
|
||||||
|
|
||||||
|
return (*re != NULL ? 2 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the PCRE exec function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifre_exec(lua_State *lua)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
pcre *re;
|
||||||
|
int ovecsize;
|
||||||
|
int *ovector;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 3)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TUSERDATA);
|
||||||
|
luaL_checktype(lua, 2, LUA_TSTRING);
|
||||||
|
luaL_checktype(lua, 3, LUA_TNUMBER);
|
||||||
|
|
||||||
|
re = *(pcre **)(lua_touserdata(lua, 1));
|
||||||
|
|
||||||
|
pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &ovecsize);
|
||||||
|
|
||||||
|
ovector = (int *)xmalloc(sizeof(int) * (ovecsize + 1) * 3);
|
||||||
|
|
||||||
|
for (i = 0; i <= ovecsize; i++)
|
||||||
|
ovector[2 * i] = ovector[2 * i + 1] = -1;
|
||||||
|
|
||||||
|
n = pcre_exec(re, NULL, lua_tostring(lua, 2), lua_strlen(lua, 2), 0,
|
||||||
|
lua_tonumber(lua, 3), ovector, (ovecsize + 1) * 3);
|
||||||
|
|
||||||
|
if (n > 0)
|
||||||
|
for (i = 1; i < n; i++)
|
||||||
|
if (ovector[2 * i] != -1 && ovector[2 * i + 1] != -1)
|
||||||
|
lua_pushlstring(lua, lua_tostring(lua, 2) +
|
||||||
|
ovector[2 * i], ovector[2 * i + 1] -
|
||||||
|
ovector[2 * i]);
|
||||||
|
else
|
||||||
|
lua_pushnil(lua);
|
||||||
|
|
||||||
|
xfree(ovector);
|
||||||
|
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
|
||||||
|
lua_pushboolean(lua, (n > 0));
|
||||||
|
lua_insert(lua, 1);
|
||||||
|
|
||||||
|
return (n > 0 ? n : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of a PCRE free function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifre_free(lua_State *lua)
|
||||||
|
{
|
||||||
|
pcre *re;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 1)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TUSERDATA);
|
||||||
|
|
||||||
|
re = *(pcre **)(lua_touserdata(lua, 1));
|
||||||
|
|
||||||
|
xfree(re);
|
||||||
|
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open imapfilter library of PCRE related functions.
|
||||||
|
*/
|
||||||
|
LUALIB_API int
|
||||||
|
luaopen_ifre(lua_State *lua)
|
||||||
|
{
|
||||||
|
|
||||||
|
luaL_register(lua, "ifre", ifrelib);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
-- A simple wrapper for PCRE that uses a cache for compiled expressions.
|
||||||
|
|
||||||
|
_regex_cache = {}
|
||||||
|
|
||||||
|
_regex_cache.mt = {}
|
||||||
|
setmetatable(_regex_cache, _regex_cache.mt)
|
||||||
|
|
||||||
|
|
||||||
|
_regex_cache.mt.__index = function (self, key)
|
||||||
|
local _, _, pattern, cflags = string.find(key, '^(.*)%z(.*)$')
|
||||||
|
|
||||||
|
local _, compiled = ifre.compile(pattern, tonumber(cflags))
|
||||||
|
|
||||||
|
self[key] = compiled
|
||||||
|
|
||||||
|
return compiled
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function regex_search(pattern, subject, cflags, eflags)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
_check_required(subject, 'string')
|
||||||
|
_check_optional(cflags, 'table')
|
||||||
|
_check_optional(eflags, 'table')
|
||||||
|
|
||||||
|
if (cflags == nil) then
|
||||||
|
cflags = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if (eflags == nil) then
|
||||||
|
eflags = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local flags = ifre.flags()
|
||||||
|
|
||||||
|
cf = 0
|
||||||
|
for _, f in ipairs(cflags) do
|
||||||
|
cf = cf + flags[f]
|
||||||
|
end
|
||||||
|
|
||||||
|
ef = 0
|
||||||
|
for _, f in pairs(eflags) do
|
||||||
|
ef = cf + flags[f]
|
||||||
|
end
|
||||||
|
|
||||||
|
local compiled = _regex_cache[pattern .. '\0' .. cf]
|
||||||
|
|
||||||
|
if (compiled == nil) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return ifre.exec(compiled, subject, ef)
|
||||||
|
end
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "regexp.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile all the patterns and allocate the necessary space for the substring
|
||||||
|
* matching.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
regexp_compile(regexp *reg)
|
||||||
|
{
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
for (re = reg; re->pattern != NULL; re++) {
|
||||||
|
re->preg = (regex_t *)xmalloc(sizeof(regex_t));
|
||||||
|
regcomp(re->preg, re->pattern, REG_EXTENDED | REG_ICASE);
|
||||||
|
re->nmatch = re->preg->re_nsub + 1;
|
||||||
|
re->pmatch = (regmatch_t *)xmalloc(sizeof(regmatch_t) *
|
||||||
|
re->nmatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the compiled regular expressions and the space allocated for the
|
||||||
|
* substring matching.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
regexp_free(regexp *reg)
|
||||||
|
{
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
for (re = reg; re->pattern != NULL; re++) {
|
||||||
|
regfree(re->preg);
|
||||||
|
xfree(re->preg);
|
||||||
|
xfree(re->pmatch);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef REGEXP_H
|
||||||
|
#define REGEXP_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Regular expression convenience structure. */
|
||||||
|
typedef struct regexp {
|
||||||
|
const char *pattern; /* Regular expression pattern string. */
|
||||||
|
regex_t *preg; /* Compiled regular expression. */
|
||||||
|
size_t nmatch; /* Number of subexpressions in pattern. */
|
||||||
|
regmatch_t *pmatch; /* Structure for substrings that matched. */
|
||||||
|
} regexp;
|
||||||
|
|
||||||
|
|
||||||
|
/* regexp.c */
|
||||||
|
void regexp_compile(regexp *reg);
|
||||||
|
void regexp_free(regexp *reg);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* REGEXP_H */
|
|
@ -0,0 +1,918 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern options opts;
|
||||||
|
|
||||||
|
|
||||||
|
int create_mailbox(session *ssn, const char *mbox);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset any inactivity autologout timer on the server.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_noop(const char *server, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_noop(s))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to the server, login to the IMAP server, get it's capabilities, get
|
||||||
|
* the namespace of the mailboxes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_login(const char *server, const char *port, const char *ssl,
|
||||||
|
const char *user, const char *pass)
|
||||||
|
{
|
||||||
|
int r = -1, rg = -1;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if ((s = session_find(server, port, user)))
|
||||||
|
return STATUS_RESPONSE_NONE;
|
||||||
|
|
||||||
|
s = session_new();
|
||||||
|
|
||||||
|
s->server = xstrdup(server);
|
||||||
|
s->port = xstrdup(port);
|
||||||
|
s->username = xstrdup(user);
|
||||||
|
|
||||||
|
if (ssl && strncasecmp(ssl, "tls1", 4) &&
|
||||||
|
strncasecmp(ssl, "ssl3", 4) && strncasecmp(ssl, "ssl2", 4))
|
||||||
|
ssl = NULL;
|
||||||
|
|
||||||
|
if (open_connection(s, server, port, ssl) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if ((rg = response_greeting(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (opts.debug > 0)
|
||||||
|
if (response_generic(s, imap_noop(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (response_capability(s, imap_capability(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
if (!ssl && s->capabilities & CAPABILITY_STARTTLS &&
|
||||||
|
get_option_boolean("starttls"))
|
||||||
|
switch (response_generic(s, imap_starttls(s))) {
|
||||||
|
case STATUS_RESPONSE_OK:
|
||||||
|
if (open_secure_connection(s, server, port, "tls1")
|
||||||
|
== -1)
|
||||||
|
goto fail;
|
||||||
|
if (response_capability(s, imap_capability(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rg != STATUS_RESPONSE_PREAUTH) {
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
if (s->capabilities & CAPABILITY_CRAMMD5 &&
|
||||||
|
get_option_boolean("crammd5")) {
|
||||||
|
if ((r = auth_cram_md5(s, user, pass)) == -1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (r != STATUS_RESPONSE_OK &&
|
||||||
|
(r = response_generic(s, imap_login(s, user, pass))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (r == STATUS_RESPONSE_NO) {
|
||||||
|
error("username %s or password rejected at %s\n",
|
||||||
|
user, server);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r = STATUS_RESPONSE_PREAUTH;
|
||||||
|
}
|
||||||
|
if (s->capabilities & CAPABILITY_NAMESPACE &&
|
||||||
|
get_option_boolean("namespace")) {
|
||||||
|
if (response_namespace(s, imap_namespace(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Logout from the IMAP server and disconnect from the server.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_logout(const char *server, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
r = response_generic(s, imap_logout(s));
|
||||||
|
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get mailbox's status.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_status(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox, unsigned int *exists, unsigned int *recent,
|
||||||
|
unsigned int *unseen, unsigned int *uidnext)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if (s->protocol == PROTOCOL_IMAP4REV1) {
|
||||||
|
t = imap_status(s, m, "MESSAGES RECENT UNSEEN UIDNEXT");
|
||||||
|
if ((r = response_status(s, t, exists, recent, unseen, uidnext)) == -1)
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
t = imap_examine(s, m);
|
||||||
|
if ((r = response_examine(s, t, exists, recent)) == -1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open mailbox in read-write mode.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_select(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_select(s, imap_select(s, m))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close examined/selected mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_close(const char *server, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_close(s))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove all messages marked for deletion from selected mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_expunge(const char *server, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_expunge(s))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List available mailboxes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_list(const char *server, const char *port, const char *user,
|
||||||
|
const char *refer, const char *name, char **mboxs, char **folders)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
const char *n;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n = apply_namespace(name, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
t = imap_list(s, refer, n);
|
||||||
|
if ((r = response_list(s, t, mboxs, folders)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List subscribed mailboxes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_lsub(const char *server, const char *port, const char *user,
|
||||||
|
const char *refer, const char *name, char **mboxs, char **folders)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
const char *n;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n = apply_namespace(name, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
t = imap_lsub(s, refer, n);
|
||||||
|
if ((r = response_list(s, t, mboxs, folders)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search selected mailbox according to the supplied search criteria.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_search(const char *server, const char *port, const char *user,
|
||||||
|
const char *criteria, const char *charset, char **mesgs)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_search(s, charset, criteria);
|
||||||
|
if ((r = response_search(s, t, mesgs)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the FLAGS, INTERNALDATE and RFC822.SIZE of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchfast(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **flags, char **date, char **size)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "FAST");
|
||||||
|
if ((r = response_fetchfast(s, t, flags, date, size)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the FLAGS of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchflags(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **flags)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "FLAGS");
|
||||||
|
if ((r = response_fetchflags(s, t, flags)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the INTERNALDATE of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchdate(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **date)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "INTERNALDATE");
|
||||||
|
if ((r = response_fetchdate(s, t, date)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Fetch the RFC822.SIZE of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchsize(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **size)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "RFC822.SIZE");
|
||||||
|
if ((r = response_fetchsize(s, t, size)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the body structure, ie. BODYSTRUCTURE, of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchstructure(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **structure)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "BODYSTRUCTURE");
|
||||||
|
if ((r = response_fetchstructure(s, t, structure)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the header, ie. BODY[HEADER], of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchheader(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **header, size_t *len)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "BODY.PEEK[HEADER]");
|
||||||
|
if ((r = response_fetchbody(s, t, header, len)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the text, ie. BODY[TEXT], of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchtext(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, char **text, size_t *len)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, "BODY.PEEK[TEXT]");
|
||||||
|
if ((r = response_fetchbody(s, t, text, len)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the specified header fields, ie. BODY[HEADER.FIELDS (<fields>)], of
|
||||||
|
* the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchfields(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *headerfields, char **fields, size_t *len)
|
||||||
|
{
|
||||||
|
int t, r, n;
|
||||||
|
session *s;
|
||||||
|
char *f;
|
||||||
|
|
||||||
|
n = strlen("BODY.PEEK[HEADER.FIELDS ()]") + strlen(headerfields) + 1;
|
||||||
|
f = (char *)xmalloc(n * sizeof(char));
|
||||||
|
snprintf(f, n, "%s%s%s", "BODY.PEEK[HEADER.FIELDS (", headerfields, ")]");
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, f);
|
||||||
|
if ((r = response_fetchbody(s, t, fields, len)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
xfree(f);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the specified message part, ie. BODY[<part>], of the
|
||||||
|
* messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_fetchpart(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *part, char **bodypart, size_t *len)
|
||||||
|
{
|
||||||
|
int t, r, n;
|
||||||
|
session *s;
|
||||||
|
char *f;
|
||||||
|
|
||||||
|
n = strlen("BODY.PEEK[]") + strlen(part) + 1;
|
||||||
|
f = (char *)xmalloc(n * sizeof(char));
|
||||||
|
snprintf(f, n, "%s%s%s", "BODY.PEEK[", part, "]");
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_fetch(s, mesg, f);
|
||||||
|
if ((r = response_fetchbody(s, t, bodypart, len)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
xfree(f);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add, remove or replace the specified flags of the messages.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_store(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *mode, const char *flags)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
t = imap_store(s, mesg, mode, flags);
|
||||||
|
if ((r = response_generic(s, t)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (xstrcasestr(flags, "\\Deleted") && get_option_boolean("expunge"))
|
||||||
|
if (response_generic(s, imap_expunge(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the specified messages to another mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_copy(const char *server, const char *port, const char *user,
|
||||||
|
const char *mesg, const char *mbox)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
do {
|
||||||
|
t = imap_copy(s, mesg, m);
|
||||||
|
switch (r = response_generic(s, t)) {
|
||||||
|
case STATUS_RESPONSE_TRYCREATE:
|
||||||
|
if (create_mailbox(s, mbox) == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (r == STATUS_RESPONSE_TRYCREATE);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append supplied message to the specified mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_append(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox, const char *mesg, size_t mesglen, const char *flags,
|
||||||
|
const char *date)
|
||||||
|
{
|
||||||
|
int t, r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if ((t = imap_append(s, m, flags, date, mesglen)) == -1)
|
||||||
|
goto fail;
|
||||||
|
if ((r = response_continuation(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
switch (r) {
|
||||||
|
case STATUS_RESPONSE_CONTINUE:
|
||||||
|
if (imap_continuation(s, mesg, mesglen) == -1)
|
||||||
|
goto fail;
|
||||||
|
if ((r = response_generic(s, t)) == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case STATUS_RESPONSE_TRYCREATE:
|
||||||
|
if (create_mailbox(s, mbox) == -1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (r == STATUS_RESPONSE_TRYCREATE);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the specified mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_create(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_create(s, m))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete the specified mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_delete(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_delete(s, m))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rename a mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_rename(const char *server, const char *port, const char *user,
|
||||||
|
const char *oldmbox, const char *newmbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
char *o, *n;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
o = xstrdup(apply_namespace(oldmbox, s->ns.prefix, s->ns.delim));
|
||||||
|
n = xstrdup(apply_namespace(newmbox, s->ns.prefix, s->ns.delim));
|
||||||
|
|
||||||
|
r = response_generic(s, imap_rename(s, o, n));
|
||||||
|
|
||||||
|
xfree(o);
|
||||||
|
xfree(n);
|
||||||
|
|
||||||
|
if (r == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subscribe the specified mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_subscribe(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_subscribe(s, m))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unsubscribe the specified mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
request_unsubscribe(const char *server, const char *port, const char *user,
|
||||||
|
const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
session *s;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, s->ns.prefix, s->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_generic(s, imap_unsubscribe(s, m))) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
request_idle(const char *server, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
int t, rg, ri;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
if (!(s = session_find(server, port, user)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
if (!(s->capabilities & CAPABILITY_IDLE))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ri = 0;
|
||||||
|
|
||||||
|
t = imap_idle(s);
|
||||||
|
|
||||||
|
if ((rg = response_continuation(s)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (rg == STATUS_RESPONSE_CONTINUE) {
|
||||||
|
if ((ri = response_idle(s, t)) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
imap_done(s);
|
||||||
|
|
||||||
|
if ((rg = response_generic(s, t)) == -1)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} while (ri == STATUS_RESPONSE_TIMEOUT);
|
||||||
|
|
||||||
|
return rg;
|
||||||
|
fail:
|
||||||
|
close_connection(s);
|
||||||
|
session_destroy(s);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Auxiliary function to create a mailbox.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
create_mailbox(session *ssn, const char *mbox)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
const char *m;
|
||||||
|
|
||||||
|
m = apply_namespace(mbox, ssn->ns.prefix, ssn->ns.delim);
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, imap_create(ssn, m))) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (get_option_boolean("subscribe"))
|
||||||
|
if (response_generic(ssn, imap_subscribe(ssn, m)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,846 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
#include "regexp.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern options opts;
|
||||||
|
|
||||||
|
buffer ibuf; /* Input buffer. */
|
||||||
|
enum { /* Server data responses to be parsed;
|
||||||
|
* regular expressions index. */
|
||||||
|
DATA_RESPONSE_TAGGED,
|
||||||
|
DATA_RESPONSE_CAPABILITY,
|
||||||
|
DATA_RESPONSE_AUTHENTICATE,
|
||||||
|
DATA_RESPONSE_NAMESPACE,
|
||||||
|
DATA_RESPONSE_STATUS,
|
||||||
|
DATA_RESPONSE_STATUS_MESSAGES,
|
||||||
|
DATA_RESPONSE_STATUS_RECENT,
|
||||||
|
DATA_RESPONSE_STATUS_UNSEEN,
|
||||||
|
DATA_RESPONSE_STATUS_UIDNEXT,
|
||||||
|
DATA_RESPONSE_EXAMINE_EXISTS,
|
||||||
|
DATA_RESPONSE_EXAMINE_RECENT,
|
||||||
|
DATA_RESPONSE_LIST,
|
||||||
|
DATA_RESPONSE_SEARCH,
|
||||||
|
DATA_RESPONSE_FETCH,
|
||||||
|
DATA_RESPONSE_FETCH_FLAGS,
|
||||||
|
DATA_RESPONSE_FETCH_DATE,
|
||||||
|
DATA_RESPONSE_FETCH_SIZE,
|
||||||
|
DATA_RESPONSE_FETCH_STRUCTURE,
|
||||||
|
DATA_RESPONSE_FETCH_BODY,
|
||||||
|
DATA_RESPONSE_IDLE,
|
||||||
|
};
|
||||||
|
regexp responses[] = { /* Server data responses to be parsed;
|
||||||
|
* regular expressions patterns. */
|
||||||
|
{ "([[:xdigit:]]{4,4}) (OK|NO|BAD) [[:print:]]*\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* CAPABILITY ([[:print:]]*)\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\+ ([[:graph:]]*)\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* NAMESPACE (NIL|\\(\\(\"([[:graph:]]*)\" \"([[:print:]])\"\\)"
|
||||||
|
"[[:print:]]*\\)) (NIL|\\([[:print:]]*\\)) (NIL|\\([[:print:]]*\\))"
|
||||||
|
"\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* STATUS [[:print:]]* \\(([[:alnum:] ]*)\\) *\r\n", NULL, 0, NULL },
|
||||||
|
{ "MESSAGES ([[:digit:]]+)", NULL, 0, NULL },
|
||||||
|
{ "RECENT ([[:digit:]]+)", NULL, 0, NULL },
|
||||||
|
{ "UNSEEN ([[:digit:]]+)", NULL, 0, NULL },
|
||||||
|
{ "UIDNEXT ([[:digit:]]+)", NULL, 0, NULL },
|
||||||
|
{ "\\* ([[:digit:]]+) EXISTS\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* ([[:digit:]]+) RECENT\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* (LIST|LSUB) \\(([[:print:]]*)\\) (\"[[:print:]]\"|NIL) "
|
||||||
|
"(\"([[:print:]]+)\"|([[:print:]]+)|\\{([[:digit:]]+)\\}\r\n"
|
||||||
|
"([[:print:]]*))\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* SEARCH ?([[:digit:] ]*)\r\n", NULL, 0, NULL },
|
||||||
|
{ "\\* [[:digit:]]+ FETCH \\(([[:print:]]*)\\)\r\n", NULL, 0, NULL },
|
||||||
|
{ "FLAGS \\(([[:print:]]*)\\)", NULL, 0, NULL },
|
||||||
|
{ "INTERNALDATE \"([[:print:]]*)\"", NULL, 0, NULL },
|
||||||
|
{ "RFC822.SIZE ([[:digit:]]+)", NULL, 0, NULL },
|
||||||
|
{ "BODYSTRUCTURE (\\([[:print:]]+\\))", NULL, 0, NULL },
|
||||||
|
{ "\\* [[:digit:]]+ FETCH \\([[:print:]]*BODY\\[[[:print:]]*\\] "
|
||||||
|
"(\\{([[:digit:]]+)\\}\r\n|\"([[:print:]]*)\")", NULL, 0, NULL },
|
||||||
|
{ "\\* [[:digit:]]+ (RECENT|EXISTS)\r\n", NULL, 0, NULL },
|
||||||
|
{ NULL, NULL, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int receive_response(session *ssn, char *buf, long timeout, int timeoutfail);
|
||||||
|
|
||||||
|
int check_tag(char *buf, session *ssn, int tag);
|
||||||
|
int check_bye(char *buf);
|
||||||
|
int check_continuation(char *buf);
|
||||||
|
int check_trycreate(char *buf);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read data the server sent.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
receive_response(session *ssn, char *buf, long timeout, int timeoutfail)
|
||||||
|
{
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
if ((n = socket_read(ssn, buf, INPUT_BUF, timeout ? timeout :
|
||||||
|
(long)(get_option_number("timeout")), timeoutfail)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
if (opts.debug > 0) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
debug("getting response (%d):\n\n", ssn->socket);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
debugc(buf[i]);
|
||||||
|
|
||||||
|
debug("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search for tagged response in the data that the server sent.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_tag(char *buf, session *ssn, int tag)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char t[4 + 1];
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
r = STATUS_RESPONSE_NONE;
|
||||||
|
|
||||||
|
snprintf(t, sizeof(t), "%04X", tag);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_TAGGED];
|
||||||
|
|
||||||
|
if (!regexec(re->preg, buf, re->nmatch, re->pmatch, 0)) {
|
||||||
|
if (!strncasecmp(buf + re->pmatch[1].rm_so, t,
|
||||||
|
strlen(t))) {
|
||||||
|
if (!strncasecmp(buf + re->pmatch[2].rm_so,
|
||||||
|
"OK", strlen("OK")))
|
||||||
|
r = STATUS_RESPONSE_OK;
|
||||||
|
else if (!strncasecmp(buf + re->pmatch[2].rm_so,
|
||||||
|
"NO", strlen("NO")))
|
||||||
|
r = STATUS_RESPONSE_NO;
|
||||||
|
else if (!strncasecmp(buf + re->pmatch[2].rm_so,
|
||||||
|
"BAD", strlen("BAD")))
|
||||||
|
r = STATUS_RESPONSE_BAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r != STATUS_RESPONSE_NONE)
|
||||||
|
verbose("S (%d): %s", ssn->socket, buf + re->pmatch[0].rm_so);
|
||||||
|
|
||||||
|
if (r == STATUS_RESPONSE_NO || r == STATUS_RESPONSE_BAD)
|
||||||
|
error("IMAP (%d): %s", ssn->socket, buf + re->pmatch[0].rm_so);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if server sent a BYE response (connection is closed immediately).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_bye(char *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (xstrcasestr(buf, "* BYE") &&
|
||||||
|
!xstrcasestr(buf, " LOGOUT "))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if server sent a PREAUTH response (connection already authenticated
|
||||||
|
* by external means).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_preauth(char *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (xstrcasestr(ibuf.data, "* PREAUTH"))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the server sent a continuation request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_continuation(char *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((buf[0] == '+' && buf[1] == ' ') || xstrcasestr(buf, "\r\n+ "))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the server sent a TRYCREATE response.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
check_trycreate(char *buf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (xstrcasestr(buf, "[TRYCREATE]"))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get server data and make sure there is a tagged response inside them.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_generic(session *ssn, int tag)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
if (tag == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer_reset(&ibuf);
|
||||||
|
|
||||||
|
do {
|
||||||
|
buffer_check(&ibuf, ibuf.len + INPUT_BUF);
|
||||||
|
if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
|
||||||
|
return -1;
|
||||||
|
ibuf.len += n;
|
||||||
|
|
||||||
|
if (check_bye(ibuf.data))
|
||||||
|
return -1;
|
||||||
|
} while ((r = check_tag(ibuf.data, ssn, tag)) == STATUS_RESPONSE_NONE);
|
||||||
|
|
||||||
|
if (r == STATUS_RESPONSE_NO &&
|
||||||
|
(check_trycreate(ibuf.data) || get_option_boolean("create")))
|
||||||
|
return STATUS_RESPONSE_TRYCREATE;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get server data and make sure there is a continuation response inside them.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_continuation(session *ssn)
|
||||||
|
{
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
buffer_reset(&ibuf);
|
||||||
|
|
||||||
|
do {
|
||||||
|
buffer_check(&ibuf, ibuf.len + INPUT_BUF);
|
||||||
|
if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
|
||||||
|
return -1;
|
||||||
|
ibuf.len += n;
|
||||||
|
|
||||||
|
if (check_bye(ibuf.data))
|
||||||
|
return -1;
|
||||||
|
} while (!check_continuation(ibuf.data));
|
||||||
|
|
||||||
|
return STATUS_RESPONSE_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the greeting that server sends during connection.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_greeting(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
buffer_reset(&ibuf);
|
||||||
|
|
||||||
|
if (receive_response(ssn, ibuf.data, 0, 1) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
verbose("S (%d): %s", ssn->socket, ibuf.data);
|
||||||
|
|
||||||
|
if (check_bye(ibuf.data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (check_preauth(ibuf.data))
|
||||||
|
return STATUS_RESPONSE_PREAUTH;
|
||||||
|
|
||||||
|
return STATUS_RESPONSE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP CAPABILITY client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_capability(session *ssn, int tag)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ssn->protocol = PROTOCOL_NONE;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_CAPABILITY];
|
||||||
|
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
if (xstrcasestr(s, "IMAP4rev1"))
|
||||||
|
ssn->protocol = PROTOCOL_IMAP4REV1;
|
||||||
|
else if (xstrcasestr(s, "IMAP4"))
|
||||||
|
ssn->protocol = PROTOCOL_IMAP4;
|
||||||
|
else {
|
||||||
|
error("server supports neither the IMAP4rev1 nor the "
|
||||||
|
"IMAP4 protocol\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssn->capabilities = CAPABILITY_NONE;
|
||||||
|
|
||||||
|
if (xstrcasestr(s, "NAMESPACE"))
|
||||||
|
ssn->capabilities |= CAPABILITY_NAMESPACE;
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
if (xstrcasestr(s, "AUTH=CRAM-MD5"))
|
||||||
|
ssn->capabilities |= CAPABILITY_CRAMMD5;
|
||||||
|
#endif
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
if (xstrcasestr(s, "STARTTLS"))
|
||||||
|
ssn->capabilities |= CAPABILITY_STARTTLS;
|
||||||
|
#endif
|
||||||
|
if (xstrcasestr(s, "CHILDREN"))
|
||||||
|
ssn->capabilities |= CAPABILITY_CHILDREN;
|
||||||
|
|
||||||
|
if (xstrcasestr(s, "IDLE"))
|
||||||
|
ssn->capabilities |= CAPABILITY_IDLE;
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_CRAMMD5
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP AUTHENTICATE client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_authenticate(session *ssn, int tag, unsigned char **cont)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_AUTHENTICATE];
|
||||||
|
|
||||||
|
if ((r = response_continuation(ssn)) == STATUS_RESPONSE_CONTINUE &&
|
||||||
|
!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
|
||||||
|
*cont = (unsigned char *)xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP NAMESPACE client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_namespace(session *ssn, int tag)
|
||||||
|
{
|
||||||
|
int r, n;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ssn->ns.prefix = NULL;
|
||||||
|
ssn->ns.delim = '\0';
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_NAMESPACE];
|
||||||
|
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
n = re->pmatch[2].rm_eo - re->pmatch[2].rm_so;
|
||||||
|
if (n > 0)
|
||||||
|
ssn->ns.prefix = xstrndup(ibuf.data +
|
||||||
|
re->pmatch[2].rm_so, n);
|
||||||
|
ssn->ns.delim = *(ibuf.data + re->pmatch[3].rm_so);
|
||||||
|
}
|
||||||
|
debug("namespace (%d): '%s' '%c'\n", ssn->socket,
|
||||||
|
(ssn->ns.prefix ? ssn->ns.prefix : ""), ssn->ns.delim);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP STATUS client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_status(session *ssn, int tag, unsigned int *exist,
|
||||||
|
unsigned int *recent, unsigned int *unseen, unsigned int *uidnext)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_STATUS];
|
||||||
|
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_STATUS_MESSAGES];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*exist = strtol(s + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_STATUS_RECENT];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*recent = strtol(s + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_STATUS_UNSEEN];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*unseen = strtol(s + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_STATUS_UIDNEXT];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*uidnext = strtol(s + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP EXAMINE client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_examine(session *ssn, int tag, unsigned int *exist,
|
||||||
|
unsigned int *recent)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_EXAMINE_EXISTS];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
|
||||||
|
*exist = strtol(ibuf.data + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_EXAMINE_RECENT];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0))
|
||||||
|
*recent = strtol(ibuf.data + re->pmatch[1].rm_so, NULL, 10);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP SELECT client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_select(session *ssn, int tag)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (xstrcasestr(ibuf.data, "[READ-ONLY]"))
|
||||||
|
return STATUS_RESPONSE_READONLY;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP LIST or IMAP LSUB client
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_list(session *ssn, int tag, char **mboxs, char **folders)
|
||||||
|
{
|
||||||
|
int r, n;
|
||||||
|
char *b, *a, *s, *m, *f;
|
||||||
|
const char *v;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
m = *mboxs = (char *)xmalloc((ibuf.len + 1) * sizeof(char));
|
||||||
|
f = *folders = (char *)xmalloc((ibuf.len + 1) * sizeof(char));
|
||||||
|
*m = *f = '\0';
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_LIST];
|
||||||
|
|
||||||
|
b = ibuf.data;
|
||||||
|
while (!regexec(re->preg, b, re->nmatch, re->pmatch, 0)) {
|
||||||
|
a = xstrndup(b + re->pmatch[2].rm_so,
|
||||||
|
re->pmatch[2].rm_eo - re->pmatch[2].rm_so);
|
||||||
|
|
||||||
|
if (re->pmatch[5].rm_so != -1 && re->pmatch[5].rm_so != -1)
|
||||||
|
s = xstrndup(b + re->pmatch[5].rm_so,
|
||||||
|
re->pmatch[5].rm_eo - re->pmatch[5].rm_so);
|
||||||
|
else if (re->pmatch[6].rm_so != -1 &&
|
||||||
|
re->pmatch[6].rm_so != -1)
|
||||||
|
s = xstrndup(b + re->pmatch[6].rm_so,
|
||||||
|
re->pmatch[6].rm_eo - re->pmatch[6].rm_so);
|
||||||
|
else
|
||||||
|
s = xstrndup(b + re->pmatch[8].rm_so, strtoul(b +
|
||||||
|
re->pmatch[7].rm_so, NULL, 10));
|
||||||
|
|
||||||
|
v = reverse_namespace(s, ssn->ns.prefix, ssn->ns.delim);
|
||||||
|
n = strlen(v);
|
||||||
|
|
||||||
|
if (!xstrcasestr(a, "\\NoSelect")) {
|
||||||
|
xstrncpy(m, v, ibuf.len - (m - *mboxs));
|
||||||
|
m += n;
|
||||||
|
xstrncpy(m, "\n", ibuf.len - (m - *mboxs));
|
||||||
|
m += strlen("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xstrcasestr(a, "\\NoInferiors") &&
|
||||||
|
(!(ssn->capabilities & CAPABILITY_CHILDREN) ||
|
||||||
|
((ssn->capabilities & CAPABILITY_CHILDREN) &&
|
||||||
|
(xstrcasestr(a, "\\HasChildren")) &&
|
||||||
|
!xstrcasestr(a, "\\HasNoChildren")))) {
|
||||||
|
xstrncpy(f, v, ibuf.len - (f - *folders));
|
||||||
|
f += n;
|
||||||
|
xstrncpy(f, "\n", ibuf.len - (f - *folders));
|
||||||
|
f += strlen("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
b += re->pmatch[0].rm_eo;
|
||||||
|
|
||||||
|
xfree(a);
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP SEARCH client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_search(session *ssn, int tag, char **mesgs)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
unsigned int min;
|
||||||
|
regexp *re;
|
||||||
|
char *b, *m;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_SEARCH];
|
||||||
|
|
||||||
|
b = ibuf.data;
|
||||||
|
m = NULL;
|
||||||
|
while (!regexec(re->preg, b, re->nmatch, re->pmatch, 0)) {
|
||||||
|
if (!*mesgs) {
|
||||||
|
m = *mesgs = (char *)xmalloc((ibuf.len + 1) *
|
||||||
|
sizeof(char));
|
||||||
|
*m = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
min = (unsigned int)(re->pmatch[1].rm_eo - re->pmatch[1].rm_so) < ibuf.len ?
|
||||||
|
(unsigned int)(re->pmatch[1].rm_eo - re->pmatch[1].rm_so) :
|
||||||
|
ibuf.len;
|
||||||
|
|
||||||
|
xstrncpy(m, b + re->pmatch[1].rm_so, min);
|
||||||
|
m += min;
|
||||||
|
xstrncpy(m++, " ", ibuf.len - min);
|
||||||
|
|
||||||
|
b += re->pmatch[0].rm_eo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH FAST client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchfast(session *ssn, int tag, char **flags, char **date,
|
||||||
|
char **size)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_FLAGS];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*flags = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_DATE];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*date = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_SIZE];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*size = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH FLAGS client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchflags(session *ssn, int tag, char **flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_FLAGS];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*flags = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH INTERNALDATE client
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchdate(session *ssn, int tag, char **date)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_DATE];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*date = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH RFC822.SIZE client
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchsize(session *ssn, int tag, char **size)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_SIZE];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0))
|
||||||
|
*size = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH BODYSTRUCTURE client
|
||||||
|
* request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchstructure(session *ssn, int tag, char **structure)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
char *s;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if ((r = response_generic(ssn, tag)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH];
|
||||||
|
if (!regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0)) {
|
||||||
|
s = xstrndup(ibuf.data + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_STRUCTURE];
|
||||||
|
if (!regexec(re->preg, s, re->nmatch, re->pmatch, 0)) {
|
||||||
|
*structure = xstrndup(s + re->pmatch[1].rm_so,
|
||||||
|
re->pmatch[1].rm_eo - re->pmatch[1].rm_so);
|
||||||
|
}
|
||||||
|
xfree(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP FETCH BODY[] client request,
|
||||||
|
* ie. FETCH BODY[HEADER], FETCH BODY[TEXT], FETCH BODY[HEADER.FIELDS
|
||||||
|
* (<fields>)], FETCH BODY[<part>].
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_fetchbody(session *ssn, int tag, char **body, size_t *len)
|
||||||
|
{
|
||||||
|
int r, match;
|
||||||
|
unsigned int offset;
|
||||||
|
ssize_t n;
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
if (tag == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
buffer_reset(&ibuf);
|
||||||
|
|
||||||
|
match = -1;
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_FETCH_BODY];
|
||||||
|
|
||||||
|
do {
|
||||||
|
buffer_check(&ibuf, ibuf.len + INPUT_BUF);
|
||||||
|
if ((n = receive_response(ssn, ibuf.data + ibuf.len, 0, 1)) == -1)
|
||||||
|
return -1;
|
||||||
|
ibuf.len += n;
|
||||||
|
|
||||||
|
if (match != 0) {
|
||||||
|
match = regexec(re->preg, ibuf.data, re->nmatch,
|
||||||
|
re->pmatch, 0);
|
||||||
|
|
||||||
|
if (match == 0 && re->pmatch[2].rm_so != -1 &&
|
||||||
|
re->pmatch[2].rm_eo != -1) {
|
||||||
|
*len = strtoul(ibuf.data + re->pmatch[2].rm_so,
|
||||||
|
NULL, 10);
|
||||||
|
offset = re->pmatch[0].rm_eo + *len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset != 0 && ibuf.len >= offset) {
|
||||||
|
if (check_bye(ibuf.data + offset))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} while (ibuf.len < offset || (r = check_tag(ibuf.data + offset, ssn,
|
||||||
|
tag)) == STATUS_RESPONSE_NONE);
|
||||||
|
|
||||||
|
if (match == 0) {
|
||||||
|
if (re->pmatch[2].rm_so != -1 &&
|
||||||
|
re->pmatch[2].rm_eo != -1) {
|
||||||
|
*body = ibuf.data + re->pmatch[0].rm_eo;
|
||||||
|
} else {
|
||||||
|
*body = ibuf.data + re->pmatch[3].rm_so;
|
||||||
|
*len = re->pmatch[3].rm_eo - re->pmatch[3].rm_so;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process the data that server sent due to IMAP IDLE client request.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
response_idle(session *ssn, int tag)
|
||||||
|
{
|
||||||
|
regexp *re;
|
||||||
|
|
||||||
|
re = &responses[DATA_RESPONSE_IDLE];
|
||||||
|
|
||||||
|
do {
|
||||||
|
buffer_reset(&ibuf);
|
||||||
|
|
||||||
|
switch (receive_response(ssn, ibuf.data,
|
||||||
|
get_option_number("keepalive") * 60, 0)) {
|
||||||
|
case -1:
|
||||||
|
return -1;
|
||||||
|
break; /* NOTREACHED */
|
||||||
|
case 0:
|
||||||
|
return STATUS_RESPONSE_TIMEOUT;
|
||||||
|
break; /* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose("S (%d): %s", ssn->socket, ibuf.data);
|
||||||
|
|
||||||
|
if (check_bye(ibuf.data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
} while (regexec(re->preg, ibuf.data, re->nmatch, re->pmatch, 0));
|
||||||
|
|
||||||
|
return STATUS_RESPONSE_UNTAGGED;
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
---------------
|
||||||
|
-- Options --
|
||||||
|
---------------
|
||||||
|
|
||||||
|
options.timeout = 120
|
||||||
|
options.subscribe = true
|
||||||
|
|
||||||
|
|
||||||
|
----------------
|
||||||
|
-- Accounts --
|
||||||
|
----------------
|
||||||
|
|
||||||
|
-- Connects to "imap1.mail.server", as user "user1" with "secret1" as
|
||||||
|
-- password.
|
||||||
|
account1 = IMAP {
|
||||||
|
server = 'imap1.mail.server',
|
||||||
|
username = 'user1',
|
||||||
|
password = 'secret1',
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Another account which connects to the mail server using the SSLv3
|
||||||
|
-- protocol.
|
||||||
|
account2 = IMAP {
|
||||||
|
server = 'imap2.mail.server',
|
||||||
|
username = 'user2',
|
||||||
|
password = 'secret2',
|
||||||
|
ssl = 'ssl3',
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Get a list of the available mailboxes and folders
|
||||||
|
mailboxes, folders = account1:list_all()
|
||||||
|
|
||||||
|
-- Get a list of the subscribed mailboxes and folders
|
||||||
|
mailboxes, folders = account1:list_subscribed()
|
||||||
|
|
||||||
|
-- Create a mailbox
|
||||||
|
account1:create_mailbox('Friends')
|
||||||
|
|
||||||
|
-- Subscribe a mailbox
|
||||||
|
account1:subscribe_mailbox('Friends')
|
||||||
|
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
-- Mailboxes --
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
-- Get the status of a mailbox
|
||||||
|
account1.INBOX:check_status()
|
||||||
|
|
||||||
|
-- Get all the messages in the mailbox.
|
||||||
|
results = account1.INBOX:select_all()
|
||||||
|
|
||||||
|
-- Get newly arrived, unread messages
|
||||||
|
results = account1.INBOX:is_new()
|
||||||
|
|
||||||
|
-- Get unseen messages with the specified "From" header.
|
||||||
|
results = account1.INBOX:is_unseen() *
|
||||||
|
account1.INBOX:contain_from('weekly-news@news.letter')
|
||||||
|
|
||||||
|
-- Copy messages between mailboxes at the same account.
|
||||||
|
results:copy_messages(account1.news)
|
||||||
|
|
||||||
|
-- Get messages with the specified "From" header but without the
|
||||||
|
-- specified "Subject" header.
|
||||||
|
results = account1.INBOX:contain_from('announce@my.unix.os') -
|
||||||
|
account1.INBOX:contain_subject('security advisory')
|
||||||
|
|
||||||
|
-- Copy messages between mailboxes at a different account.
|
||||||
|
results:copy_messages(account2.security)
|
||||||
|
|
||||||
|
-- Get messages with any of the specified headers.
|
||||||
|
results = account1.INBOX:contain_from('marketing@company.junk') +
|
||||||
|
account1.INBOX:contain_from('advertising@annoying.promotion') +
|
||||||
|
account1.INBOX:contain_subject('new great products')
|
||||||
|
|
||||||
|
-- Delete messages.
|
||||||
|
results:delete_messages()
|
||||||
|
|
||||||
|
-- Get messages with the specified "Sender" header, which are older than
|
||||||
|
-- 30 days.
|
||||||
|
results = account1.INBOX:contain_field('sender', 'owner@announce-list') *
|
||||||
|
account1.INBOX:is_older(30)
|
||||||
|
|
||||||
|
-- Move messages to the "announce" mailbox inside the "lists" folder.
|
||||||
|
results:move_messages(account1['lists/announce'])
|
||||||
|
|
||||||
|
-- Get messages, in the "devel" mailbox inside the "lists" folder, with the
|
||||||
|
-- specified "Subject" header and a size less than 50000 octets (bytes).
|
||||||
|
results = account1['lists/devel']:contain_subject('[patch]') *
|
||||||
|
account1['lists/devel']:is_smaller(50000)
|
||||||
|
|
||||||
|
-- Move messages to the "patch" mailbox.
|
||||||
|
results:move_messages(account2.patch)
|
||||||
|
|
||||||
|
-- Get recent, unseen messages, that have either one of the specified
|
||||||
|
-- "From" headers, but do not have the specified pattern in the body of
|
||||||
|
-- the message.
|
||||||
|
results = ( account1.INBOX:is_recent() *
|
||||||
|
account1.INBOX:is_unseen() *
|
||||||
|
( account1.INBOX:contain_from('tux@penguin.land') +
|
||||||
|
account1.INBOX:contain_from('beastie@daemon.land') ) ) -
|
||||||
|
account1.INBOX:match_body('.*all.work.and.no.play.*')
|
||||||
|
|
||||||
|
-- Mark messages as important.
|
||||||
|
results:mark_flagged()
|
||||||
|
|
||||||
|
-- Get all messages in two mailboxes residing in the same server.
|
||||||
|
results = account1.news:select_all() +
|
||||||
|
account1.security:select_all()
|
||||||
|
|
||||||
|
-- Mark messages as seen.
|
||||||
|
results:mark_seen()
|
||||||
|
|
||||||
|
-- Get recent messages in two mailboxes residing in different servers.
|
||||||
|
results = account1.INBOX:is_recent() +
|
||||||
|
account2.INBOX:is_recent()
|
||||||
|
|
||||||
|
-- Flag messages as seen and important.
|
||||||
|
results:add_flags({ '\\Seen', '\\Flagged' })
|
||||||
|
|
||||||
|
-- Get unseen messages.
|
||||||
|
results = account1.INBOX:is_unseen()
|
||||||
|
|
||||||
|
-- From the messages that were unseen, match only those with the specified
|
||||||
|
-- regular expression in the header.
|
||||||
|
newresults = results:match_header('^.+MailScanner.*Check: [Ss]pam$')
|
||||||
|
|
||||||
|
-- Delete those messages.
|
||||||
|
newresults:delete_messages()
|
|
@ -0,0 +1,103 @@
|
||||||
|
--
|
||||||
|
-- This file contains examples on how IMAPFilter can be extended using
|
||||||
|
-- the Lua programming language.
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
-- IMAPFilter can be detached from the controlling terminal and run in
|
||||||
|
-- the background as a system daemon.
|
||||||
|
--
|
||||||
|
-- The auxiliary function daemon_mode() is supplied for conveniency.
|
||||||
|
-- The following example puts imapfilter in the background and runs
|
||||||
|
-- endlessly, executing the commands in the forever() function and
|
||||||
|
-- sleeping for 600 seconds between intervals:
|
||||||
|
|
||||||
|
function forever()
|
||||||
|
results = myaccount.mymailbox:is_old()
|
||||||
|
results:move_messages(myaccount.archive)
|
||||||
|
end
|
||||||
|
|
||||||
|
become_daemon(600, forever)
|
||||||
|
|
||||||
|
|
||||||
|
-- IMAPFilter can take advantage of all those filtering utilities that
|
||||||
|
-- are available and use a wide range of heuristic tests, text analysis,
|
||||||
|
-- internet-based realtime blacklists, advanced learning algorithms,
|
||||||
|
-- etc. to classify mail. IMAPFilter can pipe a message to a program
|
||||||
|
-- and act on the message based on the program's exit status.
|
||||||
|
--
|
||||||
|
-- The auxiliary function pipe_to() is supplied for conveniency. For
|
||||||
|
-- example if there was a utility named "bayesian-spam-filter", which
|
||||||
|
-- returned 1 when it considered the message "spam" and 0 otherwise:
|
||||||
|
|
||||||
|
all = myaccount.mymailbox:select_all()
|
||||||
|
|
||||||
|
results = Set {}
|
||||||
|
for _, mesg in ipairs(all) do
|
||||||
|
mbox, uid = unpack(mesg)
|
||||||
|
text = mbox[uid]:fetch_message()
|
||||||
|
if (pipe_to('bayesian-spam-filter', text) == 1) then
|
||||||
|
table.insert(results, mesg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results:delete_messages()
|
||||||
|
|
||||||
|
|
||||||
|
-- One might want to run the bayesian filter only in those parts (attachments)
|
||||||
|
-- of the message that are of type text/plain and smaller than 1024 bytes.
|
||||||
|
-- This is possible using the fetch_structure() and fetch_part() functions:
|
||||||
|
|
||||||
|
all = myaccount.mymailbox:select_all()
|
||||||
|
|
||||||
|
results = Set {}
|
||||||
|
for _, mesg in ipairs(all) do
|
||||||
|
mbox, uid = unpack(mesg)
|
||||||
|
structure = mbox[uid]:fetch_structure()
|
||||||
|
for partid, partinf in pairs(structure) do
|
||||||
|
if partinf.type:lower() == 'text/plain' and partinf.size < 1024 then
|
||||||
|
part = mbox[uid]:fetch_part(partid)
|
||||||
|
if (pipe_to('bayesian-spam-filter', part) == 1) then
|
||||||
|
table.insert(results, mesg)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
results:delete_messages()
|
||||||
|
|
||||||
|
|
||||||
|
-- Passwords could be extracted during execution time from an encrypted
|
||||||
|
-- file.
|
||||||
|
--
|
||||||
|
-- The file is encrypted using the openssl(1) command line tool. For
|
||||||
|
-- example the "passwords.txt" file:
|
||||||
|
--
|
||||||
|
-- secret1 secret2
|
||||||
|
--
|
||||||
|
-- ... is encrypted and saved to a file named "passwords.enc" with the
|
||||||
|
-- command:
|
||||||
|
--
|
||||||
|
-- $ openssl bf -salt -in passwords.txt -out passwords.enc
|
||||||
|
--
|
||||||
|
-- The auxiliary function pipe_from() is supplied for conveniency. The
|
||||||
|
-- user is prompted to enter the decryption password, the file is
|
||||||
|
-- decrypted and the account passwords are set accordingly:
|
||||||
|
|
||||||
|
status, output = pipe_from('openssl bf -d -salt -in ~/passwords.enc')
|
||||||
|
|
||||||
|
_, _, password1, password2 = string.find(output, '([%w%p]+)\n([%w%p]+)\n')
|
||||||
|
|
||||||
|
account1 = IMAP {
|
||||||
|
server = 'imap1.mail.server',
|
||||||
|
username = 'user1',
|
||||||
|
password = password1
|
||||||
|
}
|
||||||
|
|
||||||
|
account2 = IMAP {
|
||||||
|
server = 'imap2.mail.server',
|
||||||
|
username = 'user2',
|
||||||
|
password = password2
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern list *sessions;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate memory for a new session and add it to the sessions linked list.
|
||||||
|
*/
|
||||||
|
session *
|
||||||
|
session_new(void)
|
||||||
|
{
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
s = (session *)xmalloc(sizeof(session));
|
||||||
|
|
||||||
|
session_init(s);
|
||||||
|
|
||||||
|
sessions = list_append(sessions, s);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set session variables to safe values.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
session_init(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
ssn->server = NULL;
|
||||||
|
ssn->port = NULL;
|
||||||
|
ssn->username = NULL;
|
||||||
|
ssn->socket = -1;
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
ssn->ssl = NULL;
|
||||||
|
#endif
|
||||||
|
ssn->protocol = PROTOCOL_NONE;
|
||||||
|
ssn->capabilities = CAPABILITY_NONE;
|
||||||
|
ssn->ns.prefix = NULL;
|
||||||
|
ssn->ns.delim = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove session from sessions linked list.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
session_destroy(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
sessions = list_remove(sessions, ssn);
|
||||||
|
|
||||||
|
session_free(ssn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free session allocated memory.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
session_free(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ssn->server)
|
||||||
|
xfree(ssn->server);
|
||||||
|
if (ssn->port)
|
||||||
|
xfree(ssn->port);
|
||||||
|
if (ssn->username)
|
||||||
|
xfree(ssn->username);
|
||||||
|
if (ssn->ns.prefix)
|
||||||
|
xfree(ssn->ns.prefix);
|
||||||
|
xfree(ssn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Based on the specified socket, find an active IMAP session.
|
||||||
|
*/
|
||||||
|
session *
|
||||||
|
session_find(const char *serv, const char *port, const char *user)
|
||||||
|
{
|
||||||
|
list *l;
|
||||||
|
session *s;
|
||||||
|
|
||||||
|
for (l = sessions; l; l = l->next) {
|
||||||
|
s = l->data;
|
||||||
|
if (!strcmp(s->server, serv) &&
|
||||||
|
!strcmp(s->port, port) &&
|
||||||
|
!strcmp(s->username, user))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef SESSION_H
|
||||||
|
#define SESSION_H
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* IMAP session. */
|
||||||
|
typedef struct session {
|
||||||
|
char *server; /* Server hostname. */
|
||||||
|
char *port; /* Server port. */
|
||||||
|
char *username; /* User name. */
|
||||||
|
int socket; /* Socket. */
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
SSL *ssl; /* SSL socket. */
|
||||||
|
#endif
|
||||||
|
unsigned int protocol; /* IMAP protocol. Currently IMAP4rev1 and
|
||||||
|
* IMAP4 are supported. */
|
||||||
|
unsigned int capabilities; /* Capabilities of the mail server. */
|
||||||
|
struct { /* Namespace of the mail server's mailboxes. */
|
||||||
|
char *prefix; /* Namespace prefix. */
|
||||||
|
char delim; /* Namespace delimiter. */
|
||||||
|
} ns;
|
||||||
|
} session;
|
||||||
|
|
||||||
|
|
||||||
|
/* session.c */
|
||||||
|
session *session_new(void);
|
||||||
|
void session_init(session *ssn);
|
||||||
|
void session_destroy(session *ssn);
|
||||||
|
void session_free(session *ssn);
|
||||||
|
session *session_find(const char *server, const char *port, const char *user);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* SESSION_H */
|
|
@ -0,0 +1,700 @@
|
||||||
|
-- A simple implementation of sets.
|
||||||
|
|
||||||
|
Set = {}
|
||||||
|
|
||||||
|
Set._mt = {}
|
||||||
|
setmetatable(Set, Set._mt)
|
||||||
|
|
||||||
|
|
||||||
|
function Set._new(self, values)
|
||||||
|
local object
|
||||||
|
|
||||||
|
object = values or {}
|
||||||
|
|
||||||
|
for key, value in pairs(Set) do
|
||||||
|
if (type(value) == 'function') then
|
||||||
|
object[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
object._type = 'set'
|
||||||
|
|
||||||
|
object._mt = {}
|
||||||
|
object._mt.__add = object._union
|
||||||
|
object._mt.__mul = object._intersection
|
||||||
|
object._mt.__sub = object._difference
|
||||||
|
setmetatable(object, object._mt)
|
||||||
|
|
||||||
|
return object
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set._union(seta, setb)
|
||||||
|
local set = Set()
|
||||||
|
local t = {}
|
||||||
|
|
||||||
|
for _, v in ipairs(seta) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if not t[b] then t[b] = {} end
|
||||||
|
t[b][m] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(setb) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if not t[b] then t[b] = {} end
|
||||||
|
t[b][m] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for b in pairs(t) do
|
||||||
|
for m in pairs(t[b]) do
|
||||||
|
table.insert(set, { b, m })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set._intersection(seta, setb)
|
||||||
|
local set = Set()
|
||||||
|
local ta = {}
|
||||||
|
local tb = {}
|
||||||
|
|
||||||
|
for _, v in ipairs(seta) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if not ta[b] then ta[b] = {} end
|
||||||
|
ta[b][m] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(setb) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if not tb[b] then tb[b] = {} end
|
||||||
|
tb[b][m] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for b in pairs(ta) do
|
||||||
|
if tb[b] then
|
||||||
|
for m in pairs(ta[b]) do
|
||||||
|
if tb[b][m] then
|
||||||
|
table.insert(set, { b, m })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set._difference(seta, setb)
|
||||||
|
local set = Set()
|
||||||
|
local t = {}
|
||||||
|
|
||||||
|
for _, v in ipairs(seta) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if not t[b] then t[b] = {} end
|
||||||
|
t[b][m] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(setb) do
|
||||||
|
b, m = unpack(v)
|
||||||
|
if t[b] then
|
||||||
|
t[b][m] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for b in pairs(t) do
|
||||||
|
for m in pairs(t[b]) do
|
||||||
|
table.insert(set, { b, m })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Set.add_flags(self, flags)
|
||||||
|
_check_required(flags, 'table')
|
||||||
|
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.add_flags(mbox, flags, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.remove_flags(self, flags)
|
||||||
|
_check_required(flags, 'table')
|
||||||
|
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.remove_flags(mbox, flags, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.replace_flags(self, flags)
|
||||||
|
_check_required(flags, 'table')
|
||||||
|
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.replace_flags(mbox, flags, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.mark_answered(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.mark_answered(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.mark_deleted(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.mark_deleted(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.mark_draft(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.mark_draft(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.mark_flagged(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.mark_flagged(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.mark_seen(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.mark_seen(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.unmark_answered(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.unmark_answered(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.unmark_deleted(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.unmark_deleted(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.unmark_draft(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.unmark_draft(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.unmark_flagged(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.unmark_flagged(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.unmark_seen(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.unmark_seen(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.delete_messages(self)
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.delete_messages(mbox, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.copy_messages(self, dest)
|
||||||
|
_check_required(dest, 'table')
|
||||||
|
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.copy_messages(mbox, dest, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.move_messages(self, dest)
|
||||||
|
_check_required(dest, 'table')
|
||||||
|
|
||||||
|
local r = true
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
if not mbox.move_messages(mbox, dest, self) then
|
||||||
|
r = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Set.select_all(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.select_all(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.send_query(self, criteria)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.send_query(mbox, criteria)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_answered(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_answered(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_deleted(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_deleted(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_draft(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_draft(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_flagged(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_flagged(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_new(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_new(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_old(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_old(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_recent(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_recent(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_seen(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_seen(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_unanswered(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_unanswered(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_undeleted(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_undeleted(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_undraft(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_undraft(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_unflagged(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_unflagged(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_unseen(self)
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_unseen(mbox)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_larger(self, size)
|
||||||
|
_check_required(size, 'number')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_larger(mbox, size)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_smaller(self, size)
|
||||||
|
_check_required(size, 'number')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_smaller(mbox, size)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Set.arrived_on(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.arrived_on(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.arrived_before(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.arrived_before(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.arrived_since(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.arrived_since(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.sent_on(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.sent_on(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.sent_before(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.sent_before(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.sent_since(self, date)
|
||||||
|
_check_required(date, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.sent_since(mbox, date)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_newer(self, days)
|
||||||
|
_check_required(days, 'number')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_newer(mbox, days)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.is_older(self, days)
|
||||||
|
_check_required(days, 'number')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.is_older(mbox, days)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Set.has_flag(self, flag)
|
||||||
|
_check_required(flag, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.has_flag(mbox, flag)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function Set.contain_field(self, field, string)
|
||||||
|
_check_required(field, 'string')
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_field(mbox, field, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_bcc(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_bcc(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_cc(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_cc(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_from(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_from(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_subject(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_subject(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_to(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_to(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_header(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_header(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_body(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_body(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.contain_message(self, string)
|
||||||
|
_check_required(string, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.contain_message(mbox, string)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_bcc(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_bcc(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_cc(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_cc(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_from(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_from(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_subject(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_subject(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_to(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_to(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_field(self, field, pattern)
|
||||||
|
_check_required(field, 'string')
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_field(mbox, field, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_header(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_header(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_body(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_body(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
function Set.match_message(self, pattern)
|
||||||
|
_check_required(pattern, 'string')
|
||||||
|
|
||||||
|
local set = Set()
|
||||||
|
for mbox in pairs(_extract_mailboxes(self)) do
|
||||||
|
set = set + mbox.match_message(mbox, pattern, self)
|
||||||
|
end
|
||||||
|
return self * set
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
Set._mt.__call = Set._new
|
|
@ -0,0 +1,45 @@
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
void signal_handler(int sig);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Catch signals that cause program's termination.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
catch_signals(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
signal(SIGQUIT, signal_handler);
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release signals and reset them to default action.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
release_signals(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGQUIT, SIG_DFL);
|
||||||
|
signal(SIGTERM, SIG_DFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signal handler for signals that cause termination of program.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
signal_handler(int sig)
|
||||||
|
{
|
||||||
|
|
||||||
|
release_signals();
|
||||||
|
|
||||||
|
fatal(ERROR_SIGNAL, "killed by signal %d\n", sig);
|
||||||
|
}
|
|
@ -0,0 +1,440 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to mail server.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
open_connection(session *ssn, const char *server, const char *port,
|
||||||
|
const char *protocol)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *res, *ressave;
|
||||||
|
int n, sockfd;
|
||||||
|
|
||||||
|
#ifdef NO_SSLTLS
|
||||||
|
if (protocol) {
|
||||||
|
error("SSL not supported by this build\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
n = getaddrinfo(server, port, &hints, &res);
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
error("gettaddrinfo; %s\n", gai_strerror(n));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ressave = res;
|
||||||
|
|
||||||
|
sockfd = -1;
|
||||||
|
|
||||||
|
while (res) {
|
||||||
|
sockfd = socket(res->ai_family, res->ai_socktype,
|
||||||
|
res->ai_protocol);
|
||||||
|
|
||||||
|
if (sockfd >= 0) {
|
||||||
|
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sockfd = -1;
|
||||||
|
}
|
||||||
|
res = res->ai_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ressave)
|
||||||
|
freeaddrinfo(ressave);
|
||||||
|
|
||||||
|
if (sockfd == -1) {
|
||||||
|
error("error while initiating connection to %s at port %s\n",
|
||||||
|
server, port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssn->socket = sockfd;
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
if (protocol) {
|
||||||
|
if (open_secure_connection(ssn, server, port, protocol) == -1) {
|
||||||
|
close_connection(ssn);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ssn->socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
/*
|
||||||
|
* Initialize SSL/TLS connection.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
open_secure_connection(session *ssn, const char *server, const char *port,
|
||||||
|
const char *protocol)
|
||||||
|
{
|
||||||
|
int r, e;
|
||||||
|
SSL_CTX *ctx;
|
||||||
|
SSL_METHOD *method;
|
||||||
|
|
||||||
|
method = NULL;
|
||||||
|
|
||||||
|
if (!strncasecmp(protocol, "tls1", 4))
|
||||||
|
method = TLSv1_client_method();
|
||||||
|
else if (!strncasecmp(protocol, "ssl3", 4) ||
|
||||||
|
!strncasecmp(protocol, "ssl2", 4))
|
||||||
|
method = SSLv23_client_method();
|
||||||
|
|
||||||
|
if (!(ctx = SSL_CTX_new(method)))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (!(ssn->ssl = SSL_new(ctx)))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
SSL_set_fd(ssn->ssl, ssn->socket);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if ((r = SSL_connect(ssn->ssl)) > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (SSL_get_error(ssn->ssl, r)) {
|
||||||
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
|
error("initiating SSL connection to %s; the "
|
||||||
|
"connection has been closed cleanly\n", server);
|
||||||
|
goto fail;
|
||||||
|
case SSL_ERROR_WANT_CONNECT:
|
||||||
|
case SSL_ERROR_WANT_ACCEPT:
|
||||||
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
|
case SSL_ERROR_WANT_READ:
|
||||||
|
case SSL_ERROR_WANT_WRITE:
|
||||||
|
break;
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
e = ERR_get_error();
|
||||||
|
if (e == 0)
|
||||||
|
error("initiating SSL connection to %s; EOF "
|
||||||
|
"in violation of the protocol\n", server);
|
||||||
|
else if (e == -1)
|
||||||
|
error("initiating SSL connection to %s; %s\n",
|
||||||
|
server, strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
case SSL_ERROR_SSL:
|
||||||
|
error("initiating SSL connection to %s; %s\n", server,
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
goto fail;
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (get_option_boolean("certificates") && get_cert(ssn) == -1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
ssn->ssl = NULL;
|
||||||
|
SSL_CTX_free(ctx);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif /* NO_SSLTLS */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disconnect from mail server.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
close_connection(session *ssn)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
close_secure_connection(ssn);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ssn->socket != -1) {
|
||||||
|
r = close(ssn->socket);
|
||||||
|
ssn->socket = -1;
|
||||||
|
|
||||||
|
if (r == -1)
|
||||||
|
error("closing socket; %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
/*
|
||||||
|
* Shutdown SSL/TLS connection.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
close_secure_connection(session *ssn)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ssn->ssl) {
|
||||||
|
SSL_shutdown(ssn->ssl);
|
||||||
|
SSL_free(ssn->ssl);
|
||||||
|
ssn->ssl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read data from socket.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
socket_read(session *ssn, char *buf, size_t len, long timeout, int timeoutfail)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
ssize_t r;
|
||||||
|
fd_set fds;
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
struct timeval *tvp;
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
s = 1;
|
||||||
|
tvp = NULL;
|
||||||
|
|
||||||
|
memset(buf, 0, len + 1);
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
tvp = &tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(ssn->socket, &fds);
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
if (ssn->ssl) {
|
||||||
|
if (SSL_pending(ssn->ssl) > 0 ||
|
||||||
|
((s = select(ssn->socket + 1, &fds, NULL, NULL, tvp)) > 0 &&
|
||||||
|
FD_ISSET(ssn->socket, &fds))) {
|
||||||
|
r = socket_secure_read(ssn, buf, len);
|
||||||
|
|
||||||
|
if (r <= 0)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if ((s = select(ssn->socket + 1, &fds, NULL, NULL, tvp)) > 0 &&
|
||||||
|
FD_ISSET(ssn->socket, &fds)) {
|
||||||
|
r = read(ssn->socket, buf, len);
|
||||||
|
|
||||||
|
if (r == -1) {
|
||||||
|
error("reading data; %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (r == 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s == -1) {
|
||||||
|
error("waiting to read from socket; %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (s == 0 && timeoutfail) {
|
||||||
|
error("timeout period expired while waiting to read data\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
fail:
|
||||||
|
close_connection(ssn);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
/*
|
||||||
|
* Read data from a TLS/SSL connection.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
socket_secure_read(session *ssn, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int r, e;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
r = (ssize_t) SSL_read(ssn->ssl, buf, len);
|
||||||
|
|
||||||
|
if (r > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (SSL_get_error(ssn->ssl, r)) {
|
||||||
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
|
error("reading data; the connection has been closed "
|
||||||
|
"cleanly\n");
|
||||||
|
return -1;
|
||||||
|
case SSL_ERROR_WANT_READ:
|
||||||
|
case SSL_ERROR_WANT_WRITE:
|
||||||
|
case SSL_ERROR_WANT_CONNECT:
|
||||||
|
case SSL_ERROR_WANT_ACCEPT:
|
||||||
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
|
break;
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
e = ERR_get_error();
|
||||||
|
if (e == 0)
|
||||||
|
error("reading data; EOF in violation of the "
|
||||||
|
"protocol\n");
|
||||||
|
else if (e == -1)
|
||||||
|
error("reading data; %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
case SSL_ERROR_SSL:
|
||||||
|
error("reading data; %s\n",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write data to socket.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
socket_write(session *ssn, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
ssize_t w, wt;
|
||||||
|
fd_set fds;
|
||||||
|
|
||||||
|
w = wt = 0;
|
||||||
|
s = 1;
|
||||||
|
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(ssn->socket, &fds);
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
if ((s = select(ssn->socket + 1, NULL, &fds, NULL, NULL) > 0 &&
|
||||||
|
FD_ISSET(ssn->socket, &fds))) {
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
if (ssn->ssl) {
|
||||||
|
w = socket_secure_write(ssn, buf, len);
|
||||||
|
|
||||||
|
if (w <= 0)
|
||||||
|
goto fail;
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
w = write(ssn->socket, buf, len);
|
||||||
|
|
||||||
|
if (w == -1) {
|
||||||
|
error("writing data; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (w == 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w > 0) {
|
||||||
|
len -= w;
|
||||||
|
buf += w;
|
||||||
|
wt += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s == -1) {
|
||||||
|
error("waiting to write to socket; %s\n", strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
} else if (s == 0) {
|
||||||
|
error("timeout period expired while waiting to write data\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wt;
|
||||||
|
fail:
|
||||||
|
close_connection(ssn);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NO_SSLTLS
|
||||||
|
/*
|
||||||
|
* Write data to a TLS/SSL connection.
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
socket_secure_write(session *ssn, const char *buf, size_t len)
|
||||||
|
{
|
||||||
|
int w, e;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
w = (ssize_t) SSL_write(ssn->ssl, buf, len);
|
||||||
|
|
||||||
|
if (w > 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (SSL_get_error(ssn->ssl, w)) {
|
||||||
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
|
error("writing data; the connection has been closed "
|
||||||
|
"cleanly\n");
|
||||||
|
return -1;
|
||||||
|
case SSL_ERROR_WANT_READ:
|
||||||
|
case SSL_ERROR_WANT_WRITE:
|
||||||
|
case SSL_ERROR_WANT_CONNECT:
|
||||||
|
case SSL_ERROR_WANT_ACCEPT:
|
||||||
|
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||||
|
break;
|
||||||
|
case SSL_ERROR_SYSCALL:
|
||||||
|
e = ERR_get_error();
|
||||||
|
if (e == 0)
|
||||||
|
error("writing data; EOF in violation of the "
|
||||||
|
"protocol\n");
|
||||||
|
else if (e == -1)
|
||||||
|
error("writing data; %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
case SSL_ERROR_SSL:
|
||||||
|
error("writing data; %s\n",
|
||||||
|
ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,308 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include <lua.h>
|
||||||
|
#include <lauxlib.h>
|
||||||
|
#include <lualib.h>
|
||||||
|
|
||||||
|
#include "imapfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int ifsys_echo(lua_State *lua);
|
||||||
|
static int ifsys_noecho(lua_State *lua);
|
||||||
|
static int ifsys_popen(lua_State *lua);
|
||||||
|
static int ifsys_pclose(lua_State *lua);
|
||||||
|
static int ifsys_read(lua_State *lua);
|
||||||
|
static int ifsys_write(lua_State *lua);
|
||||||
|
static int ifsys_sleep(lua_State *lua);
|
||||||
|
static int ifsys_daemon(lua_State *lua);
|
||||||
|
|
||||||
|
/* Lua imapfilter library of system's functions. */
|
||||||
|
static const luaL_reg ifsyslib[] = {
|
||||||
|
{ "echo", ifsys_echo },
|
||||||
|
{ "noecho", ifsys_noecho },
|
||||||
|
{ "popen", ifsys_popen },
|
||||||
|
{ "pclose", ifsys_pclose },
|
||||||
|
{ "read", ifsys_read },
|
||||||
|
{ "write", ifsys_write },
|
||||||
|
{ "sleep", ifsys_sleep },
|
||||||
|
{ "daemon", ifsys_daemon },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable character echoing.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_echo(lua_State *lua)
|
||||||
|
{
|
||||||
|
struct termios t;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 0)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
if (tcgetattr(fileno(stdin), &t)) {
|
||||||
|
fprintf(stderr, "getting term attributs; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.c_lflag |= (ECHO);
|
||||||
|
t.c_lflag &= ~(ECHONL);
|
||||||
|
|
||||||
|
if (tcsetattr(fileno(stdin), TCSAFLUSH, &t)) {
|
||||||
|
fprintf(stderr, "setting term attributes; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable character echoing.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_noecho(lua_State *lua)
|
||||||
|
{
|
||||||
|
struct termios t;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 0)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
if (tcgetattr(fileno(stdin), &t)) {
|
||||||
|
fprintf(stderr, "getting term attributs; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.c_lflag &= ~(ECHO);
|
||||||
|
t.c_lflag |= (ECHONL);
|
||||||
|
|
||||||
|
if (tcsetattr(fileno(stdin), TCSAFLUSH, &t)) {
|
||||||
|
fprintf(stderr, "setting term attributes; %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the POSIX popen() function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_popen(lua_State *lua)
|
||||||
|
{
|
||||||
|
FILE **fp;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 2)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TSTRING);
|
||||||
|
luaL_checktype(lua, 2, LUA_TSTRING);
|
||||||
|
|
||||||
|
fp = (FILE **) lua_newuserdata(lua, sizeof(FILE *));
|
||||||
|
|
||||||
|
*fp = NULL;
|
||||||
|
|
||||||
|
*fp = popen(lua_tostring(lua, 1), lua_tostring(lua, 2));
|
||||||
|
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
|
||||||
|
return (*fp == NULL ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the POSIX pclose() function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_pclose(lua_State *lua)
|
||||||
|
{
|
||||||
|
lua_Number r;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 1)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TUSERDATA);
|
||||||
|
|
||||||
|
fp = *(FILE **) (lua_touserdata(lua, 1));
|
||||||
|
|
||||||
|
r = (lua_Number) (pclose(fp) >> 8);
|
||||||
|
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
if (r == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fp = NULL;
|
||||||
|
lua_pushnumber(lua, r);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads a line from a file stream.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_read(lua_State *lua)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
luaL_Buffer b;
|
||||||
|
char *c;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 1)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TUSERDATA);
|
||||||
|
|
||||||
|
fp = *(FILE **) (lua_touserdata(lua, 1));
|
||||||
|
|
||||||
|
lua_pop(lua, 1);
|
||||||
|
|
||||||
|
luaL_buffinit(lua, &b);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
c = luaL_prepbuffer(&b);
|
||||||
|
|
||||||
|
if (fgets(c, LUAL_BUFFERSIZE, fp) == NULL && feof(fp)) {
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
|
||||||
|
return (lua_strlen(lua, -1) > 0);
|
||||||
|
}
|
||||||
|
n = strlen(c);
|
||||||
|
|
||||||
|
if (c[n - 1] != '\n')
|
||||||
|
luaL_addsize(&b, n);
|
||||||
|
else {
|
||||||
|
luaL_addsize(&b, n);
|
||||||
|
luaL_pushresult(&b);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a string to a file stream.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_write(lua_State *lua)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 2)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TUSERDATA);
|
||||||
|
luaL_checktype(lua, 2, LUA_TSTRING);
|
||||||
|
|
||||||
|
n = fwrite(lua_tostring(lua, 2), sizeof(char), strlen(lua_tostring(lua,
|
||||||
|
2)), *(FILE **) (lua_touserdata(lua, 1)));
|
||||||
|
|
||||||
|
lua_pop(lua, 2);
|
||||||
|
|
||||||
|
lua_pushboolean(lua, (n != 0));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the POSIX sleep() function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_sleep(lua_State *lua)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 1)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
luaL_checktype(lua, 1, LUA_TNUMBER);
|
||||||
|
|
||||||
|
lua_pushnumber(lua,
|
||||||
|
(lua_Number) (sleep) ((unsigned int)(lua_tonumber(lua, 1))));
|
||||||
|
|
||||||
|
lua_remove(lua, 1);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lua implementation of the BSD daemon() function.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ifsys_daemon(lua_State *lua)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (lua_gettop(lua) != 0)
|
||||||
|
luaL_error(lua, "wrong number of arguments");
|
||||||
|
|
||||||
|
switch (fork()) {
|
||||||
|
case -1:
|
||||||
|
fprintf(stderr, "forking; %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsid() == -1) {
|
||||||
|
fprintf(stderr, "creating session; %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
switch (fork()) {
|
||||||
|
case -1:
|
||||||
|
fprintf(stderr, "creating session; %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
|
||||||
|
if (open("/dev/null", O_RDWR) == -1 ||
|
||||||
|
dup(STDIN_FILENO) == -1 ||
|
||||||
|
dup(STDIN_FILENO) == -1)
|
||||||
|
fprintf(stderr, "creating session; %s\n", strerror(errno));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open imapfilter library of system's functions.
|
||||||
|
*/
|
||||||
|
LUALIB_API int
|
||||||
|
luaopen_ifsys(lua_State *lua)
|
||||||
|
{
|
||||||
|
|
||||||
|
luaL_register(lua, "ifsys", ifsyslib);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
Loading…
Reference in New Issue