3bd3428b629329660ab56406a2440381c6b28492
[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 int madmutt_assign(lua_State *L)
62 {
63     const char *idx = luaL_checkstring(L, 2);
64     const char *val = luaL_checkstring(L, 3);
65     int tk;
66
67     switch ((tk = lua_which_token(idx, -1))) {
68         char buf[STRING];
69
70       default:
71         luaL_error(L, "read-only or inexistant property '%s'", idx, tk);
72         return 0;
73
74       case LTK_DOTLOCK:
75       case LTK_SENDMAIL:
76       case LTK_SHELL:
77         _mutt_expand_path(buf, sizeof(buf), val, 0);
78         val = buf;
79         /* FALLTHROUGH */
80
81       case LTK_EDITOR:
82         mlua_regsets(tk, val);
83         return 0;
84     }
85 }
86
87 static int madmutt_get(lua_State *L)
88 {
89     const char *idx = luaL_checkstring(L, 2);
90     int tk;
91
92     switch ((tk = lua_which_token(idx, -1))) {
93       default:
94         lua_getmetatable(L, 1);
95         lua_replace(L, 1);
96         lua_rawget(L, 1);
97         return 1;
98
99       case LTK_DOTLOCK:
100       case LTK_SENDMAIL:
101       case LTK_SHELL:
102         lua_pushstring(L, mlua_reggets(tk));
103         break;
104     }
105
106     return 1;
107 }
108
109 static const struct luaL_Reg madmutt_module_funcs[] = {
110     { "pwd",         madmutt_pwd },
111     /*
112      ** .pp
113      ** \fIThis is a read-only system property and, at runtime,
114      ** specifies the current working directory of the madmutt
115      ** binary.\fP
116      */
117     { "folder_path", madmutt_folder_path },
118     /*
119      ** .pp
120      ** \fIThis is a read-only system property and, at runtime,
121      ** specifies the full path or URI of the folder currently
122      ** open (if any).\fP
123      */
124     { "folder_name", madmutt_folder_name },
125     /*
126      ** .pp
127      ** \fIThis is a read-only system property and, at runtime,
128      ** specifies the actual name of the folder as far as it could
129      ** be detected.\fP
130      ** .pp
131      ** For detection, $$$folder is first taken into account
132      ** and simply stripped to form the result when a match is found. For
133      ** example, with $$$folder being \fTimap://host\fP and the folder is
134      ** \fTimap://host/INBOX/foo\fP, $$$madmutt_folder_name will be just
135      ** \fTINBOX/foo\fP.)
136      ** .pp
137      ** Second, if the initial portion of a name is not $$$folder,
138      ** the result will be everything after the last ``/''.
139      ** .pp
140      ** Third and last, the result will be just the name if neither
141      ** $$$folder nor a ``/'' were found in the name.
142      */
143
144     { "__newindex",  madmutt_assign },
145     { "__index",     madmutt_get },
146     { NULL, NULL }
147 };
148
149 /* }}} */
150
151 /* {{{ read-only properties */
152
153 static const struct {
154     const char *key;
155     const char *value;
156 } madmutt_module_vars[] = {
157     { "version",    VERSION },
158     /*
159      ** .pp
160      ** \fIThis is a read-only system property and specifies madmutt's
161      ** version string.\fP
162      */
163     { "sysconfdir", SYSCONFDIR },
164     /*
165      ** .pp
166      ** \fIThis is a read-only system property and specifies madmutt's
167      ** subversion revision string.\fP
168      */
169     { "bindir",     BINDIR },
170     /*
171      ** .pp
172      ** \fIThis is a read-only system property and specifies the
173      ** directory containing the madmutt binary.\fP
174      */
175     { "docdir",     PKGDOCDIR },
176     /*
177      ** .pp
178      ** \fIThis is a read-only system property and specifies the
179      ** directory containing the madmutt documentation.\fP
180      */
181 #ifdef USE_HCACHE
182 #if defined(HAVE_QDBM)
183     { "hcache_backend", "qdbm" },
184 #elif defined(HAVE_GDBM)
185     { "hcache_backend", "gdbm" },
186 #elif defined(HAVE_DB4)
187     { "hcache_backend", "db4" },
188 #else
189     { "hcache_backend", "unknown" },
190 #endif
191     /*
192      ** .pp
193      ** \fIThis is a read-only system property and specifies the
194      ** header chaching's database backend.\fP
195      */
196 #endif
197 };
198
199 /* }}} */
200
201 /* {{{ madmutt magic properties */
202
203 static void madmutt_init_editor(char *buf, ssize_t len)
204 {
205     m_strcpy(buf, len, getenv("VISUAL") ?: getenv("EDITOR") ?: "vi");
206     fprintf("%s\n", buf);
207 }
208
209 static void madmutt_init_shell(char *buf, ssize_t len)
210 {
211     struct passwd *pw = getpwuid(getuid());
212
213     if (pw) {
214         m_strcpy(buf, len, pw->pw_shell);
215         _mutt_expand_path(buf, len, pw->pw_shell, 0);
216     } else {
217         m_strcpy(buf, len, getenv("SHELL") ?: "/bin/sh");
218     }
219 }
220
221 static const struct {
222     const char *key;
223     void (*fun)(char *buf, ssize_t len);
224     const char *val;
225 } madmutt_module_vars2[] = {
226     { "dotlock",     NULL, BINDIR "/mutt_dotlock" },
227     /*
228      ** .pp
229      ** Contains the path of the \fTmadmutt_dotlock(1)\fP binary to be used by
230      ** Madmutt.
231      */
232     {"editor",       madmutt_init_editor, NULL },
233     /*
234      ** .pp
235      ** This variable specifies which editor is used by Madmutt.
236      ** It defaults to the value of the \fT$$$VISUAL\fP, or \fT$$$EDITOR\fP, environment
237      ** variable, or to the string "\fTvi\fP" if neither of those are set.
238      */
239     { "sendmail",    NULL, SENDMAIL " -oem -oi" },
240     /*
241      ** .pp
242      ** Specifies the program and arguments used to deliver mail sent by Madmutt.
243      ** Madmutt expects that the specified program interprets additional
244      ** arguments as recipient addresses.
245      */
246     { "shell",       madmutt_init_shell, NULL },
247     /*
248      ** .pp
249      ** Command to use when spawning a subshell.  By default, the user's login
250      ** shell from \fT/etc/passwd\fP is used.
251      */
252 };
253
254 /* }}} */
255
256 int luaopen_madmutt(lua_State *L)
257 {
258     int i;
259
260     lua_newuserdata(L, sizeof(void*));
261     luaL_newmetatable(L, "madmutt.core");
262
263     luaL_openlib(L, NULL, madmutt_module_funcs, 0);
264
265     for (i = 0; i < countof(madmutt_module_vars); i++) {
266         lua_pushstring(L, madmutt_module_vars[i].value);
267         lua_setfield(L, -2, madmutt_module_vars[i].key);
268     }
269
270     lua_setmetatable(L, -2);
271
272     for (i = 0; i < countof(madmutt_module_vars2); i++) {
273         if (madmutt_module_vars2[i].fun) {
274             char buf[STRING];
275             (madmutt_module_vars2[i].fun)(buf, sizeof(buf));
276             lua_pushstring(L, buf);
277         } else {
278             lua_pushstring(L, madmutt_module_vars2[i].val);
279         }
280         lua_setfield(L, -2, madmutt_module_vars2[i].key);
281     }
282
283     lua_setglobal(L, "madmutt");
284     return 1;
285 }