* The size constant "256" which shows up everywhere as a buffer size has now
[citadel.git] / citadel / dynloader.c
1 /*
2  * $Id$
3  *
4  * Citadel Dynamic Loading Module
5  * Written by Brian Costello <btx@calyx.net>
6  *
7  */
8
9 #include "sysdep.h"
10 #include <stdio.h>
11 #include <stdlib.h>
12 #ifdef HAVE_DLFCN_H
13 #include <dlfcn.h>
14 #endif
15 #ifdef HAVE_DL_H
16 #include <dl.h>
17 #include "hpsux.h"
18 #endif
19 #include <sys/types.h>
20 #include <dirent.h>
21 #include <string.h>
22 #include <syslog.h>
23 #include <limits.h>
24 #include <ctype.h>
25 #include "citadel.h"
26 #include "server.h"
27 #include "dynloader.h"
28 #include "sysdep_decls.h"
29 #include "msgbase.h"
30 #include "tools.h"
31 #include "config.h"
32
33 #ifndef HAVE_SNPRINTF
34 #include <stdarg.h>
35 #include "snprintf.h"
36 #endif
37
38 struct LogFunctionHook *LogHookTable = NULL;
39 struct CleanupFunctionHook *CleanupHookTable = NULL;
40 struct SessionFunctionHook *SessionHookTable = NULL;
41 struct UserFunctionHook *UserHookTable = NULL;
42 struct XmsgFunctionHook *XmsgHookTable = NULL;
43 struct MessageFunctionHook *MessageHookTable = NULL;
44 struct ServiceFunctionHook *ServiceHookTable = NULL;
45
46 struct ProtoFunctionHook {
47         void (*handler) (char *cmdbuf);
48         char *cmd;
49         char *desc;
50         struct ProtoFunctionHook *next;
51 } *ProtoHookList = NULL;
52
53 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
54 {
55         struct ProtoFunctionHook *p = mallok(sizeof *p);
56
57         if (p == NULL) {
58                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
59                 exit(EXIT_FAILURE);
60         }
61         p->handler = handler;
62         p->cmd = cmd;
63         p->desc = desc;
64         p->next = ProtoHookList;
65         ProtoHookList = p;
66         lprintf(5, "Registered server command %s (%s)\n", cmd, desc);
67 }
68
69 int DLoader_Exec_Cmd(char *cmdbuf)
70 {
71         struct ProtoFunctionHook *p;
72
73         for (p = ProtoHookList; p; p = p->next) {
74                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
75                         p->handler(&cmdbuf[5]);
76                         return 1;
77                 }
78         }
79         return 0;
80 }
81
82 void DLoader_Init(char *pathname)
83 {
84         void *fcn_handle;
85         char dl_error[SIZ];
86         DIR *dir;
87         int i;
88         struct dirent *dptr;
89         char *(*h_init_fcn) (void);
90         char *dl_info;
91
92         char pathbuf[PATH_MAX];
93
94         if ((dir = opendir(pathname)) == NULL) {
95                 perror("opendir");
96                 exit(1);
97         }
98         while ((dptr = readdir(dir)) != NULL) {
99                 if (strlen(dptr->d_name) < 4)
100                         continue;
101                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-3], ".so"))
102                         continue;
103
104                 snprintf(pathbuf, PATH_MAX, "%s/%s", pathname, dptr->d_name);
105                 lprintf(7, "Initializing %s...\n", pathbuf);
106
107 #ifdef RTLD_LAZY
108                 if (!(fcn_handle = dlopen(pathbuf, RTLD_LAZY)))
109 #else                           /* OpenBSD */
110                 if (!(fcn_handle = dlopen(pathbuf, DL_LAZY)))
111 #endif
112                 {
113                         safestrncpy(dl_error, dlerror(), sizeof dl_error);
114                         for (i=0; i<strlen(dl_error); ++i)
115                                 if (!isprint(dl_error[i]))
116                                         dl_error[i]='.';
117                         fprintf(stderr, "DLoader_Init dlopen failed: %s\n",
118                                 dl_error);
119                         continue;
120                 }
121                 h_init_fcn = (char * (*)(void))
122 #ifndef __OpenBSD__
123                     dlsym(fcn_handle, "Dynamic_Module_Init");
124 #else
125                     dlsym(fcn_handle, "_Dynamic_Module_Init");
126 #endif
127
128                 if (dlerror() != NULL) {
129                         fprintf(stderr, "DLoader_Init dlsym failed\n");
130                         continue;
131                 }
132                 dl_info = h_init_fcn();
133
134                 lprintf(3, "Loaded module: %s\n", dl_info);
135         }       /* While */
136 }
137
138
139
140 void CtdlRegisterLogHook(void (*fcn_ptr) (char *), int loglevel)
141 {
142
143         struct LogFunctionHook *newfcn;
144
145         newfcn = (struct LogFunctionHook *)
146             mallok(sizeof(struct LogFunctionHook));
147         newfcn->next = LogHookTable;
148         newfcn->h_function_pointer = fcn_ptr;
149         newfcn->loglevel = loglevel;
150         LogHookTable = newfcn;
151
152         lprintf(5, "Registered a new logging function\n");
153 }
154
155
156 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
157 {
158
159         struct CleanupFunctionHook *newfcn;
160
161         newfcn = (struct CleanupFunctionHook *)
162             mallok(sizeof(struct CleanupFunctionHook));
163         newfcn->next = CleanupHookTable;
164         newfcn->h_function_pointer = fcn_ptr;
165         CleanupHookTable = newfcn;
166
167         lprintf(5, "Registered a new cleanup function\n");
168 }
169
170
171 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
172 {
173
174         struct SessionFunctionHook *newfcn;
175
176         newfcn = (struct SessionFunctionHook *)
177             mallok(sizeof(struct SessionFunctionHook));
178         newfcn->next = SessionHookTable;
179         newfcn->h_function_pointer = fcn_ptr;
180         newfcn->eventtype = EventType;
181         SessionHookTable = newfcn;
182
183         lprintf(5, "Registered a new session function (type %d)\n",
184                 EventType);
185 }
186
187
188 void CtdlRegisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
189 {
190
191         struct UserFunctionHook *newfcn;
192
193         newfcn = (struct UserFunctionHook *)
194             mallok(sizeof(struct UserFunctionHook));
195         newfcn->next = UserHookTable;
196         newfcn->h_function_pointer = fcn_ptr;
197         newfcn->eventtype = EventType;
198         UserHookTable = newfcn;
199
200         lprintf(5, "Registered a new user function (type %d)\n",
201                 EventType);
202 }
203
204
205 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
206                                 int EventType)
207 {
208
209         struct MessageFunctionHook *newfcn;
210
211         newfcn = (struct MessageFunctionHook *)
212             mallok(sizeof(struct MessageFunctionHook));
213         newfcn->next = MessageHookTable;
214         newfcn->h_function_pointer = handler;
215         newfcn->eventtype = EventType;
216         MessageHookTable = newfcn;
217
218         lprintf(5, "Registered a new message function (type %d)\n",
219                 EventType);
220 }
221
222
223 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
224 {
225
226         struct XmsgFunctionHook *newfcn;
227
228         newfcn = (struct XmsgFunctionHook *)
229             mallok(sizeof(struct XmsgFunctionHook));
230         newfcn->next = XmsgHookTable;
231         newfcn->order = order;
232         newfcn->h_function_pointer = fcn_ptr;
233         XmsgHookTable = newfcn;
234         lprintf(5, "Registered a new x-msg function (priority %d)\n", order);
235 }
236
237 void CtdlRegisterServiceHook(int tcp_port,
238                         char *sockpath,
239                         void (*h_greeting_function) (void),
240                         void (*h_command_function) (void) )
241 {
242         struct ServiceFunctionHook *newfcn;
243         char message[SIZ];
244
245         newfcn = (struct ServiceFunctionHook *)
246             mallok(sizeof(struct ServiceFunctionHook));
247         newfcn->next = ServiceHookTable;
248         newfcn->tcp_port = tcp_port;
249         newfcn->sockpath = sockpath;
250         newfcn->h_greeting_function = h_greeting_function;
251         newfcn->h_command_function = h_command_function;
252
253         if (sockpath != NULL) {
254                 newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions);
255                 sprintf(message, "Unix domain socket '%s': ", sockpath);
256         }
257         else if (tcp_port <= 0) {       /* port -1 to disable */
258                 lprintf(7, "Service has been manually disabled, skipping\n");
259                 phree(newfcn);
260                 return;
261         }
262         else {
263                 newfcn->msock = ig_tcp_server(tcp_port, config.c_maxsessions);
264                 sprintf(message, "TCP port %d: ", tcp_port);
265         }
266
267         if (newfcn->msock > 0) {
268                 ServiceHookTable = newfcn;
269                 strcat(message, "registered.");
270                 lprintf(5, "%s\n", message);
271         }
272         else {
273                 strcat(message, "FAILED.");
274                 lprintf(2, "%s\n", message);
275                 phree(newfcn);
276         }
277 }
278
279
280
281 void PerformSessionHooks(int EventType)
282 {
283         struct SessionFunctionHook *fcn;
284
285         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
286                 if (fcn->eventtype == EventType) {
287                         (*fcn->h_function_pointer) ();
288                 }
289         }
290 }
291
292 void PerformLogHooks(int loglevel, char *logmsg)
293 {
294         struct LogFunctionHook *fcn;
295
296         for (fcn = LogHookTable; fcn != NULL; fcn = fcn->next) {
297                 if (fcn->loglevel >= loglevel) {
298                         (*fcn->h_function_pointer) (logmsg);
299                 }
300         }
301 }
302
303 void PerformUserHooks(char *username, long usernum, int EventType)
304 {
305         struct UserFunctionHook *fcn;
306
307         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
308                 if (fcn->eventtype == EventType) {
309                         (*fcn->h_function_pointer) (username, usernum);
310                 }
311         }
312 }
313
314 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
315 {
316         struct MessageFunctionHook *fcn;
317         int total_retval = 0;
318
319         /* Other code may elect to protect this message from server-side
320          * handlers; if this is the case, don't do anything.
321          */
322         lprintf(9, "** Event type is %d, flags are %d\n",
323                 EventType, msg->cm_flags);
324         if (msg->cm_flags & CM_SKIP_HOOKS) {
325                 lprintf(9, "Skipping hooks\n");
326                 return(0);
327         }
328
329         /* Otherwise, run all the hooks appropriate to this event type.
330          */
331         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
332                 if (fcn->eventtype == EventType) {
333                         total_retval = total_retval +
334                                 (*fcn->h_function_pointer) (msg);
335                 }
336         }
337
338         /* Return the sum of the return codes from the hook functions.  If
339          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
340          * the save operation to abort.
341          */
342         return total_retval;
343 }
344
345
346
347 int PerformXmsgHooks(char *sender, char *recp, char *msg)
348 {
349         struct XmsgFunctionHook *fcn;
350         int total_sent = 0;
351         int p;
352
353         for (p=0; p<MAX_XMSG_PRI; ++p) {
354                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
355                         if (fcn->order == p) {
356                                 total_sent +=
357                                         (*fcn->h_function_pointer)
358                                                 (sender, recp, msg);
359                         }
360                 }
361                 /* Break out of the loop if a higher-priority function
362                  * successfully delivered the message.  This prevents duplicate
363                  * deliveries to local users simultaneously signed onto
364                  * remote services.
365                  */
366                 if (total_sent) goto DONE;
367         }
368 DONE:   return total_sent;
369 }