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