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