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