00bd8091aac80468df0332ba26a63f859cc6324d
[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 #ifdef DLL_EXPORT
10 #define IN_LIBCIT
11 #endif
12
13 #include "sysdep.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #ifdef HAVE_DLFCN_H
18 #include <dlfcn.h>
19 #endif
20 #ifdef HAVE_DL_H
21 #include <dl.h>
22 #include "hpsux.h"
23 #endif
24 #include <sys/types.h>
25 #include <dirent.h>
26 #include <string.h>
27 #include <syslog.h>
28 #include <limits.h>
29 #include <ctype.h>
30 #include "citadel.h"
31 #include "server.h"
32 #include "dynloader.h"
33 #include "sysdep_decls.h"
34 #include "msgbase.h"
35 #include "tools.h"
36 #include "config.h"
37
38 #ifndef HAVE_SNPRINTF
39 #include <stdarg.h>
40 #include "snprintf.h"
41 #endif
42
43 struct LogFunctionHook *LogHookTable = NULL;
44 struct CleanupFunctionHook *CleanupHookTable = NULL;
45 struct SessionFunctionHook *SessionHookTable = NULL;
46 struct UserFunctionHook *UserHookTable = NULL;
47 struct XmsgFunctionHook *XmsgHookTable = NULL;
48 struct MessageFunctionHook *MessageHookTable = NULL;
49 struct NetprocFunctionHook *NetprocHookTable = NULL;
50 struct DeleteFunctionHook *DeleteHookTable = NULL;
51 struct ServiceFunctionHook *ServiceHookTable = NULL;
52
53 struct ProtoFunctionHook {
54         void (*handler) (char *cmdbuf);
55         char *cmd;
56         char *desc;
57         struct ProtoFunctionHook *next;
58 } *ProtoHookList = NULL;
59
60 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
61 {
62         struct ProtoFunctionHook *p;
63
64         p = (struct ProtoFunctionHook *)
65                 mallok(sizeof(struct ProtoFunctionHook));
66
67         if (p == NULL) {
68                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
69                 exit(EXIT_FAILURE);
70         }
71         p->handler = handler;
72         p->cmd = cmd;
73         p->desc = desc;
74         p->next = ProtoHookList;
75         ProtoHookList = p;
76         lprintf(5, "Registered server command %s (%s)\n", cmd, desc);
77 }
78
79
80 void CtdlUnregisterProtoHook(void (*handler) (char *), char *cmd)
81 {
82         struct ProtoFunctionHook *cur, *p;
83
84         for (cur = ProtoHookList; cur != NULL; cur = cur->next) {
85                 /* This will also remove duplicates if any */
86                 while (cur != NULL &&
87                                 handler == cur->handler &&
88                                 !strcmp(cmd, cur->cmd)) {
89                         lprintf(5, "Unregistered server command %s (%s)\n",
90                                         cmd, cur->desc);
91                         p = cur->next;
92                         if (cur == ProtoHookList) {
93                                 ProtoHookList = p;
94                         }
95                         phree(cur);
96                         cur = p;
97                 }
98         }
99 }
100
101
102 int DLoader_Exec_Cmd(char *cmdbuf)
103 {
104         struct ProtoFunctionHook *p;
105
106         for (p = ProtoHookList; p; p = p->next) {
107                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
108                         p->handler(&cmdbuf[5]);
109                         return 1;
110                 }
111         }
112         return 0;
113 }
114
115 void DLoader_Init(char *pathname)
116 {
117         void *fcn_handle;
118         char dl_error[SIZ];
119         DIR *dir;
120         int i;
121         struct dirent *dptr;
122         char *(*h_init_fcn) (void);
123         char *dl_info;
124
125         char pathbuf[PATH_MAX];
126
127         if ((dir = opendir(pathname)) == NULL) {
128                 perror("opendir");
129                 exit(1);
130         }
131         while ((dptr = readdir(dir)) != NULL) {
132                 if (strlen(dptr->d_name) < 4)
133                         continue;
134 #ifndef __CYGWIN__
135                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-3], ".so"))
136 #else
137                 if (strcasecmp(&dptr->d_name[strlen(dptr->d_name)-4], ".dll"))
138 #endif
139                         continue;
140
141                 snprintf(pathbuf, PATH_MAX, "%s/%s", pathname, dptr->d_name);
142                 lprintf(7, "Initializing %s...\n", pathbuf);
143
144 #ifdef RTLD_LAZY
145                 if (!(fcn_handle = dlopen(pathbuf, RTLD_LAZY)))
146 #else                           /* OpenBSD */
147                 if (!(fcn_handle = dlopen(pathbuf, DL_LAZY)))
148 #endif
149                 {
150                         safestrncpy(dl_error, dlerror(), sizeof dl_error);
151                         for (i=0; i<strlen(dl_error); ++i)
152                                 if (!isprint(dl_error[i]))
153                                         dl_error[i]='.';
154                         fprintf(stderr, "DLoader_Init dlopen failed: %s\n",
155                                 dl_error);
156                         continue;
157                 }
158                 h_init_fcn = (char * (*)(void))
159 #ifndef __OpenBSD__
160                     dlsym(fcn_handle, "Dynamic_Module_Init");
161 #else
162                     dlsym(fcn_handle, "_Dynamic_Module_Init");
163 #endif
164
165                 if (dlerror() != NULL) {
166                         fprintf(stderr, "DLoader_Init dlsym failed\n");
167                         continue;
168                 }
169                 dl_info = h_init_fcn();
170
171                 lprintf(3, "Loaded module: %s\n", dl_info);
172         }       /* While */
173 }
174
175
176
177 void CtdlRegisterLogHook(void (*fcn_ptr) (char *), int loglevel)
178 {
179
180         struct LogFunctionHook *newfcn;
181
182         newfcn = (struct LogFunctionHook *)
183             mallok(sizeof(struct LogFunctionHook));
184         newfcn->next = LogHookTable;
185         newfcn->h_function_pointer = fcn_ptr;
186         newfcn->loglevel = loglevel;
187         LogHookTable = newfcn;
188
189         lprintf(5, "Registered a new logging function\n");
190 }
191
192
193 void CtdlUnregisterLogHook(void (*fcn_ptr) (char *), int loglevel)
194 {
195         struct LogFunctionHook *cur, *p;
196
197         for (cur = LogHookTable; cur != NULL; cur = cur->next) {
198                 /* This will also remove duplicates if any */
199                 while (cur != NULL &&
200                                 fcn_ptr == cur->h_function_pointer &&
201                                 loglevel == cur->loglevel) {
202                         lprintf(5, "Unregistered logging function\n");
203                         p = cur->next;
204                         if (cur == LogHookTable) {
205                                 LogHookTable = p;
206                         }
207                         phree(cur);
208                         cur = p;
209                 }
210         }
211 }
212
213
214 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
215 {
216
217         struct CleanupFunctionHook *newfcn;
218
219         newfcn = (struct CleanupFunctionHook *)
220             mallok(sizeof(struct CleanupFunctionHook));
221         newfcn->next = CleanupHookTable;
222         newfcn->h_function_pointer = fcn_ptr;
223         CleanupHookTable = newfcn;
224
225         lprintf(5, "Registered a new cleanup function\n");
226 }
227
228
229 void CtdlUnregisterCleanupHook(void (*fcn_ptr) (void))
230 {
231         struct CleanupFunctionHook *cur, *p;
232
233         for (cur = CleanupHookTable; cur != NULL; cur = cur->next) {
234                 /* This will also remove duplicates if any */
235                 while (cur != NULL &&
236                                 fcn_ptr == cur->h_function_pointer) {
237                         lprintf(5, "Unregistered cleanup function\n");
238                         p = cur->next;
239                         if (cur == CleanupHookTable) {
240                                 CleanupHookTable = p;
241                         }
242                         phree(cur);
243                         cur = p;
244                 }
245         }
246 }
247
248
249 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
250 {
251
252         struct SessionFunctionHook *newfcn;
253
254         newfcn = (struct SessionFunctionHook *)
255             mallok(sizeof(struct SessionFunctionHook));
256         newfcn->next = SessionHookTable;
257         newfcn->h_function_pointer = fcn_ptr;
258         newfcn->eventtype = EventType;
259         SessionHookTable = newfcn;
260
261         lprintf(5, "Registered a new session function (type %d)\n",
262                 EventType);
263 }
264
265
266 void CtdlUnregisterSessionHook(void (*fcn_ptr) (void), int EventType)
267 {
268         struct SessionFunctionHook *cur, *p;
269
270         for (cur = SessionHookTable; cur != NULL; cur = cur->next) {
271                 /* This will also remove duplicates if any */
272                 while (cur != NULL &&
273                                 fcn_ptr == cur->h_function_pointer &&
274                                 EventType == cur->eventtype) {
275                         lprintf(5, "Unregistered session function (type %d)\n",
276                                         EventType);
277                         p = cur->next;
278                         if (cur == SessionHookTable) {
279                                 SessionHookTable = p;
280                         }
281                         phree(cur);
282                         cur = p;
283                 }
284         }
285 }
286
287
288 void CtdlRegisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
289 {
290
291         struct UserFunctionHook *newfcn;
292
293         newfcn = (struct UserFunctionHook *)
294             mallok(sizeof(struct UserFunctionHook));
295         newfcn->next = UserHookTable;
296         newfcn->h_function_pointer = fcn_ptr;
297         newfcn->eventtype = EventType;
298         UserHookTable = newfcn;
299
300         lprintf(5, "Registered a new user function (type %d)\n",
301                 EventType);
302 }
303
304
305 void CtdlUnregisterUserHook(void (*fcn_ptr) (char *, long), int EventType)
306 {
307         struct UserFunctionHook *cur, *p;
308
309         for (cur = UserHookTable; cur != NULL; cur = cur->next) {
310                 /* This will also remove duplicates if any */
311                 while (cur != NULL &&
312                                 fcn_ptr == cur->h_function_pointer &&
313                                 EventType == cur->eventtype) {
314                         lprintf(5, "Unregistered user function (type %d)\n",
315                                         EventType);
316                         p = cur->next;
317                         if (cur == UserHookTable) {
318                                 UserHookTable = p;
319                         }
320                         phree(cur);
321                         cur = p;
322                 }
323         }
324 }
325
326
327 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
328                                 int EventType)
329 {
330
331         struct MessageFunctionHook *newfcn;
332
333         newfcn = (struct MessageFunctionHook *)
334             mallok(sizeof(struct MessageFunctionHook));
335         newfcn->next = MessageHookTable;
336         newfcn->h_function_pointer = handler;
337         newfcn->eventtype = EventType;
338         MessageHookTable = newfcn;
339
340         lprintf(5, "Registered a new message function (type %d)\n",
341                 EventType);
342 }
343
344
345 void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *),
346                 int EventType)
347 {
348         struct MessageFunctionHook *cur, *p;
349
350         for (cur = MessageHookTable; cur != NULL; cur = cur->next) {
351                 /* This will also remove duplicates if any */
352                 while (cur != NULL &&
353                                 handler == cur->h_function_pointer &&
354                                 EventType == cur->eventtype) {
355                         lprintf(5, "Unregistered message function (type %d)\n",
356                                         EventType);
357                         p = cur->next;
358                         if (cur == MessageHookTable) {
359                                 MessageHookTable = p;
360                         }
361                         phree(cur);
362                         cur = p;
363                 }
364         }
365 }
366
367
368 void CtdlRegisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
369 {
370         struct NetprocFunctionHook *newfcn;
371
372         newfcn = (struct NetprocFunctionHook *)
373             mallok(sizeof(struct NetprocFunctionHook));
374         newfcn->next = NetprocHookTable;
375         newfcn->h_function_pointer = handler;
376         NetprocHookTable = newfcn;
377
378         lprintf(5, "Registered a new netproc function\n");
379 }
380
381
382 void CtdlUnregisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
383 {
384         struct NetprocFunctionHook *cur, *p;
385
386         for (cur = NetprocHookTable; cur != NULL; cur = cur->next) {
387                 /* This will also remove duplicates if any */
388                 while (cur != NULL &&
389                                 handler == cur->h_function_pointer ) {
390                         lprintf(5, "Unregistered netproc function\n");
391                         p = cur->next;
392                         if (cur == NetprocHookTable) {
393                                 NetprocHookTable = p;
394                         }
395                         phree(cur);
396                         cur = p;
397                 }
398         }
399 }
400
401
402 void CtdlRegisterDeleteHook(void (*handler)(char *, long) )
403 {
404         struct DeleteFunctionHook *newfcn;
405
406         newfcn = (struct DeleteFunctionHook *)
407             mallok(sizeof(struct DeleteFunctionHook));
408         newfcn->next = DeleteHookTable;
409         newfcn->h_function_pointer = handler;
410         DeleteHookTable = newfcn;
411
412         lprintf(5, "Registered a new netproc function\n");
413 }
414
415
416 void CtdlUnregisterDeleteHook(void (*handler)(char *, long) )
417 {
418         struct DeleteFunctionHook *cur, *p;
419
420         for (cur = DeleteHookTable; cur != NULL; cur = cur->next) {
421                 /* This will also remove duplicates if any */
422                 while (cur != NULL &&
423                                 handler == cur->h_function_pointer ) {
424                         lprintf(5, "Unregistered netproc function\n");
425                         p = cur->next;
426                         if (cur == DeleteHookTable) {
427                                 DeleteHookTable = p;
428                         }
429                         phree(cur);
430                         cur = p;
431                 }
432         }
433 }
434
435
436 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
437 {
438
439         struct XmsgFunctionHook *newfcn;
440
441         newfcn = (struct XmsgFunctionHook *)
442             mallok(sizeof(struct XmsgFunctionHook));
443         newfcn->next = XmsgHookTable;
444         newfcn->order = order;
445         newfcn->h_function_pointer = fcn_ptr;
446         XmsgHookTable = newfcn;
447         lprintf(5, "Registered a new x-msg function (priority %d)\n", order);
448 }
449
450
451 void CtdlUnregisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
452 {
453         struct XmsgFunctionHook *cur, *p;
454
455         for (cur = XmsgHookTable; cur != NULL; cur = cur->next) {
456                 /* This will also remove duplicates if any */
457                 while (cur != NULL &&
458                                 fcn_ptr == cur->h_function_pointer &&
459                                 order == cur->order) {
460                         lprintf(5, "Unregistered x-msg function "
461                                         "(priority %d)\n", order);
462                         p = cur->next;
463                         if (cur == XmsgHookTable) {
464                                 XmsgHookTable = p;
465                         }
466                         phree(cur);
467                         cur = p;
468                 }
469         }
470 }
471
472
473 void CtdlRegisterServiceHook(int tcp_port,
474                         char *sockpath,
475                         void (*h_greeting_function) (void),
476                         void (*h_command_function) (void) )
477 {
478         struct ServiceFunctionHook *newfcn;
479         char message[SIZ];
480
481         newfcn = (struct ServiceFunctionHook *)
482             mallok(sizeof(struct ServiceFunctionHook));
483         newfcn->next = ServiceHookTable;
484         newfcn->tcp_port = tcp_port;
485         newfcn->sockpath = sockpath;
486         newfcn->h_greeting_function = h_greeting_function;
487         newfcn->h_command_function = h_command_function;
488
489         if (sockpath != NULL) {
490                 newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions);
491                 sprintf(message, "Unix domain socket '%s': ", sockpath);
492         }
493         else if (tcp_port <= 0) {       /* port -1 to disable */
494                 lprintf(7, "Service has been manually disabled, skipping\n");
495                 phree(newfcn);
496                 return;
497         }
498         else {
499                 newfcn->msock = ig_tcp_server(tcp_port, config.c_maxsessions);
500                 sprintf(message, "TCP port %d: ", tcp_port);
501         }
502
503         if (newfcn->msock > 0) {
504                 ServiceHookTable = newfcn;
505                 strcat(message, "registered.");
506                 lprintf(5, "%s\n", message);
507         }
508         else {
509                 strcat(message, "FAILED.");
510                 lprintf(2, "%s\n", message);
511                 phree(newfcn);
512         }
513 }
514
515
516 void CtdlUnregisterServiceHook(int tcp_port, char *sockpath,
517                         void (*h_greeting_function) (void),
518                         void (*h_command_function) (void) )
519 {
520         struct ServiceFunctionHook *cur, *p;
521
522         for (cur = ServiceHookTable; cur != NULL; cur = cur->next) {
523                 /* This will also remove duplicates if any */
524                 while (cur != NULL &&
525                                 !(sockpath && cur->sockpath &&
526                                         strcmp(sockpath, cur->sockpath)) &&
527                                 h_greeting_function == cur->h_greeting_function &&
528                                 h_command_function == cur->h_command_function &&
529                                 tcp_port == cur->tcp_port) {
530                         close(cur->msock);
531                         if (sockpath) {
532                                 lprintf(5, "Closed UNIX domain socket %s\n",
533                                                 sockpath);
534                         } else if (tcp_port) {
535                                 lprintf(5, "Closed TCP port %d\n", tcp_port);
536                         } else {
537                                 lprintf(5, "Unregistered unknown service\n");
538                         }
539                         p = cur->next;
540                         if (cur == ServiceHookTable) {
541                                 ServiceHookTable = p;
542                         }
543                         phree(cur);
544                         cur = p;
545                 }
546         }
547 }
548
549
550 void PerformSessionHooks(int EventType)
551 {
552         struct SessionFunctionHook *fcn;
553
554         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
555                 if (fcn->eventtype == EventType) {
556                         (*fcn->h_function_pointer) ();
557                 }
558         }
559 }
560
561 void PerformLogHooks(int loglevel, char *logmsg)
562 {
563         struct LogFunctionHook *fcn;
564
565         for (fcn = LogHookTable; fcn != NULL; fcn = fcn->next) {
566                 if (fcn->loglevel >= loglevel) {
567                         (*fcn->h_function_pointer) (logmsg);
568                 }
569         }
570 }
571
572 void PerformUserHooks(char *username, long usernum, int EventType)
573 {
574         struct UserFunctionHook *fcn;
575
576         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
577                 if (fcn->eventtype == EventType) {
578                         (*fcn->h_function_pointer) (username, usernum);
579                 }
580         }
581 }
582
583 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
584 {
585         struct MessageFunctionHook *fcn;
586         int total_retval = 0;
587
588         /* Other code may elect to protect this message from server-side
589          * handlers; if this is the case, don't do anything.
590         lprintf(9, "** Event type is %d, flags are %d\n",
591                 EventType, msg->cm_flags);
592          */
593         if (msg->cm_flags & CM_SKIP_HOOKS) {
594                 lprintf(9, "Skipping hooks\n");
595                 return(0);
596         }
597
598         /* Otherwise, run all the hooks appropriate to this event type.
599          */
600         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
601                 if (fcn->eventtype == EventType) {
602                         total_retval = total_retval +
603                                 (*fcn->h_function_pointer) (msg);
604                 }
605         }
606
607         /* Return the sum of the return codes from the hook functions.  If
608          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
609          * the save operation to abort.
610          */
611         return total_retval;
612 }
613
614
615
616 int PerformNetprocHooks(struct CtdlMessage *msg, char *target_room)
617 {
618         struct NetprocFunctionHook *fcn;
619         int total_retval = 0;
620
621         for (fcn = NetprocHookTable; fcn != NULL; fcn = fcn->next) {
622                 total_retval = total_retval +
623                         (*fcn->h_function_pointer) (msg, target_room);
624         }
625
626         /* Return the sum of the return codes from the hook functions.
627          * A nonzero return code will cause the message to *not* be imported.
628          */
629         return total_retval;
630 }
631
632
633 void PerformDeleteHooks(char *room, long msgnum)
634 {
635         struct DeleteFunctionHook *fcn;
636
637         for (fcn = DeleteHookTable; fcn != NULL; fcn = fcn->next) {
638                 (*fcn->h_function_pointer) (room, msgnum);
639         }
640 }
641
642
643
644 int PerformXmsgHooks(char *sender, char *recp, char *msg)
645 {
646         struct XmsgFunctionHook *fcn;
647         int total_sent = 0;
648         int p;
649
650         for (p=0; p<MAX_XMSG_PRI; ++p) {
651                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
652                         if (fcn->order == p) {
653                                 total_sent +=
654                                         (*fcn->h_function_pointer)
655                                                 (sender, recp, msg);
656                         }
657                 }
658                 /* Break out of the loop if a higher-priority function
659                  * successfully delivered the message.  This prevents duplicate
660                  * deliveries to local users simultaneously signed onto
661                  * remote services.
662                  */
663                 if (total_sent) break;
664         }
665         return total_sent;
666 }