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