Browse Source

Initialize Lua script plugin for Dovecot

Peter Colberg 3 years ago
commit
19d05c6a2b

+ 6
- 0
.gitignore View File

@@ -0,0 +1,6 @@
1
+*.d
2
+*.o
3
+*.pyc
4
+*.so
5
+*.conf
6
+Mail

+ 19
- 0
LICENSE View File

@@ -0,0 +1,19 @@
1
+Copyright © 2015 Peter Colberg.
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining a copy
4
+of this software and associated documentation files (the "Software"), to deal
5
+in the Software without restriction, including without limitation the rights
6
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+copies of the Software, and to permit persons to whom the Software is
8
+furnished to do so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included in
11
+all copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+THE SOFTWARE.

+ 15
- 0
examples/antispam/Makefile View File

@@ -0,0 +1,15 @@
1
+MKDIR = mkdir -p
2
+RMDIR = rm -rf
3
+SED = sed
4
+
5
+all: test
6
+
7
+test: clean
8
+	$(MKDIR) Mail
9
+	$(SED) 's@CURDIR@$(CURDIR)@' dovecot.conf.in > dovecot.conf
10
+	./test.py
11
+
12
+clean:
13
+	$(RMDIR) Mail
14
+
15
+.PHONY: all test clean

+ 131
- 0
examples/antispam/antispam.lua View File

@@ -0,0 +1,131 @@
1
+------------------------------------------------------------------------------
2
+-- Register misclassified spam/nonspam messages.
3
+-- Copyright © 2015 Peter Colberg.
4
+-- Distributed under the MIT license. (See accompanying file LICENSE.)
5
+------------------------------------------------------------------------------
6
+
7
+require("strict")
8
+
9
+local dovecot = require("dovecot")
10
+
11
+-- Cache library functions.
12
+local assert, error, ipairs = assert, error, ipairs
13
+local insert, remove = table.insert, table.remove
14
+local popen = io.popen
15
+
16
+-- Cache mail flags.
17
+local MAIL_SEEN = dovecot.MAIL_SEEN
18
+
19
+-- Capture script arguments.
20
+local user = ...
21
+
22
+-- Optional informational messages.
23
+local info
24
+if user:plugin_getenv("antispam_verbose") == "yes" then
25
+  info = dovecot.info
26
+end
27
+
28
+if info then
29
+  info("antispam: Lua plugin version "..dovecot.PLUGIN_VERSION)
30
+end
31
+
32
+-- Returns the value of the given plugin variable.
33
+local function getenv(name, default)
34
+  local value = user:plugin_getenv(name)
35
+  if value == nil then
36
+    return error("Missing plugin variable "..name)
37
+  end
38
+  return value
39
+end
40
+
41
+-- Returns a function that matches a space-separated list of mailbox patterns.
42
+local function pattern(s)
43
+  local patt = {}
44
+  for w in s:gmatch("%S+") do
45
+    if w:sub(1, 1) == "!" then
46
+      insert(patt, 1, {w:sub(2), false})
47
+    else
48
+      insert(patt, 1, {w, true})
49
+    end
50
+  end
51
+
52
+  return function(mailbox)
53
+    local sep = mailbox:get_namespace():get_sep()
54
+    local name = mailbox:get_name()
55
+    for _, p in ipairs(patt) do
56
+      if dovecot.imap_match(p[1], false, sep)(name) then
57
+        return p[2]
58
+      end
59
+    end
60
+  end
61
+end
62
+
63
+-- Finds index of value in a sequence.
64
+local function find(t, v)
65
+  for i, e in ipairs(t) do
66
+    if e == v then return i end
67
+  end
68
+end
69
+
70
+-- Pipe message to program.
71
+local function pipe(mail, program)
72
+  if info then
73
+    info("antispam: Pipe message to program '"..program.."'")
74
+  end
75
+  local s = mail:get_stream()
76
+  local p = assert(popen(program, "w"))
77
+  while true do
78
+    local line = s:read()
79
+    if not line then break end
80
+    assert(p:write(line, "\n"))
81
+  end
82
+  assert(p:close())
83
+end
84
+
85
+-- Read plugin variables.
86
+local program_spam = getenv("antispam_program_spam")
87
+local program_nonspam = getenv("antispam_program_nonspam")
88
+local mailbox_spam = pattern(getenv("antispam_mailbox_spam"))
89
+local mailbox_nonspam = pattern(getenv("antispam_mailbox_nonspam"))
90
+local keyword_spam = getenv("antispam_keyword_spam")
91
+
92
+-- Hook function table.
93
+local _M = {}
94
+
95
+-- Called before message is copied between mailboxes.
96
+function _M.copy(save, mail)
97
+  local dest = save:get_transaction():get_box()
98
+  if info then
99
+    info("antispam: Copy message from mailbox '"..mail:get_box():get_name().."' to mailbox '"..dest:get_name().."'")
100
+  end
101
+  local spam = find(mail:get_keywords(), keyword_spam)
102
+  if mailbox_spam(dest) and not spam then
103
+    save:set_flags(MAIL_SEEN, {keyword_spam})
104
+    pipe(mail, program_spam)
105
+  elseif mailbox_nonspam(dest) and spam then
106
+    save:set_flags(MAIL_SEEN, {})
107
+    pipe(mail, program_nonspam)
108
+  end
109
+end
110
+
111
+-- Called before new message is saved.
112
+function _M.save_begin(save)
113
+  local dest = save:get_transaction():get_box()
114
+  if info then
115
+    info("antispam: Save message to mailbox '"..dest:get_name().."'")
116
+  end
117
+  if mailbox_spam(dest) then
118
+    save:set_flags(MAIL_SEEN, {keyword_spam})
119
+  end
120
+end
121
+
122
+-- Called after new message is saved.
123
+function _M.save_finish(save)
124
+  local mail = save:get_dest_mail()
125
+  local spam = find(mail:get_keywords(), keyword_spam)
126
+  if spam then
127
+    pipe(mail, program_spam)
128
+  end
129
+end
130
+
131
+return _M

+ 16
- 0
examples/antispam/dovecot.conf.in View File

@@ -0,0 +1,16 @@
1
+mail_location = maildir:CURDIR/Mail
2
+mail_plugin_dir = CURDIR/../../src
3
+
4
+protocol imap {
5
+  mail_plugins = lua
6
+}
7
+
8
+plugin {
9
+  lua_script = CURDIR/antispam.lua
10
+  antispam_program_spam = true bogofilter -l -e -d %h/.bogofilter -Ns
11
+  antispam_program_nonspam = true bogofilter -l -e -d %h/.bogofilter -Sn
12
+  antispam_mailbox_spam = Spam
13
+  antispam_mailbox_nonspam = * !Spam !Trash !Sent !Drafts !archive.*
14
+  antispam_keyword_spam = Spam
15
+  antispam_verbose = yes
16
+}

+ 71
- 0
examples/antispam/test.py View File

@@ -0,0 +1,71 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+#
4
+# Test antispam example for Dovecot Lua storage plugin.
5
+# Copyright © 2015 Peter Colberg.
6
+# Distributed under the MIT license. (See accompanying file LICENSE.)
7
+
8
+import imaplib
9
+import email, time
10
+
11
+MESSAGE = str(email.message_from_string(
12
+"""From: <spam@example>
13
+To: <user@example>
14
+Date: Mon Feb 16 00:00:00 2015 -0500
15
+Subject: Spam!
16
+
17
+spam spam SPAM
18
+spam SPAM SPAM
19
+SPAM SPAM SPAM
20
+"""))
21
+
22
+M = imaplib.IMAP4_stream("/usr/lib/dovecot/imap -c dovecot.conf")
23
+M.create("Spam")
24
+M.create("Trash")
25
+
26
+M.append("INBOX", "\\Flagged", imaplib.Time2Internaldate(time.time()), MESSAGE)
27
+assert M.select("INBOX") == ("OK", ["1"])
28
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Flagged \\Recent))"])
29
+
30
+M.append("Spam", "\\Flagged", imaplib.Time2Internaldate(time.time()), MESSAGE)
31
+assert M.select("Spam") == ("OK", ["1"])
32
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Seen \\Recent Spam))"])
33
+
34
+M.select("Spam")
35
+M.copy(1, "Spam")
36
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Seen Spam))"])
37
+assert M.fetch(2, "(FLAGS)") == ("OK", ["2 (FLAGS (\\Seen \\Recent Spam))"])
38
+
39
+M.select("Spam")
40
+M.copy(1, "Trash")
41
+assert M.select("Trash") == ("OK", ["1"])
42
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Seen \\Recent Spam))"])
43
+
44
+M.select("Spam")
45
+M.copy(1, "INBOX")
46
+assert M.select("INBOX") == ("OK", ["2"])
47
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Flagged))"])
48
+assert M.fetch(2, "(FLAGS)") == ("OK", ["2 (FLAGS (\\Seen \\Recent))"])
49
+
50
+M.select("INBOX")
51
+M.copy(1, "INBOX")
52
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Flagged))"])
53
+assert M.fetch(2, "(FLAGS)") == ("OK", ["2 (FLAGS (\\Seen))"])
54
+assert M.fetch(3, "(FLAGS)") == ("OK", ["3 (FLAGS (\\Flagged \\Recent))"])
55
+
56
+M.select("Trash")
57
+M.copy(1, "Spam")
58
+assert M.select("Spam") == ("OK", ["3"])
59
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Seen Spam))"])
60
+assert M.fetch(2, "(FLAGS)") == ("OK", ["2 (FLAGS (\\Seen Spam))"])
61
+assert M.fetch(3, "(FLAGS)") == ("OK", ["3 (FLAGS (\\Seen \\Recent Spam))"])
62
+
63
+M.select("INBOX")
64
+M.copy(1, "Spam")
65
+assert M.select("Spam") == ("OK", ["4"])
66
+assert M.fetch(1, "(FLAGS)") == ("OK", ["1 (FLAGS (\\Seen Spam))"])
67
+assert M.fetch(2, "(FLAGS)") == ("OK", ["2 (FLAGS (\\Seen Spam))"])
68
+assert M.fetch(3, "(FLAGS)") == ("OK", ["3 (FLAGS (\\Seen Spam))"])
69
+assert M.fetch(4, "(FLAGS)") == ("OK", ["4 (FLAGS (\\Seen \\Recent Spam))"])
70
+
71
+M.close()

+ 26
- 0
src/Makefile View File

@@ -0,0 +1,26 @@
1
+CFLAGS = -O2 -Wall -Wformat-security
2
+LDFLAGS = -Wl,-s -Wl,--as-needed
3
+CCOPT = -fPIC -D'PLUGIN_VERSION="$(PLUGIN_VERSION)"'
4
+LDOPT = -shared
5
+
6
+LUAMODS = lua lua5.3 lua5.2 lua5.1 luajit
7
+LUAMOD = $(firstword $(foreach mod,$(LUAMODS),$(shell pkg-config --exists $(mod) && echo $(mod))))
8
+LUACFLAGS = $(shell pkg-config --cflags $(LUAMOD))
9
+LUALIBS = $(shell pkg-config --libs $(LUAMOD))
10
+
11
+PLUGIN = lua_plugin
12
+PLUGIN_VERSION = $(shell git describe --always)
13
+
14
+all: $(PLUGIN).so
15
+
16
+%.o: %.c
17
+	$(CC) -M -o $*.d $(CPPOPT) $(CCOPT) $(CPPFLAGS) $(CFLAGS) $(LUACFLAGS) $<
18
+	$(CC) -c -o $@ $(CPPOPT) $(CCOPT) $(CPPFLAGS) $(CFLAGS) $(LUACFLAGS) $<
19
+
20
+-include $(PLUGIN).d
21
+
22
+$(PLUGIN).so: $(PLUGIN).o
23
+	$(CC) -o $@ $< $(LDOPT) $(LDFLAGS) $(LUALIBS)
24
+
25
+clean:
26
+	$(RM) $(PLUGIN).so $(PLUGIN).o $(PLUGIN).d

+ 53
- 0
src/common.h View File

@@ -0,0 +1,53 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_COMMON_H
8
+#define DOVECOT_LUA_PLUGIN_COMMON_H
9
+
10
+#include <dovecot/config.h>
11
+#include <dovecot/lib.h>
12
+#include <lua.h>
13
+#include <lualib.h>
14
+#include <lauxlib.h>
15
+
16
+#if LUA_VERSION_NUM < 502
17
+#define lua_rawlen lua_objlen
18
+#endif
19
+
20
+#define DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(o)                              \
21
+  static void doveL_push_##o(lua_State *L, struct o *v)                 \
22
+  {                                                                     \
23
+    if (!v) return lua_pushnil(L);                                      \
24
+    struct o **ud = (struct o **)lua_newuserdata(L, sizeof(*ud));       \
25
+    *ud = v;                                                            \
26
+    lua_pushliteral(L, #o);                                             \
27
+    lua_rawget(L, LUA_REGISTRYINDEX);                                   \
28
+    lua_setmetatable(L, -2);                                            \
29
+  }                                                                     \
30
+                                                                        \
31
+  static struct o *doveL_check_##o(lua_State *L, int arg)               \
32
+  {                                                                     \
33
+    void *udata = luaL_checkudata(L, arg, #o);                          \
34
+    return *((struct o **)udata);                                       \
35
+  }
36
+
37
+#define DOVECOT_LUA_PLUGIN_OBJECT_END(o)                                \
38
+  static void doveL_register_##o(lua_State *L)                          \
39
+  {                                                                     \
40
+    const luaL_Reg *l;                                                  \
41
+    luaL_newmetatable(L, #o);                                           \
42
+    for (l = doveL_##o; l->name; l++) {                                 \
43
+      lua_pushstring(L, l->name);                                       \
44
+      lua_pushcfunction(L, l->func);                                    \
45
+      lua_rawset(L, -3);                                                \
46
+    }                                                                   \
47
+    lua_pushliteral(L, "__index");                                      \
48
+    lua_pushvalue(L, -2);                                               \
49
+    lua_rawset(L, -3);                                                  \
50
+    lua_pop(L, 1);                                                      \
51
+  }
52
+
53
+#endif

+ 38
- 0
src/imap_match_glob.h View File

@@ -0,0 +1,38 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_IMAP_MATCH_GLOB_H
8
+#define DOVECOT_LUA_PLUGIN_IMAP_MATCH_GLOB_H
9
+
10
+#include "common.h"
11
+#include <dovecot/imap-match.h>
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(imap_match_glob)
14
+
15
+static int doveL_imap_match(lua_State *L)
16
+{
17
+  struct imap_match_glob *glob = doveL_check_imap_match_glob(L, 1);
18
+  const char *value = luaL_checkstring(L, 2);
19
+  lua_pushboolean(L, (imap_match(glob, value) == IMAP_MATCH_YES));
20
+  return 1;
21
+}
22
+
23
+static int doveL_imap_match_deinit(lua_State *L)
24
+{
25
+  struct imap_match_glob *glob = doveL_check_imap_match_glob(L, 1);
26
+  imap_match_deinit(&glob);
27
+  return 0;
28
+}
29
+
30
+static const luaL_Reg doveL_imap_match_glob[] = {
31
+  {"__call", doveL_imap_match},
32
+  {"__gc", doveL_imap_match_deinit},
33
+  {NULL, NULL},
34
+};
35
+
36
+DOVECOT_LUA_PLUGIN_OBJECT_END(imap_match_glob)
37
+
38
+#endif

+ 29
- 0
src/istream.h View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_ISTREAM_H
8
+#define DOVECOT_LUA_PLUGIN_ISTREAM_H
9
+
10
+#include "common.h"
11
+#include <dovecot/istream.h>
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(istream)
14
+
15
+static int doveL_istream_read(lua_State *L)
16
+{
17
+  struct istream *input = doveL_check_istream(L, 1);
18
+  lua_pushstring(L, i_stream_read_next_line(input));
19
+  return 1;
20
+}
21
+
22
+static const luaL_Reg doveL_istream[] = {
23
+  {"read", doveL_istream_read},
24
+  {NULL, NULL},
25
+};
26
+
27
+DOVECOT_LUA_PLUGIN_OBJECT_END(istream)
28
+
29
+#endif

+ 88
- 0
src/lib_dovecot.h View File

@@ -0,0 +1,88 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_LIB_DOVECOT_H
8
+#define DOVECOT_LUA_PLUGIN_LIB_DOVECOT_H
9
+
10
+#include "imap_match_glob.h"
11
+#include "common.h"
12
+#include <dovecot/mail-types.h>
13
+
14
+static int doveL_imap_match_init(lua_State *L)
15
+{
16
+  const char *pattern = luaL_checkstring(L, 1);
17
+  bool inboxcase = lua_toboolean(L, 2);
18
+  const char *sep = luaL_checkstring(L, 3);
19
+  struct imap_match_glob *glob;
20
+  glob = imap_match_init(default_pool, pattern, inboxcase, *sep);
21
+  doveL_push_imap_match_glob(L, glob);
22
+  return 1;
23
+}
24
+
25
+#define DOVECOT_LUA_PLUGIN_LOG_LEVEL(l)         \
26
+  static int doveL_##l(lua_State *L)            \
27
+  {                                             \
28
+    const char *s = luaL_checkstring(L, 1);     \
29
+    i_##l("%s", s);                             \
30
+    return 0;                                   \
31
+  }
32
+
33
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(panic)
34
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(fatal)
35
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(error)
36
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(warning)
37
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(info)
38
+DOVECOT_LUA_PLUGIN_LOG_LEVEL(debug)
39
+
40
+static const luaL_Reg doveL_lib[] =  {
41
+  {"imap_match", doveL_imap_match_init},
42
+  {"panic", doveL_panic},
43
+  {"fatal", doveL_fatal},
44
+  {"error", doveL_error},
45
+  {"warning", doveL_warning},
46
+  {"info", doveL_info},
47
+  {"debug", doveL_debug},
48
+  {NULL, NULL},
49
+};
50
+
51
+#define doveL_set_flag(L, f)                    \
52
+  lua_pushliteral(L, #f);                       \
53
+  lua_pushinteger(L, f);                        \
54
+  lua_rawset(L, -3);
55
+
56
+static int doveL_load_lib(lua_State *L)
57
+{
58
+  const luaL_Reg *l;
59
+  lua_newtable(L);
60
+  for (l = doveL_lib; l->name; l++) {
61
+    lua_pushstring(L, l->name);
62
+    lua_pushcfunction(L, l->func);
63
+    lua_rawset(L, -3);
64
+  }
65
+  doveL_set_flag(L, MAIL_ANSWERED);
66
+  doveL_set_flag(L, MAIL_FLAGGED);
67
+  doveL_set_flag(L, MAIL_DELETED);
68
+  doveL_set_flag(L, MAIL_SEEN);
69
+  doveL_set_flag(L, MAIL_DRAFT);
70
+  doveL_set_flag(L, MAIL_RECENT);
71
+  lua_pushliteral(L, "PLUGIN_VERSION");
72
+  lua_pushliteral(L, PLUGIN_VERSION);
73
+  lua_rawset(L, -3);
74
+  return 1;
75
+}
76
+
77
+static void doveL_register_lib(lua_State *L)
78
+{
79
+  lua_getglobal(L, "package");
80
+  lua_pushliteral(L, "preload");
81
+  lua_rawget(L, -2);
82
+  lua_pushliteral(L, "dovecot");
83
+  lua_pushcfunction(L, doveL_load_lib);
84
+  lua_rawset(L, -3);
85
+  lua_pop(L, 2);
86
+}
87
+
88
+#endif

+ 258
- 0
src/lua_plugin.c View File

@@ -0,0 +1,258 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#include "istream.h"
8
+#include "mail.h"
9
+#include "mail_namespace.h"
10
+#include "mail_save_context.h"
11
+#include "mail_user.h"
12
+#include "mailbox.h"
13
+#include "mailbox_transaction_context.h"
14
+#include "lib_dovecot.h"
15
+#include "common.h"
16
+
17
+static MODULE_CONTEXT_DEFINE_INIT(doveL_storage_module, &mail_storage_module_register);
18
+static MODULE_CONTEXT_DEFINE_INIT(doveL_user_module, &mail_user_module_register);
19
+
20
+#define DOVECOT_LUA_PLUGIN_CONTEXT(box) MODULE_CONTEXT(box, doveL_storage_module)
21
+#define DOVECOT_LUA_PLUGIN_USER_CONTEXT(obj) MODULE_CONTEXT(obj, doveL_user_module)
22
+
23
+struct doveL_user_context {
24
+  union mail_user_module_context module_ctx;
25
+  lua_State *L;
26
+  int traceback;
27
+  int copy;
28
+  int save_begin;
29
+  int save_finish;
30
+};
31
+
32
+static void doveL_pcall_copy(struct mail_save_context *ctx, struct mail *mail)
33
+{
34
+  struct mail_user *user = ctx->transaction->box->storage->user;
35
+  struct doveL_user_context *user_ctx = DOVECOT_LUA_PLUGIN_USER_CONTEXT(user);
36
+  lua_State *L = user_ctx->L;
37
+
38
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->traceback);
39
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->copy);
40
+  doveL_push_mail_save_context(L, ctx);
41
+  doveL_push_mail(L, mail);
42
+  if (lua_pcall(L, 2, 0, -4)) {
43
+    i_error("%s", lua_tostring(L, -1));
44
+  }
45
+  lua_pop(L, 1);
46
+}
47
+
48
+static void doveL_pcall_save_begin(struct mail_save_context *ctx)
49
+{
50
+  struct mail_user *user = ctx->transaction->box->storage->user;
51
+  struct doveL_user_context *user_ctx = DOVECOT_LUA_PLUGIN_USER_CONTEXT(user);
52
+  lua_State *L = user_ctx->L;
53
+
54
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->traceback);
55
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->save_begin);
56
+  doveL_push_mail_save_context(L, ctx);
57
+  if (lua_pcall(L, 1, 0, -3)) {
58
+    i_error("%s", lua_tostring(L, -1));
59
+  }
60
+  lua_pop(L, 1);
61
+}
62
+
63
+static void doveL_pcall_save_finish(struct mail_save_context *ctx)
64
+{
65
+  struct mail_user *user = ctx->transaction->box->storage->user;
66
+  struct doveL_user_context *user_ctx = DOVECOT_LUA_PLUGIN_USER_CONTEXT(user);
67
+  lua_State *L = user_ctx->L;
68
+
69
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->traceback);
70
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->save_finish);
71
+  doveL_push_mail_save_context(L, ctx);
72
+  if (lua_pcall(L, 1, 0, -3)) {
73
+    i_error("%s", lua_tostring(L, -1));
74
+  }
75
+  lua_pop(L, 1);
76
+}
77
+
78
+static int doveL_copy(struct mail_save_context *ctx, struct mail *mail)
79
+{
80
+  struct mailbox *box = ctx->transaction->box;
81
+  union mailbox_module_context *mbox_ctx = DOVECOT_LUA_PLUGIN_CONTEXT(box);
82
+
83
+  doveL_pcall_copy(ctx, mail);
84
+
85
+  if (mbox_ctx->super.copy(ctx, mail) < 0) {
86
+    return -1;
87
+  }
88
+
89
+  return 0;
90
+}
91
+
92
+static int doveL_save_begin(struct mail_save_context *ctx, struct istream *input)
93
+{
94
+  struct mailbox *box = ctx->transaction->box;
95
+  union mailbox_module_context *mbox_ctx = DOVECOT_LUA_PLUGIN_CONTEXT(box);
96
+
97
+  if (ctx->saving) doveL_pcall_save_begin(ctx);
98
+
99
+  if (mbox_ctx->super.save_begin(ctx, input) < 0) {
100
+    return -1;
101
+  }
102
+
103
+  return 0;
104
+}
105
+
106
+static int doveL_save_finish(struct mail_save_context *ctx)
107
+{
108
+  struct mailbox *box = ctx->transaction->box;
109
+  union mailbox_module_context *mbox_ctx = DOVECOT_LUA_PLUGIN_CONTEXT(box);
110
+
111
+  if (mbox_ctx->super.save_finish(ctx) < -1) {
112
+    return -1;
113
+  }
114
+
115
+  if (ctx->saving) doveL_pcall_save_finish(ctx);
116
+
117
+  return 0;
118
+}
119
+
120
+static void doveL_mailbox_allocated(struct mailbox *box)
121
+{
122
+  struct mail_user *user = box->storage->user;
123
+  struct doveL_user_context *user_ctx = DOVECOT_LUA_PLUGIN_USER_CONTEXT(user);
124
+  struct mailbox_vfuncs *v = box->vlast;
125
+  union mailbox_module_context *mbox_ctx;
126
+
127
+  mbox_ctx = p_new(box->pool, union mailbox_module_context, 1);
128
+  mbox_ctx->super = *v;
129
+  box->vlast = &mbox_ctx->super;
130
+
131
+  MODULE_CONTEXT_SET_SELF(box, doveL_storage_module, mbox_ctx);
132
+
133
+  if (user_ctx->copy != LUA_REFNIL) {
134
+    v->copy = doveL_copy;
135
+  }
136
+  if (user_ctx->save_begin != LUA_REFNIL) {
137
+    v->save_begin = doveL_save_begin;
138
+  }
139
+  if (user_ctx->save_finish != LUA_REFNIL) {
140
+    v->save_finish = doveL_save_finish;
141
+  }
142
+}
143
+
144
+static int doveL_traceback(lua_State *L)
145
+{
146
+  lua_pushvalue(L, lua_upvalueindex(1));
147
+  lua_pushvalue(L, 1);
148
+  lua_pushnumber(L, 2);
149
+  lua_call(L, 2, 1);
150
+  return 1;
151
+}
152
+
153
+static int doveL_ref_traceback(lua_State *L)
154
+{
155
+  int ref;
156
+  lua_getglobal(L, "debug");
157
+  lua_pushliteral(L, "traceback");
158
+  lua_rawget(L, -2);
159
+  lua_pushcclosure(L, doveL_traceback, 1);
160
+  ref = luaL_ref(L, LUA_REGISTRYINDEX);
161
+  lua_pop(L, 1);
162
+  return ref;
163
+}
164
+
165
+static int doveL_ref_field(lua_State *L, int index, const char *k)
166
+{
167
+  int ref;
168
+  lua_pushvalue(L, index);
169
+  lua_pushstring(L, k);
170
+  lua_rawget(L, -2);
171
+  ref = luaL_ref(L, LUA_REGISTRYINDEX);
172
+  lua_pop(L, 1);
173
+  return ref;
174
+}
175
+
176
+static void doveL_user_deinit(struct mail_user *user)
177
+{
178
+  struct doveL_user_context *user_ctx = DOVECOT_LUA_PLUGIN_USER_CONTEXT(user);
179
+  lua_State *L = user_ctx->L;
180
+
181
+  lua_close(L);
182
+  user_ctx->module_ctx.super.deinit(user);
183
+}
184
+
185
+static void doveL_user_created(struct mail_user *user)
186
+{
187
+  struct mail_user_vfuncs *v = user->vlast;
188
+  struct doveL_user_context *user_ctx;
189
+  const char *file;
190
+  lua_State *L = luaL_newstate();
191
+
192
+  user_ctx = p_new(user->pool, struct doveL_user_context, 1);
193
+  user_ctx->module_ctx.super = *v;
194
+  user->vlast = &user_ctx->module_ctx.super;
195
+  user_ctx->L = L;
196
+  user_ctx->traceback = LUA_REFNIL;
197
+  user_ctx->copy = LUA_REFNIL;
198
+  user_ctx->save_begin = LUA_REFNIL;
199
+  user_ctx->save_finish = LUA_REFNIL;
200
+
201
+  MODULE_CONTEXT_SET(user, doveL_user_module, user_ctx);
202
+
203
+  v->deinit = doveL_user_deinit;
204
+
205
+  if (!(file = mail_user_plugin_getenv(user, "lua_script"))) {
206
+    i_error("Missing plugin config variable 'lua_script'");
207
+    return;
208
+  }
209
+
210
+  lua_gc(L, LUA_GCSTOP, 0);
211
+  luaL_openlibs(L);
212
+  lua_gc(L, LUA_GCRESTART, 0);
213
+  user_ctx->traceback = doveL_ref_traceback(L);
214
+
215
+  doveL_register_imap_match_glob(L);
216
+  doveL_register_istream(L);
217
+  doveL_register_mail(L);
218
+  doveL_register_mail_namespace(L);
219
+  doveL_register_mail_save_context(L);
220
+  doveL_register_mail_user(L);
221
+  doveL_register_mailbox(L);
222
+  doveL_register_mailbox_transaction_context(L);
223
+  doveL_register_lib(L);
224
+
225
+  lua_rawgeti(L, LUA_REGISTRYINDEX, user_ctx->traceback);
226
+  if (luaL_loadfile(L, file)) {
227
+    i_error("%s", lua_tostring(L, -1));
228
+    return;
229
+  }
230
+  doveL_push_mail_user(L, user);
231
+  if (lua_pcall(L, 1, 1, -4)) {
232
+    i_error("%s", lua_tostring(L, -1));
233
+    return;
234
+  }
235
+  if (lua_type(L, -1) != LUA_TTABLE) {
236
+    i_error("Lua script did not return hook function table");
237
+    return;
238
+  }
239
+  user_ctx->copy = doveL_ref_field(L, -1, "copy");
240
+  user_ctx->save_begin = doveL_ref_field(L, -1, "save_begin");
241
+  user_ctx->save_finish = doveL_ref_field(L, -1, "save_finish");
242
+  lua_pop(L, 2);
243
+}
244
+
245
+static const struct mail_storage_hooks doveL_mail_storage_hooks = {
246
+  .mail_user_created = doveL_user_created,
247
+  .mailbox_allocated = doveL_mailbox_allocated,
248
+};
249
+
250
+void lua_plugin_init(struct module *module)
251
+{
252
+  mail_storage_hooks_add(module, &doveL_mail_storage_hooks);
253
+}
254
+
255
+void lua_plugin_deinit(struct module *module)
256
+{
257
+  mail_storage_hooks_remove(&doveL_mail_storage_hooks);
258
+}

+ 66
- 0
src/mail.h View File

@@ -0,0 +1,66 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAIL_H
8
+#define DOVECOT_LUA_PLUGIN_MAIL_H
9
+
10
+#include "mailbox.h"
11
+#include "istream.h"
12
+#include "common.h"
13
+
14
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mail)
15
+
16
+static int doveL_mail_get_box(lua_State *L)
17
+{
18
+  struct mail *mail = doveL_check_mail(L, 1);
19
+  doveL_push_mailbox(L, mail->box);
20
+  return 1;
21
+}
22
+
23
+static int doveL_mail_get_flags(lua_State *L)
24
+{
25
+  struct mail *mail = doveL_check_mail(L, 1);
26
+  lua_pushinteger(L, mail_get_flags(mail));
27
+  return 1;
28
+}
29
+
30
+static int doveL_mail_get_keywords(lua_State *L)
31
+{
32
+  struct mail *mail = doveL_check_mail(L, 1);
33
+  const char *const *keywords = mail_get_keywords(mail);
34
+  const char *s;
35
+  int i = 0;
36
+  lua_newtable(L);
37
+  while ((s = keywords[i])) {
38
+    lua_pushstring(L, s);
39
+    lua_rawseti(L, -2, ++i);
40
+  }
41
+  return 1;
42
+}
43
+
44
+static int doveL_mail_get_stream(lua_State *L)
45
+{
46
+  struct mail *mail = doveL_check_mail(L, 1);
47
+  struct istream *stream;
48
+  if (mail_get_stream(mail, NULL, NULL, &stream) < 0) {
49
+    lua_pushliteral(L, "Failed to get mail stream");
50
+    return lua_error(L);
51
+  }
52
+  doveL_push_istream(L, stream);
53
+  return 1;
54
+}
55
+
56
+static const luaL_Reg doveL_mail[] = {
57
+  {"get_box", doveL_mail_get_box},
58
+  {"get_flags", doveL_mail_get_flags},
59
+  {"get_keywords", doveL_mail_get_keywords},
60
+  {"get_stream", doveL_mail_get_stream},
61
+  {NULL, NULL},
62
+};
63
+
64
+DOVECOT_LUA_PLUGIN_OBJECT_END(mail)
65
+
66
+#endif

+ 30
- 0
src/mail_namespace.h View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAIL_NAMESPACE_H
8
+#define DOVECOT_LUA_PLUGIN_MAIL_NAMESPACE_H
9
+
10
+#include "common.h"
11
+#include <dovecot/mail-namespace.h>
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mail_namespace)
14
+
15
+static int doveL_mail_namespace_get_sep(lua_State *L)
16
+{
17
+  struct mail_namespace *ns = doveL_check_mail_namespace(L, 1);
18
+  char sep = mail_namespace_get_sep(ns);
19
+  lua_pushlstring(L, &sep, 1);
20
+  return 1;
21
+}
22
+
23
+static const luaL_Reg doveL_mail_namespace[] = {
24
+  {"get_sep", doveL_mail_namespace_get_sep},
25
+  {NULL, NULL},
26
+};
27
+
28
+DOVECOT_LUA_PLUGIN_OBJECT_END(mail_namespace)
29
+
30
+#endif

+ 65
- 0
src/mail_save_context.h View File

@@ -0,0 +1,65 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAIL_SAVE_CONTEXT_H
8
+#define DOVECOT_LUA_PLUGIN_MAIL_SAVE_CONTEXT_H
9
+
10
+#include "mailbox_transaction_context.h"
11
+#include "common.h"
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mail_save_context)
14
+
15
+static int doveL_mail_save_context_get_dest_mail(lua_State *L)
16
+{
17
+  struct mail_save_context *ctx = doveL_check_mail_save_context(L, 1);
18
+  doveL_push_mail(L, ctx->dest_mail);
19
+  return 1;
20
+}
21
+
22
+static int doveL_mail_save_context_get_transaction(lua_State *L)
23
+{
24
+  struct mail_save_context *ctx = doveL_check_mail_save_context(L, 1);
25
+  doveL_push_mailbox_transaction_context(L, ctx->transaction);
26
+  return 1;
27
+}
28
+
29
+static int doveL_mail_save_context_set_flags(lua_State *L)
30
+{
31
+  struct mail_save_context *ctx = doveL_check_mail_save_context(L, 1);
32
+  struct mailbox *box = ctx->transaction->box;
33
+  enum mail_flags flags = luaL_checkinteger(L, 2);
34
+  struct mail_keywords *keywords;
35
+  const char **list;
36
+  int len, i;
37
+  luaL_checktype(L, 3, LUA_TTABLE);
38
+  len = lua_rawlen(L, 3);
39
+  list = t_new(const char *, len+1);
40
+  for (i = 0; i < len; ++i) {
41
+    lua_rawgeti(L, 3, i+1);
42
+    if (!(list[i] = lua_tostring(L, -1))) {
43
+      lua_pushliteral(L, "Invalid value in keyword list");
44
+      return lua_error(L);
45
+    }
46
+    lua_pop(L, 1);
47
+  }
48
+  if (mailbox_keywords_create(box, list, &keywords) < 0) {
49
+    lua_pushstring(L, mailbox_get_last_error(box, NULL));
50
+    return lua_error(L);
51
+  }
52
+  mailbox_save_set_flags(ctx, flags, keywords);
53
+  return 1;
54
+}
55
+
56
+static const luaL_Reg doveL_mail_save_context[] = {
57
+  {"get_dest_mail", doveL_mail_save_context_get_dest_mail},
58
+  {"get_transaction", doveL_mail_save_context_get_transaction},
59
+  {"set_flags", doveL_mail_save_context_set_flags},
60
+  {NULL, NULL},
61
+};
62
+
63
+DOVECOT_LUA_PLUGIN_OBJECT_END(mail_save_context)
64
+
65
+#endif

+ 76
- 0
src/mail_user.h View File

@@ -0,0 +1,76 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAIL_USER_H
8
+#define DOVECOT_LUA_PLUGIN_MAIL_USER_H
9
+
10
+#include "common.h"
11
+#include <dovecot/mail-user.h>
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mail_user)
14
+
15
+static int doveL_mail_user_get_username(lua_State *L)
16
+{
17
+  struct mail_user *user = doveL_check_mail_user(L, 1);
18
+  lua_pushstring(L, user->username);
19
+  return 1;
20
+}
21
+
22
+static int doveL_mail_user_get_uid(lua_State *L)
23
+{
24
+  struct mail_user *user = doveL_check_mail_user(L, 1);
25
+  lua_pushinteger(L, user->uid);
26
+  return 1;
27
+}
28
+
29
+static int doveL_mail_user_get_gid(lua_State *L)
30
+{
31
+  struct mail_user *user = doveL_check_mail_user(L, 1);
32
+  lua_pushinteger(L, user->gid);
33
+  return 1;
34
+}
35
+
36
+static int doveL_mail_user_get_home(lua_State *L)
37
+{
38
+  struct mail_user *user = doveL_check_mail_user(L, 1);
39
+  const char *s;
40
+  if (mail_user_get_home(user, &s) < 0) {
41
+    lua_pushliteral(L, "Failed to lookup user home directory");
42
+    return lua_error(L);
43
+  }
44
+  lua_pushstring(L, s);
45
+  return 1;
46
+}
47
+
48
+static int doveL_mail_user_home_expand(lua_State *L)
49
+{
50
+  struct mail_user *user = doveL_check_mail_user(L, 1);
51
+  const char *path = luaL_checkstring(L, 2);
52
+  lua_pushstring(L, mail_user_home_expand(user, path));
53
+  return 1;
54
+}
55
+
56
+static int doveL_mail_user_plugin_getenv(lua_State *L)
57
+{
58
+  struct mail_user *user = doveL_check_mail_user(L, 1);
59
+  const char *s = luaL_checkstring(L, 2);
60
+  lua_pushstring(L, mail_user_plugin_getenv(user, s));
61
+  return 1;
62
+}
63
+
64
+static const luaL_Reg doveL_mail_user[] = {
65
+  {"get_username", doveL_mail_user_get_username},
66
+  {"get_uid", doveL_mail_user_get_uid},
67
+  {"get_gid", doveL_mail_user_get_gid},
68
+  {"get_home", doveL_mail_user_get_home},
69
+  {"home_expand", doveL_mail_user_home_expand},
70
+  {"plugin_getenv", doveL_mail_user_plugin_getenv},
71
+  {NULL, NULL},
72
+};
73
+
74
+DOVECOT_LUA_PLUGIN_OBJECT_END(mail_user)
75
+
76
+#endif

+ 39
- 0
src/mailbox.h View File

@@ -0,0 +1,39 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAILBOX_H
8
+#define DOVECOT_LUA_PLUGIN_MAILBOX_H
9
+
10
+#include "mail_namespace.h"
11
+#include "common.h"
12
+#include <dovecot/mail-storage.h>
13
+#include <dovecot/mail-storage-private.h>
14
+
15
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mailbox)
16
+
17
+static int doveL_mailbox_get_name(lua_State *L)
18
+{
19
+  struct mailbox *box = doveL_check_mailbox(L, 1);
20
+  lua_pushstring(L, mailbox_get_name(box));
21
+  return 1;
22
+}
23
+
24
+static int doveL_mailbox_get_namespace(lua_State *L)
25
+{
26
+  struct mailbox *box = doveL_check_mailbox(L, 1);
27
+  doveL_push_mail_namespace(L, mailbox_get_namespace(box));
28
+  return 1;
29
+}
30
+
31
+static const luaL_Reg doveL_mailbox[] = {
32
+  {"get_name", doveL_mailbox_get_name},
33
+  {"get_namespace", doveL_mailbox_get_namespace},
34
+  {NULL, NULL},
35
+};
36
+
37
+DOVECOT_LUA_PLUGIN_OBJECT_END(mailbox)
38
+
39
+#endif

+ 29
- 0
src/mailbox_transaction_context.h View File

@@ -0,0 +1,29 @@
1
+/*
2
+ * Lua script plugin for Dovecot.
3
+ * Copyright © 2015 Peter Colberg.
4
+ * Distributed under the MIT license. (See accompanying file LICENSE.)
5
+ */
6
+
7
+#ifndef DOVECOT_LUA_PLUGIN_MAILBOX_TRANSACTION_CONTEXT_H
8
+#define DOVECOT_LUA_PLUGIN_MAILBOX_TRANSACTION_CONTEXT_H
9
+
10
+#include "common.h"
11
+#include "mailbox.h"
12
+
13
+DOVECOT_LUA_PLUGIN_OBJECT_BEGIN(mailbox_transaction_context)
14
+
15
+static int doveL_mailbox_transaction_context_get_box(lua_State *L)
16
+{
17
+  struct mailbox_transaction_context *transaction = doveL_check_mailbox_transaction_context(L, 1);
18
+  doveL_push_mailbox(L, transaction->box);
19
+  return 1;
20
+}
21
+
22
+static const luaL_Reg doveL_mailbox_transaction_context[] = {
23
+  {"get_box", doveL_mailbox_transaction_context_get_box},
24
+  {NULL, NULL},
25
+};
26
+
27
+DOVECOT_LUA_PLUGIN_OBJECT_END(mailbox_transaction_context)
28
+
29
+#endif

Loading…
Cancel
Save