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