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