* Added in skeleton modules for fulltext indexer
[citadel.git] / citadel / serv_extensions.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 <unistd.h>
13 #include <sys/types.h>
14 #include <dirent.h>
15 #include <string.h>
16 #include <limits.h>
17 #include <ctype.h>
18 #include "citadel.h"
19 #include "server.h"
20 #include "serv_extensions.h"
21 #include "sysdep_decls.h"
22 #include "msgbase.h"
23 #include "tools.h"
24 #include "config.h"
25
26 #ifndef HAVE_SNPRINTF
27 #include <stdarg.h>
28 #include "snprintf.h"
29 #endif
30
31 struct CleanupFunctionHook *CleanupHookTable = NULL;
32 struct SessionFunctionHook *SessionHookTable = NULL;
33 struct UserFunctionHook *UserHookTable = NULL;
34 struct XmsgFunctionHook *XmsgHookTable = NULL;
35 struct MessageFunctionHook *MessageHookTable = NULL;
36 struct NetprocFunctionHook *NetprocHookTable = NULL;
37 struct DeleteFunctionHook *DeleteHookTable = 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;
50
51         p = (struct ProtoFunctionHook *)
52                 malloc(sizeof(struct ProtoFunctionHook));
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(CTDL_INFO, "Registered server command %s (%s)\n", cmd, desc);
64 }
65
66
67 void CtdlUnregisterProtoHook(void (*handler) (char *), char *cmd)
68 {
69         struct ProtoFunctionHook *cur, *p;
70
71         for (cur = ProtoHookList; cur != NULL; cur = cur->next) {
72                 /* This will also remove duplicates if any */
73                 while (cur != NULL &&
74                                 handler == cur->handler &&
75                                 !strcmp(cmd, cur->cmd)) {
76                         lprintf(CTDL_INFO, "Unregistered server command %s (%s)\n",
77                                         cmd, cur->desc);
78                         p = cur->next;
79                         if (cur == ProtoHookList) {
80                                 ProtoHookList = p;
81                         }
82                         free(cur);
83                         cur = p;
84                 }
85         }
86 }
87
88
89 int DLoader_Exec_Cmd(char *cmdbuf)
90 {
91         struct ProtoFunctionHook *p;
92
93         for (p = ProtoHookList; p; p = p->next) {
94                 if (!strncasecmp(cmdbuf, p->cmd, 4)) {
95                         p->handler(&cmdbuf[5]);
96                         return 1;
97                 }
98         }
99         return 0;
100 }
101
102 void initialize_server_extensions(void)
103 {
104         serv_bio_init();
105         serv_calendar_init();
106         serv_notes_init();
107         serv_ldap_init();
108         serv_chat_init();
109         serv_expire_init();
110         serv_imap_init();
111         serv_inetcfg_init();
112         serv_listsub_init();
113         serv_mrtg_init();
114         serv_netfilter_init();
115         serv_network_init();
116         serv_newuser_init();
117         serv_pas2_init();
118         serv_pop3_init();
119         serv_rwho_init();
120         serv_smtp_init();
121         serv_spam_init();
122         /* serv_test_init(); */
123         serv_upgrade_init();
124         serv_vandelay_init();
125         serv_vcard_init();
126         serv_fulltext_init();
127 }
128
129
130
131 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
132 {
133
134         struct CleanupFunctionHook *newfcn;
135
136         newfcn = (struct CleanupFunctionHook *)
137             malloc(sizeof(struct CleanupFunctionHook));
138         newfcn->next = CleanupHookTable;
139         newfcn->h_function_pointer = fcn_ptr;
140         CleanupHookTable = newfcn;
141
142         lprintf(CTDL_INFO, "Registered a new cleanup function\n");
143 }
144
145
146 void CtdlUnregisterCleanupHook(void (*fcn_ptr) (void))
147 {
148         struct CleanupFunctionHook *cur, *p;
149
150         for (cur = CleanupHookTable; cur != NULL; cur = cur->next) {
151                 /* This will also remove duplicates if any */
152                 while (cur != NULL &&
153                                 fcn_ptr == cur->h_function_pointer) {
154                         lprintf(CTDL_INFO, "Unregistered cleanup function\n");
155                         p = cur->next;
156                         if (cur == CleanupHookTable) {
157                                 CleanupHookTable = p;
158                         }
159                         free(cur);
160                         cur = p;
161                 }
162         }
163 }
164
165
166 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
167 {
168
169         struct SessionFunctionHook *newfcn;
170
171         newfcn = (struct SessionFunctionHook *)
172             malloc(sizeof(struct SessionFunctionHook));
173         newfcn->next = SessionHookTable;
174         newfcn->h_function_pointer = fcn_ptr;
175         newfcn->eventtype = EventType;
176         SessionHookTable = newfcn;
177
178         lprintf(CTDL_INFO, "Registered a new session function (type %d)\n",
179                 EventType);
180 }
181
182
183 void CtdlUnregisterSessionHook(void (*fcn_ptr) (void), int EventType)
184 {
185         struct SessionFunctionHook *cur, *p;
186
187         for (cur = SessionHookTable; cur != NULL; cur = cur->next) {
188                 /* This will also remove duplicates if any */
189                 while (cur != NULL &&
190                                 fcn_ptr == cur->h_function_pointer &&
191                                 EventType == cur->eventtype) {
192                         lprintf(CTDL_INFO, "Unregistered session function (type %d)\n",
193                                         EventType);
194                         p = cur->next;
195                         if (cur == SessionHookTable) {
196                                 SessionHookTable = p;
197                         }
198                         free(cur);
199                         cur = p;
200                 }
201         }
202 }
203
204
205 void CtdlRegisterUserHook(void (*fcn_ptr) (struct ctdluser *), int EventType)
206 {
207
208         struct UserFunctionHook *newfcn;
209
210         newfcn = (struct UserFunctionHook *)
211             malloc(sizeof(struct UserFunctionHook));
212         newfcn->next = UserHookTable;
213         newfcn->h_function_pointer = fcn_ptr;
214         newfcn->eventtype = EventType;
215         UserHookTable = newfcn;
216
217         lprintf(CTDL_INFO, "Registered a new user function (type %d)\n",
218                 EventType);
219 }
220
221
222 void CtdlUnregisterUserHook(void (*fcn_ptr) (struct ctdluser *), int EventType)
223 {
224         struct UserFunctionHook *cur, *p;
225
226         for (cur = UserHookTable; cur != NULL; cur = cur->next) {
227                 /* This will also remove duplicates if any */
228                 while (cur != NULL &&
229                                 fcn_ptr == cur->h_function_pointer &&
230                                 EventType == cur->eventtype) {
231                         lprintf(CTDL_INFO, "Unregistered user function (type %d)\n",
232                                         EventType);
233                         p = cur->next;
234                         if (cur == UserHookTable) {
235                                 UserHookTable = p;
236                         }
237                         free(cur);
238                         cur = p;
239                 }
240         }
241 }
242
243
244 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
245                                 int EventType)
246 {
247
248         struct MessageFunctionHook *newfcn;
249
250         newfcn = (struct MessageFunctionHook *)
251             malloc(sizeof(struct MessageFunctionHook));
252         newfcn->next = MessageHookTable;
253         newfcn->h_function_pointer = handler;
254         newfcn->eventtype = EventType;
255         MessageHookTable = newfcn;
256
257         lprintf(CTDL_INFO, "Registered a new message function (type %d)\n",
258                 EventType);
259 }
260
261
262 void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *),
263                 int EventType)
264 {
265         struct MessageFunctionHook *cur, *p;
266
267         for (cur = MessageHookTable; cur != NULL; cur = cur->next) {
268                 /* This will also remove duplicates if any */
269                 while (cur != NULL &&
270                                 handler == cur->h_function_pointer &&
271                                 EventType == cur->eventtype) {
272                         lprintf(CTDL_INFO, "Unregistered message function (type %d)\n",
273                                         EventType);
274                         p = cur->next;
275                         if (cur == MessageHookTable) {
276                                 MessageHookTable = p;
277                         }
278                         free(cur);
279                         cur = p;
280                 }
281         }
282 }
283
284
285 void CtdlRegisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
286 {
287         struct NetprocFunctionHook *newfcn;
288
289         newfcn = (struct NetprocFunctionHook *)
290             malloc(sizeof(struct NetprocFunctionHook));
291         newfcn->next = NetprocHookTable;
292         newfcn->h_function_pointer = handler;
293         NetprocHookTable = newfcn;
294
295         lprintf(CTDL_INFO, "Registered a new netproc function\n");
296 }
297
298
299 void CtdlUnregisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
300 {
301         struct NetprocFunctionHook *cur, *p;
302
303         for (cur = NetprocHookTable; cur != NULL; cur = cur->next) {
304                 /* This will also remove duplicates if any */
305                 while (cur != NULL &&
306                                 handler == cur->h_function_pointer ) {
307                         lprintf(CTDL_INFO, "Unregistered netproc function\n");
308                         p = cur->next;
309                         if (cur == NetprocHookTable) {
310                                 NetprocHookTable = p;
311                         }
312                         free(cur);
313                         cur = p;
314                 }
315         }
316 }
317
318
319 void CtdlRegisterDeleteHook(void (*handler)(char *, long) )
320 {
321         struct DeleteFunctionHook *newfcn;
322
323         newfcn = (struct DeleteFunctionHook *)
324             malloc(sizeof(struct DeleteFunctionHook));
325         newfcn->next = DeleteHookTable;
326         newfcn->h_function_pointer = handler;
327         DeleteHookTable = newfcn;
328
329         lprintf(CTDL_INFO, "Registered a new netproc function\n");
330 }
331
332
333 void CtdlUnregisterDeleteHook(void (*handler)(char *, long) )
334 {
335         struct DeleteFunctionHook *cur, *p;
336
337         for (cur = DeleteHookTable; cur != NULL; cur = cur->next) {
338                 /* This will also remove duplicates if any */
339                 while (cur != NULL &&
340                                 handler == cur->h_function_pointer ) {
341                         lprintf(CTDL_INFO, "Unregistered netproc function\n");
342                         p = cur->next;
343                         if (cur == DeleteHookTable) {
344                                 DeleteHookTable = p;
345                         }
346                         free(cur);
347                         cur = p;
348                 }
349         }
350 }
351
352
353 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
354 {
355
356         struct XmsgFunctionHook *newfcn;
357
358         newfcn = (struct XmsgFunctionHook *)
359             malloc(sizeof(struct XmsgFunctionHook));
360         newfcn->next = XmsgHookTable;
361         newfcn->order = order;
362         newfcn->h_function_pointer = fcn_ptr;
363         XmsgHookTable = newfcn;
364         lprintf(CTDL_INFO, "Registered a new x-msg function (priority %d)\n", order);
365 }
366
367
368 void CtdlUnregisterXmsgHook(int (*fcn_ptr) (char *, char *, char *), int order)
369 {
370         struct XmsgFunctionHook *cur, *p;
371
372         for (cur = XmsgHookTable; cur != NULL; cur = cur->next) {
373                 /* This will also remove duplicates if any */
374                 while (cur != NULL &&
375                                 fcn_ptr == cur->h_function_pointer &&
376                                 order == cur->order) {
377                         lprintf(CTDL_INFO, "Unregistered x-msg function "
378                                         "(priority %d)\n", order);
379                         p = cur->next;
380                         if (cur == XmsgHookTable) {
381                                 XmsgHookTable = p;
382                         }
383                         free(cur);
384                         cur = p;
385                 }
386         }
387 }
388
389
390 void CtdlRegisterServiceHook(int tcp_port,
391                         char *sockpath,
392                         void (*h_greeting_function) (void),
393                         void (*h_command_function) (void),
394                         void (*h_async_function) (void)
395                         )
396 {
397         struct ServiceFunctionHook *newfcn;
398         char message[SIZ];
399
400         newfcn = (struct ServiceFunctionHook *)
401             malloc(sizeof(struct ServiceFunctionHook));
402         newfcn->next = ServiceHookTable;
403         newfcn->tcp_port = tcp_port;
404         newfcn->sockpath = sockpath;
405         newfcn->h_greeting_function = h_greeting_function;
406         newfcn->h_command_function = h_command_function;
407         newfcn->h_async_function = h_async_function;
408
409         if (sockpath != NULL) {
410                 newfcn->msock = ig_uds_server(sockpath, config.c_maxsessions);
411                 snprintf(message, sizeof message, "Unix domain socket '%s': ", sockpath);
412         }
413         else if (tcp_port <= 0) {       /* port -1 to disable */
414                 lprintf(CTDL_INFO, "Service has been manually disabled, skipping\n");
415                 free(newfcn);
416                 return;
417         }
418         else {
419                 newfcn->msock = ig_tcp_server(config.c_ip_addr,
420                                         tcp_port,
421                                         config.c_maxsessions);
422                 snprintf(message, sizeof message, "TCP port %d: ", tcp_port);
423         }
424
425         if (newfcn->msock > 0) {
426                 ServiceHookTable = newfcn;
427                 strcat(message, "registered.");
428                 lprintf(CTDL_INFO, "%s\n", message);
429         }
430         else {
431                 strcat(message, "FAILED.");
432                 lprintf(CTDL_CRIT, "%s\n", message);
433                 free(newfcn);
434         }
435 }
436
437
438 void CtdlUnregisterServiceHook(int tcp_port, char *sockpath,
439                         void (*h_greeting_function) (void),
440                         void (*h_command_function) (void),
441                         void (*h_async_function) (void)
442                         )
443 {
444         struct ServiceFunctionHook *cur, *p;
445
446         for (cur = ServiceHookTable; cur != NULL; cur = cur->next) {
447                 /* This will also remove duplicates if any */
448                 while (cur != NULL &&
449                                 !(sockpath && cur->sockpath &&
450                                         strcmp(sockpath, cur->sockpath)) &&
451                                 h_greeting_function == cur->h_greeting_function &&
452                                 h_command_function == cur->h_command_function &&
453                                 h_async_function == cur->h_async_function &&
454                                 tcp_port == cur->tcp_port) {
455                         close(cur->msock);
456                         if (sockpath) {
457                                 lprintf(CTDL_INFO, "Closed UNIX domain socket %s\n",
458                                                 sockpath);
459                         } else if (tcp_port) {
460                                 lprintf(CTDL_INFO, "Closed TCP port %d\n", tcp_port);
461                         } else {
462                                 lprintf(CTDL_INFO, "Unregistered unknown service\n");
463                         }
464                         p = cur->next;
465                         if (cur == ServiceHookTable) {
466                                 ServiceHookTable = p;
467                         }
468                         free(cur);
469                         cur = p;
470                 }
471         }
472 }
473
474
475 void PerformSessionHooks(int EventType)
476 {
477         struct SessionFunctionHook *fcn;
478
479         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
480                 if (fcn->eventtype == EventType) {
481                         (*fcn->h_function_pointer) ();
482                 }
483         }
484 }
485
486 void PerformUserHooks(struct ctdluser *usbuf, int EventType)
487 {
488         struct UserFunctionHook *fcn;
489
490         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
491                 if (fcn->eventtype == EventType) {
492                         (*fcn->h_function_pointer) (usbuf);
493                 }
494         }
495 }
496
497 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
498 {
499         struct MessageFunctionHook *fcn;
500         int total_retval = 0;
501
502         /* Other code may elect to protect this message from server-side
503          * handlers; if this is the case, don't do anything.
504         lprintf(CTDL_DEBUG, "** Event type is %d, flags are %d\n",
505                 EventType, msg->cm_flags);
506          */
507         if (msg->cm_flags & CM_SKIP_HOOKS) {
508                 lprintf(CTDL_DEBUG, "Skipping hooks\n");
509                 return(0);
510         }
511
512         /* Otherwise, run all the hooks appropriate to this event type.
513          */
514         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
515                 if (fcn->eventtype == EventType) {
516                         total_retval = total_retval +
517                                 (*fcn->h_function_pointer) (msg);
518                 }
519         }
520
521         /* Return the sum of the return codes from the hook functions.  If
522          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
523          * the save operation to abort.
524          */
525         return total_retval;
526 }
527
528
529
530 int PerformNetprocHooks(struct CtdlMessage *msg, char *target_room)
531 {
532         struct NetprocFunctionHook *fcn;
533         int total_retval = 0;
534
535         for (fcn = NetprocHookTable; fcn != NULL; fcn = fcn->next) {
536                 total_retval = total_retval +
537                         (*fcn->h_function_pointer) (msg, target_room);
538         }
539
540         /* Return the sum of the return codes from the hook functions.
541          * A nonzero return code will cause the message to *not* be imported.
542          */
543         return total_retval;
544 }
545
546
547 void PerformDeleteHooks(char *room, long msgnum)
548 {
549         struct DeleteFunctionHook *fcn;
550
551         for (fcn = DeleteHookTable; fcn != NULL; fcn = fcn->next) {
552                 (*fcn->h_function_pointer) (room, msgnum);
553         }
554 }
555
556
557
558 int PerformXmsgHooks(char *sender, char *recp, char *msg)
559 {
560         struct XmsgFunctionHook *fcn;
561         int total_sent = 0;
562         int p;
563
564         for (p=0; p<MAX_XMSG_PRI; ++p) {
565                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
566                         if (fcn->order == p) {
567                                 total_sent +=
568                                         (*fcn->h_function_pointer)
569                                                 (sender, recp, msg);
570                         }
571                 }
572                 /* Break out of the loop if a higher-priority function
573                  * successfully delivered the message.  This prevents duplicate
574                  * deliveries to local users simultaneously signed onto
575                  * remote services.
576                  */
577                 if (total_sent) break;
578         }
579         return total_sent;
580 }