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