6fe2b4d629bc8f179270680e9ddb2bd07b797c29
[apps/madmutt.git] / lib-lua / madmutt.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or (at
5  *  your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful, but
8  *  WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
15  *  MA 02110-1301, USA.
16  *
17  *  Copyright © 2007 Pierre Habouzit
18  */
19
20 #include <lib-lib/lib-lib.h>
21
22 #include <sys/types.h>
23 #include <pwd.h>
24
25 #include "lib-lua_priv.h"
26
27 #include "../mutt.h"
28
29 /* {{{ madmutt functions */
30
31 static int madmutt_pwd(lua_State *L)
32 {
33     char path[_POSIX_PATH_MAX];
34     getcwd(path, sizeof(path));
35     lua_pushstring(L, path);
36     return 1;
37 }
38
39 static int madmutt_folder_path(lua_State *L)
40 {
41     lua_pushstring(L, CurrentFolder ?: "");
42     return 1;
43 }
44
45 static int madmutt_folder_name(lua_State *L)
46 {
47     const char *p;
48
49     if (!m_strisempty(Maildir)
50     && m_strstart(CurrentFolder, Maildir, &p) && *p) {
51         while (*p == '/')
52             p++;
53         lua_pushstring(L, p);
54     } else {
55         p = strchr(CurrentFolder ?: "", '/');
56         lua_pushstring(L, p ? p + 1 : (CurrentFolder ?: ""));
57     }
58     return 1;
59 }
60
61 static quadopt_t quadopt_parse(const char *s)
62 {
63     if (!m_strcasecmp("yes", s))
64         return M_YES;
65     if (!m_strcasecmp("no", s))
66         return M_NO;
67     if (!m_strcasecmp("ask-yes", s))
68         return M_ASKYES;
69     if (!m_strcasecmp("ask-no", s))
70         return M_ASKNO;
71     return -1;
72 }
73
74 static int madmutt_assign(lua_State *L)
75 {
76     const char *idx = luaL_checkstring(L, 2);
77     const char *val = luaL_checkstring(L, 3);
78     int tk;
79
80     switch ((tk = lua_which_token(idx, -1))) {
81         char buf[STRING];
82         int i;
83
84       case LTK_DOTLOCK:
85       case LTK_SENDMAIL:
86       case LTK_SHELL:
87       case LTK_EDITOR:
88         _mutt_expand_path(buf, sizeof(buf), val, 0);
89         val = buf;
90         /* FALLTHROUGH */
91
92         mlua_regsets(tk, val);
93         return 0;
94
95       case LTK_QUIT:
96         i = quadopt_parse(val);
97         if (i < 0)
98             return luaL_error(L, "invalid quad option value: '%s'", val);
99         mlua_regseti(tk, i);
100         return 0;
101
102       case LTK_UNKNOWN:
103       case LTK_count:
104         break;
105     }
106
107     return luaL_error(L, "read-only or inexistant property '%s'", idx, tk);
108 }
109
110 static int madmutt_get(lua_State *L)
111 {
112     const char *idx = luaL_checkstring(L, 2);
113     enum lua_token tk = lua_which_token(idx, -1);
114
115     switch (tk) {
116       case LTK_DOTLOCK:
117       case LTK_SENDMAIL:
118       case LTK_SHELL:
119       case LTK_EDITOR:
120         lua_pushstring(L, mlua_reggets(tk));
121         return 1;
122
123       case LTK_QUIT:
124         switch (mlua_reggeti(tk)) {
125           case M_YES:
126             lua_pushstring(L, "yes");
127             return 1;
128           case M_NO:
129             lua_pushstring(L, "no");
130             return 1;
131           case M_ASKNO:
132             lua_pushstring(L, "ask-no");
133             return 1;
134           case M_ASKYES:
135             lua_pushstring(L, "ask-yes");
136             return 1;
137           default:
138             return 0;
139         }
140
141       case LTK_UNKNOWN:
142       case LTK_count:
143         break;
144     }
145
146     lua_getmetatable(L, 1);
147     lua_replace(L, 1);
148     lua_rawget(L, 1);
149     return 1;
150 }
151
152 static const struct luaL_Reg madmutt_module_funcs[] = {
153     { "pwd",         madmutt_pwd },
154     /*
155      ** .pp
156      ** \fIThis is a read-only system property and, at runtime,
157      ** specifies the current working directory of the madmutt
158      ** binary.\fP
159      */
160     { "folder_path", madmutt_folder_path },
161     /*
162      ** .pp
163      ** \fIThis is a read-only system property and, at runtime,
164      ** specifies the full path or URI of the folder currently
165      ** open (if any).\fP
166      */
167     { "folder_name", madmutt_folder_name },
168     /*
169      ** .pp
170      ** \fIThis is a read-only system property and, at runtime,
171      ** specifies the actual name of the folder as far as it could
172      ** be detected.\fP
173      ** .pp
174      ** For detection, $$$folder is first taken into account
175      ** and simply stripped to form the result when a match is found. For
176      ** example, with $$$folder being \fTimap://host\fP and the folder is
177      ** \fTimap://host/INBOX/foo\fP, $$$madmutt_folder_name will be just
178      ** \fTINBOX/foo\fP.)
179      ** .pp
180      ** Second, if the initial portion of a name is not $$$folder,
181      ** the result will be everything after the last ``/''.
182      ** .pp
183      ** Third and last, the result will be just the name if neither
184      ** $$$folder nor a ``/'' were found in the name.
185      */
186
187     { "__newindex",  madmutt_assign },
188     { "__index",     madmutt_get },
189     { NULL, NULL }
190 };
191
192 /* }}} */
193
194 /* {{{ read-only properties */
195
196 static const struct {
197     const char *key;
198     const char *value;
199 } madmutt_module_vars[] = {
200     { "version",    VERSION },
201     /*
202      ** .pp
203      ** \fIThis is a read-only system property and specifies madmutt's
204      ** version string.\fP
205      */
206     { "sysconfdir", SYSCONFDIR },
207     /*
208      ** .pp
209      ** \fIThis is a read-only system property and specifies madmutt's
210      ** subversion revision string.\fP
211      */
212     { "bindir",     BINDIR },
213     /*
214      ** .pp
215      ** \fIThis is a read-only system property and specifies the
216      ** directory containing the madmutt binary.\fP
217      */
218     { "docdir",     PKGDOCDIR },
219     /*
220      ** .pp
221      ** \fIThis is a read-only system property and specifies the
222      ** directory containing the madmutt documentation.\fP
223      */
224 #ifdef USE_HCACHE
225 #if defined(HAVE_QDBM)
226     { "hcache_backend", "qdbm" },
227 #elif defined(HAVE_GDBM)
228     { "hcache_backend", "gdbm" },
229 #elif defined(HAVE_DB4)
230     { "hcache_backend", "db4" },
231 #else
232     { "hcache_backend", "unknown" },
233 #endif
234     /*
235      ** .pp
236      ** \fIThis is a read-only system property and specifies the
237      ** header chaching's database backend.\fP
238      */
239 #endif
240 };
241
242 /* }}} */
243
244 /* {{{ madmutt magic properties */
245
246 static void madmutt_init_editor(char *buf, ssize_t len)
247 {
248     m_strcpy(buf, len, getenv("VISUAL") ?: getenv("EDITOR") ?: "vi");
249 }
250
251 static void madmutt_init_shell(char *buf, ssize_t len)
252 {
253     struct passwd *pw = getpwuid(getuid());
254
255     if (pw) {
256         m_strcpy(buf, len, pw->pw_shell);
257         _mutt_expand_path(buf, len, pw->pw_shell, 0);
258     } else {
259         m_strcpy(buf, len, getenv("SHELL") ?: "/bin/sh");
260     }
261 }
262
263 static const struct {
264     const char *key;
265     void (*fun)(char *buf, ssize_t len);
266     const char *val;
267 } madmutt_module_vars2[] = {
268     { "dotlock",     NULL, BINDIR "/mutt_dotlock" },
269     /*
270      ** .pp
271      ** Contains the path of the \fTmadmutt_dotlock(1)\fP binary to be used by
272      ** Madmutt.
273      */
274     { "editor",       madmutt_init_editor, NULL },
275     /*
276      ** .pp
277      ** This variable specifies which editor is used by Madmutt.
278      ** It defaults to the value of the \fT$$$VISUAL\fP, or \fT$$$EDITOR\fP, environment
279      ** variable, or to the string "\fTvi\fP" if neither of those are set.
280      */
281     { "sendmail",    NULL, SENDMAIL " -oem -oi" },
282     /*
283      ** .pp
284      ** Specifies the program and arguments used to deliver mail sent by Madmutt.
285      ** Madmutt expects that the specified program interprets additional
286      ** arguments as recipient addresses.
287      */
288     { "shell",       madmutt_init_shell, NULL },
289     /*
290      ** .pp
291      ** Command to use when spawning a subshell.  By default, the user's login
292      ** shell from \fT/etc/passwd\fP is used.
293      */
294     { "quit",         NULL, "yes" },
295     /*
296      ** .pp
297      ** This variable controls whether ``quit'' and ``exit'' actually quit
298      ** from Madmutt.  If it set to \fIyes\fP, they do quit, if it is set to \fIno\fP, they
299      ** have no effect, and if it is set to \fIask-yes\fP or \fIask-no\fP, you are
300      ** prompted for confirmation when you try to quit.
301      */
302 };
303
304 /* }}} */
305
306 int luaopen_madmutt(lua_State *L)
307 {
308     int i;
309
310     lua_newuserdata(L, sizeof(void*));
311     luaL_newmetatable(L, "madmutt.core");
312
313     luaL_openlib(L, NULL, madmutt_module_funcs, 0);
314
315     for (i = 0; i < countof(madmutt_module_vars); i++) {
316         lua_pushstring(L, madmutt_module_vars[i].value);
317         lua_setfield(L, -2, madmutt_module_vars[i].key);
318     }
319
320     lua_setmetatable(L, -2);
321
322     for (i = 0; i < countof(madmutt_module_vars2); i++) {
323         if (madmutt_module_vars2[i].fun) {
324             char buf[STRING];
325             (madmutt_module_vars2[i].fun)(buf, sizeof(buf));
326             lua_pushstring(L, buf);
327         } else {
328             lua_pushstring(L, madmutt_module_vars2[i].val);
329         }
330         lua_setfield(L, -2, madmutt_module_vars2[i].key);
331     }
332
333     lua_setglobal(L, "madmutt");
334     return 1;
335 }