1 #include <dlfcn.h>
2 #include "case.h"
3 #include "config.h"
4 #include "cookie.h"
5 #include "die.h"
6 #include "env.h"
7 #include "messages.h"
8 #include "fmt.h"
9 #include "scan.h"
10 #include "slurp.h"
11 #include "stralloc.h"
12 #include "str.h"
13 #include "strerr.h"
14 #include "subdb.h"
15 #include "auto_lib.h"
16
17 static stralloc line = {0};
18 static stralloc path = {0};
19 static struct sub_plugin *plugin = 0;
20 static struct subdbinfo info;
21
parsesubdb(const char * plugin_name)22 static void parsesubdb(const char *plugin_name)
23 /* Parses line into the components. The string in "line" should be
24 * plugin[:host[:port[:user[:pw[:db[:base_table]]]]]]. On success
25 * returns NULL. On error returns error string for temporary error. If
26 * the "plugin_name" parameter is not NULL, it is used as the plugin
27 * name, and the info line is assumed to start at host. */
28 {
29 unsigned int j;
30 const char *port;
31
32 info.db = "ezmlm";
33 info.host = info.user = info.pw = info.base_table = info.conn = 0;
34 info.port = 0;
35
36 /* [plugin:]host:port:db:table:user:pw:name */
37 port = 0;
38 if (!stralloc_append(&line,'\n')) die_nomem();
39 if (!stralloc_0(&line)) die_nomem();
40 if (line.s[j = str_chr(line.s,'\n')])
41 line.s[j] = '\0';
42 /* get connection parameters */
43 if (plugin_name != 0) {
44 info.plugin = plugin_name;
45 j = 0;
46 }
47 else {
48 info.plugin = line.s;
49 if (line.s[j = str_chr(line.s,':')])
50 line.s[j++] = '\0';
51 }
52 info.host = line.s + j;
53 if (line.s[j += str_chr(line.s+j,':')]) {
54 line.s[j++] = '\0';
55 port = line.s + j;
56 if (line.s[j += str_chr(line.s+j,':')]) {
57 line.s[j++] = '\0';
58 info.user = line.s + j;
59 if (line.s[j += str_chr(line.s+j,':')]) {
60 line.s[j++] = '\0';
61 info.pw = line.s + j;
62 if (line.s[j += str_chr(line.s+j,':')]) {
63 line.s[j++] = '\0';
64 info.db = line.s + j;
65 if (line.s[j += str_chr(line.s+j,':')]) {
66 line.s[j++] = '\0';
67 info.base_table = line.s + j;
68 }
69 }
70 }
71 }
72 }
73 if (!info.plugin || !*info.plugin)
74 strerr_die2x(111,FATAL,MSG(ERR_NO_PLUGIN));
75 if (port && *port)
76 scan_ulong(port,&info.port);
77 if (info.host && !*info.host)
78 info.host = (char *) 0;
79 if (info.user && !*info.user)
80 info.user = (char *) 0;
81 if (info.pw && !*info.pw)
82 info.pw = (char *) 0;
83 if (info.db && !*info.db)
84 info.db = (char *) 0;
85 if (!info.base_table || !*info.base_table)
86 info.base_table = "ezmlm";
87 }
88
loadsubdb(const char * filename,const char * plugin_name)89 static int loadsubdb(const char *filename, const char *plugin_name)
90 {
91 line.len = 0;
92 switch (slurp(filename,&line,128)) {
93 case -1:
94 strerr_die2sys(111,FATAL,MSG1(ERR_READ,filename));
95 case 0:
96 return 0;
97 default:
98 parsesubdb(plugin_name);
99 return 1;
100 }
101 }
102
103 /* Fix up the named subdirectory to strip off the leading base directory
104 * if it is an absolute path, or reject it if it falls outside of the
105 * base directory. */
fixsubdir(const char * subdir)106 static const char *fixsubdir(const char *subdir)
107 {
108 unsigned int dir_len;
109 if (subdir != 0) {
110 if (subdir[0] == '/') {
111 dir_len = str_len(listdir);
112 if (str_diffn(subdir,listdir,dir_len) != 0
113 || (subdir[dir_len] != '/'
114 && subdir[dir_len] != 0))
115 strerr_die2x(111,FATAL,MSG(ERR_NO_ABSOLUTE));
116 subdir += dir_len;
117 while (*subdir == '/')
118 ++subdir;
119 }
120 if (subdir[str_chr(subdir,'/')] == '/')
121 strerr_die2x(111,FATAL,MSG(ERR_NO_LEVELS));
122 if (subdir[0] == 0
123 || (subdir[0] == '.' && subdir[1] == 0))
124 subdir = 0;
125 }
126 return subdir;
127 }
128
opensub(void)129 static const char *opensub(void)
130 {
131 if (plugin)
132 return plugin->open(&info);
133 return 0;
134 }
135
checktag(unsigned long msgnum,unsigned long listno,const char * action,const char * seed,const char * hash)136 const char *checktag(unsigned long msgnum,
137 unsigned long listno,
138 const char *action,
139 const char *seed,
140 const char *hash)
141 {
142 const char *r = 0;
143 if ((r = opensub()) != 0)
144 return r;
145 r = plugin->checktag(&info,msgnum,listno,action,seed,hash);
146 if (listno && r == 0)
147 (void) logmsg(msgnum,listno,0L,3);
148 return r;
149 }
150
closesub(void)151 void closesub(void) {
152 if (plugin != 0)
153 plugin->close(&info);
154 }
155
issub(const char * subdir,const char * userhost,stralloc * recorded)156 int issub(const char *subdir,
157 const char *userhost,
158 stralloc *recorded)
159 {
160 const char *r = 0;
161 int result;
162 subdir = fixsubdir(subdir);
163 if ((r = opensub()) != 0)
164 strerr_die2x(111,FATAL,r);
165 if ((result = plugin->issub(&info,subdir,userhost,recorded)) == 0) {
166 userhost += str_rchr(userhost,'@');
167 if (*userhost)
168 result = plugin->issub(&info,subdir,userhost,recorded);
169 }
170 return result;
171 }
172
logmsg(unsigned long num,unsigned long listno,unsigned long subs,int done)173 const char *logmsg(unsigned long num,
174 unsigned long listno,
175 unsigned long subs,
176 int done)
177 {
178 const char *r = 0;
179 if (plugin == 0)
180 return 0;
181 if ((r = opensub()) != 0)
182 return r;
183 return plugin->logmsg(&info,num,listno,subs,done);
184 }
185
putsubs(const char * subdir,unsigned long hash_lo,unsigned long hash_hi,int subwrite ())186 unsigned long putsubs(const char *subdir,
187 unsigned long hash_lo,
188 unsigned long hash_hi,
189 int subwrite())
190 {
191 const char *r = 0;
192 subdir = fixsubdir(subdir);
193 if ((r = opensub()) != 0)
194 strerr_die2x(111,FATAL,r);
195 return plugin->putsubs(&info,subdir,hash_lo,hash_hi,subwrite);
196 }
197
searchlog(const char * subdir,char * search,int subwrite ())198 void searchlog(const char *subdir,
199 char *search,
200 int subwrite())
201 {
202 unsigned char *cps;
203 unsigned char ch;
204 unsigned int searchlen;
205 const char *r = 0;
206
207 subdir = fixsubdir(subdir);
208
209 if (!search) search = (char*)""; /* defensive */
210 searchlen = str_len(search);
211 case_lowerb(search,searchlen);
212 cps = (unsigned char *) search;
213 while ((ch = *(cps++))) { /* search is potentially hostile */
214 if (ch >= 'a' && ch <= 'z') continue;
215 if (ch >= '0' && ch <= '9') continue;
216 if (ch == '.' || ch == '_') continue;
217 *(cps - 1) = '_'; /* will match char specified as well */
218 }
219
220 if ((r = opensub()) != 0)
221 strerr_die2x(111,FATAL,r);
222 return plugin->searchlog(&info,subdir,search,subwrite);
223 }
224
subscribe(const char * subdir,const char * userhost,int flagadd,const char * comment,const char * event,int forcehash)225 int subscribe(const char *subdir,
226 const char *userhost,
227 int flagadd,
228 const char *comment,
229 const char *event,
230 int forcehash)
231 {
232 const char *r = 0;
233
234 subdir = fixsubdir(subdir);
235
236 if (userhost[str_chr(userhost,'\n')])
237 strerr_die2x(100,FATAL,MSG(ERR_ADDR_NL));
238
239 if ((r = opensub()) != 0)
240 strerr_die2x(111,FATAL,r);
241 return plugin->subscribe(&info,subdir,userhost,flagadd,comment,event,forcehash);
242 }
243
tagmsg(unsigned long msgnum,const char * seed,const char * action,char * hashout,unsigned long bodysize,unsigned long chunk)244 void tagmsg(unsigned long msgnum,
245 const char *seed,
246 const char *action,
247 char *hashout,
248 unsigned long bodysize,
249 unsigned long chunk)
250 {
251 const char *r = 0;
252 char strnum[FMT_ULONG];
253 strnum[fmt_ulong(strnum,msgnum)] = '\0'; /* message nr ->string*/
254 cookie(hashout,key.s,key.len,strnum,seed,action);
255 if ((r = opensub()) != 0)
256 strerr_die2x(111,FATAL,r);
257 if (plugin != 0)
258 plugin->tagmsg(&info,msgnum,hashout,bodysize,chunk);
259 }
260
initsub(const char * subdbline)261 void initsub(const char *subdbline)
262 {
263 void *handle;
264
265 if (subdbline == 0) {
266 if (!loadsubdb("subdb",0))
267 if (!loadsubdb("sql","sql"))
268 parsesubdb("std");
269 }
270 else {
271 if (!stralloc_copys(&line,subdbline)) die_nomem();
272 parsesubdb(0);
273 }
274 if (!stralloc_copys(&path,auto_lib())) die_nomem();
275 if (!stralloc_cats(&path,"/sub-")) die_nomem();
276 if (!stralloc_cats(&path,info.plugin)) die_nomem();
277 if (!stralloc_cats(&path,".so")) die_nomem();
278 if (!stralloc_0(&path)) die_nomem();
279 if ((handle = dlopen(path.s, RTLD_NOW | RTLD_LOCAL)) == 0)
280 /* FIXME: should use MSG functions */
281 strerr_die5x(111,FATAL,"Could not load plugin ",path.s,": ",
282 dlerror());
283 else if ((plugin = dlsym(handle,"sub_plugin")) == 0)
284 /* FIXME: should use MSG functions */
285 strerr_die5x(111,FATAL,"Plugin ",path.s," is missing symbols: ",
286 dlerror());
287 }
288
mktab(void)289 const char *mktab(void)
290 {
291 const char *r;
292 if ((r = opensub()) != 0)
293 return r;
294 return plugin->mktab(&info);
295 }
296
rmtab(void)297 const char *rmtab(void)
298 {
299 const char *r;
300 if ((r = opensub()) != 0)
301 return r;
302 return plugin->rmtab(&info);
303 }
304