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