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