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