Deleted some system banners that are no longer referenced
[citadel.git] / citadel / serv_extensions.c
1 /*
2  * Citadel Extension Loader
3  * Written by Brian Costello <btx@calyx.net>
4  *
5  * Copyright (c) 1987-2017 by the citadel.org team
6  *
7  * This program is open source software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License, version 3.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <sys/stat.h>
20 #include <libcitadel.h>
21 #include "sysdep_decls.h"
22 #include "modules/crypto/serv_crypto.h" /* Needed until a universal crypto startup hook is implimented for CtdlStartTLS */
23 #include "serv_extensions.h"
24 #include "ctdl_module.h"
25 #include "config.h"
26
27
28 int DebugModules = 0;
29 int EnableMarkers = 0;
30 int EnableCtlProto = 0;
31
32 /*
33  * Structure defentitions for hook tables
34  */
35
36 HashList *LogDebugEntryTable = NULL;
37
38 typedef struct LogFunctionHook LogFunctionHook;
39 struct LogFunctionHook {
40         LogFunctionHook *next;
41         int loglevel;
42         void (*h_function_pointer) (char *);
43 };
44
45 LogFunctionHook *LogHookTable = NULL;
46
47 typedef struct FixedOutputHook FixedOutputHook;
48 struct FixedOutputHook {
49         FixedOutputHook *next;
50         char content_type[64];
51         void (*h_function_pointer) (char *, int);
52 };
53 FixedOutputHook *FixedOutputTable = NULL;
54
55
56
57
58
59
60 /*
61  * SessionFunctionHook extensions are used for any type of hook for which
62  * the context in which it's being called (which is determined by the event
63  * type) will make it obvious for the hook function to know where to look for
64  * pertinent data.
65  */
66 typedef struct SessionFunctionHook SessionFunctionHook;
67 struct SessionFunctionHook {
68         SessionFunctionHook *next;
69         int Priority;
70         void (*h_function_pointer) (void);
71         int eventtype;
72 };
73 SessionFunctionHook *SessionHookTable = NULL;
74
75 /*
76  * UserFunctionHook extensions are used for any type of hook which implements
77  * an operation on a user or username (potentially) other than the one
78  * operating the current session.
79  */
80 typedef struct UserFunctionHook UserFunctionHook;
81 struct UserFunctionHook {
82         UserFunctionHook *next;
83         void (*h_function_pointer) (struct ctdluser *usbuf);
84         int eventtype;
85 };
86 UserFunctionHook *UserHookTable = NULL;
87
88 /*
89  * MessageFunctionHook extensions are used for hooks which implement handlers
90  * for various types of message operations (save, read, etc.)
91  */
92 typedef struct MessageFunctionHook MessageFunctionHook;
93 struct MessageFunctionHook {
94         MessageFunctionHook *next;
95         int (*h_function_pointer) (struct CtdlMessage *msg, recptypes *recps);
96         int eventtype;
97 };
98 MessageFunctionHook *MessageHookTable = NULL;
99
100
101 /*
102  * NetprocFunctionHook extensions are used for hooks which implement handlers
103  * for incoming network messages.
104  */
105 typedef struct NetprocFunctionHook NetprocFunctionHook;
106 struct NetprocFunctionHook {
107         NetprocFunctionHook *next;
108         int (*h_function_pointer) (struct CtdlMessage *msg, char *target_room);
109 };
110 NetprocFunctionHook *NetprocHookTable = NULL;
111
112
113 /*
114  * DeleteFunctionHook extensions are used for hooks which get called when a
115  * message is about to be deleted.
116  */
117 typedef struct DeleteFunctionHook DeleteFunctionHook;
118 struct DeleteFunctionHook {
119         DeleteFunctionHook *next;
120         void (*h_function_pointer) (char *target_room, long msgnum);
121 };
122 DeleteFunctionHook *DeleteHookTable = NULL;
123
124
125 /*
126  * ExpressMessageFunctionHook extensions are used for hooks which implement
127  * the sending of an instant message through various channels.  Any function
128  * registered should return the number of recipients to whom the message was
129  * successfully transmitted.
130  */
131 typedef struct XmsgFunctionHook XmsgFunctionHook;
132 struct XmsgFunctionHook {
133         XmsgFunctionHook *next;
134         int (*h_function_pointer) (char *, char *, char *, char *);
135         int order;
136 };
137 XmsgFunctionHook *XmsgHookTable = NULL;
138
139
140
141
142 /*
143  * RoomFunctionHook extensions are used for hooks which impliment room
144  * processing functions when new messages are added EG. SIEVE.
145  */
146 typedef struct RoomFunctionHook RoomFunctionHook;
147 struct RoomFunctionHook {
148         RoomFunctionHook *next;
149         int (*fcn_ptr) (struct ctdlroom *);
150 };
151 RoomFunctionHook *RoomHookTable = NULL;
152
153
154
155 typedef struct SearchFunctionHook SearchFunctionHook;
156 struct SearchFunctionHook {
157         SearchFunctionHook *next;
158         void (*fcn_ptr) (int *, long **, const char *);
159         char *name;
160 };
161 SearchFunctionHook *SearchFunctionHookTable = NULL;
162
163 CleanupFunctionHook *CleanupHookTable = NULL;
164 CleanupFunctionHook *EVCleanupHookTable = NULL;
165
166 ServiceFunctionHook *ServiceHookTable = NULL;
167
168 typedef struct ProtoFunctionHook ProtoFunctionHook;
169 struct ProtoFunctionHook {
170         void (*handler) (char *cmdbuf);
171         const char *cmd;
172         const char *desc;
173 };
174
175 HashList *ProtoHookList = NULL;
176
177
178 #define ERR_PORT (1 << 1)
179
180
181 static StrBuf *portlist = NULL;
182
183 static StrBuf *errormessages = NULL;
184
185
186 long   DetailErrorFlags;
187 ConstStr Empty = {HKEY("")};
188 char *ErrSubject = "Startup Problems";
189 ConstStr ErrGeneral[] = {
190         {HKEY("Citadel had trouble on starting up. ")},
191         {HKEY(" This means, citadel won't be the service provider for a specific service you configured it to.\n\n"
192               "If you don't want citadel to provide these services, turn them off in WebCit via: ")},
193         {HKEY("To make both ways actualy take place restart the citserver with \"sendcommand down\"\n\n"
194               "The errors returned by the system were:\n")},
195         {HKEY("You can recheck the above if you follow this faq item:\n"
196               "http://www.citadel.org/doku.php?id=faq:mastering_your_os:net#netstat")}
197 };
198
199 ConstStr ErrPortShort = { HKEY("We couldn't bind all ports you configured to be provided by citadel server.\n")};
200 ConstStr ErrPortWhere = { HKEY("\"Admin->System Preferences->Network\".\n\nThe failed ports and sockets are: ")};
201 ConstStr ErrPortHint  = { HKEY("If you want citadel to provide you with that functionality, "
202                                "check the output of \"netstat -lnp\" on linux Servers or \"netstat -na\" on *BSD"
203                                " and stop the program that binds these ports.\n You should eventually remove "
204                                " their initscripts in /etc/init.d so that you won't get this trouble once more.\n"
205                                " After that goto \"Administration -> Shutdown Citadel\" to make Citadel restart & retry to bind this port.\n")};
206
207
208 void LogPrintMessages(long err)
209 {
210         StrBuf *Message;
211         StrBuf *List, *DetailList;
212         ConstStr *Short, *Where, *Hint; 
213
214         
215         Message = NewStrBufPlain(NULL, 
216                                  StrLength(portlist) + StrLength(errormessages));
217         
218         DetailErrorFlags = DetailErrorFlags & ~err;
219
220         switch (err)
221         {
222         case ERR_PORT:
223                 Short = &ErrPortShort;
224                 Where = &ErrPortWhere;
225                 Hint  = &ErrPortHint;
226                 List  = portlist;
227                 DetailList = errormessages;
228                 break;
229         default:
230                 Short = &Empty;
231                 Where = &Empty;
232                 Hint  = &Empty;
233                 List  = NULL;
234                 DetailList = NULL;
235         }
236
237         StrBufAppendBufPlain(Message, CKEY(ErrGeneral[0]), 0);
238         StrBufAppendBufPlain(Message, CKEY(*Short), 0); 
239         StrBufAppendBufPlain(Message, CKEY(ErrGeneral[1]), 0);
240         StrBufAppendBufPlain(Message, CKEY(*Where), 0);
241         StrBufAppendBuf(Message, List, 0);
242         StrBufAppendBufPlain(Message, HKEY("\n\n"), 0);
243         StrBufAppendBufPlain(Message, CKEY(*Hint), 0);
244         StrBufAppendBufPlain(Message, HKEY("\n\n"), 0);
245         StrBufAppendBufPlain(Message, CKEY(ErrGeneral[2]), 0);
246         StrBufAppendBuf(Message, DetailList, 0);
247         StrBufAppendBufPlain(Message, HKEY("\n\n"), 0);
248         StrBufAppendBufPlain(Message, CKEY(ErrGeneral[3]), 0);
249
250         syslog(LOG_EMERG, "extensions: %s", ChrPtr(Message));
251         syslog(LOG_EMERG, "extensions: %s", ErrSubject);
252         quickie_message("Citadel", NULL, NULL, AIDEROOM, ChrPtr(Message), FMT_FIXED, ErrSubject);
253
254         FreeStrBuf(&Message);
255         FreeStrBuf(&List);
256         FreeStrBuf(&DetailList);
257 }
258
259
260 void AddPortError(char *Port, char *ErrorMessage)
261 {
262         long len;
263
264         DetailErrorFlags |= ERR_PORT;
265
266         len = StrLength(errormessages);
267         if (len > 0) StrBufAppendBufPlain(errormessages, HKEY("; "), 0);
268         else errormessages = NewStrBuf();
269         StrBufAppendBufPlain(errormessages, ErrorMessage, -1, 0);
270
271
272         len = StrLength(portlist);
273         if (len > 0) StrBufAppendBufPlain(portlist, HKEY(";"), 0);
274         else portlist = NewStrBuf();
275         StrBufAppendBufPlain(portlist, Port, -1, 0);
276 }
277
278
279 int DLoader_Exec_Cmd(char *cmdbuf)
280 {
281         void *vP;
282         ProtoFunctionHook *p;
283
284         if (GetHash(ProtoHookList, cmdbuf, 4, &vP) && (vP != NULL)) {
285                 p = (ProtoFunctionHook*) vP;
286                 p->handler(&cmdbuf[5]);
287                 return 1;
288         }
289         return 0;
290 }
291
292 void CtdlRegisterDebugFlagHook(const char *Name, long Len, CtdlDbgFunction F, const int *LogP)
293 {
294         LogDebugEntry *E;
295         if (LogDebugEntryTable == NULL)
296                 LogDebugEntryTable = NewHash(1, NULL);
297         E = (LogDebugEntry*) malloc(sizeof(LogDebugEntry));
298         E->F = F;
299         E->Name = Name;
300         E->Len = Len;
301         E->LogP = LogP;
302         Put(LogDebugEntryTable, Name, Len, E, NULL);
303         
304 }
305 void CtdlSetDebugLogFacilities(const char **Str, long n)
306 {
307         StrBuf *Token = NULL;
308         StrBuf *Buf = NULL;
309         const char *ch;
310         int i;
311         int DoAll = 0;
312         void *vptr;
313
314         for (i=0; i < n; i++){
315                 if ((Str[i] != NULL) && !IsEmptyStr(Str[i])) {
316                         if (strcmp(Str[i], "all") == 0) {
317                                 DoAll = 1;
318                                 continue;
319                         }
320                         Buf = NewStrBufPlain(Str[i], -1);
321                         ch = NULL;
322                         if (Token == NULL)
323                                 Token = NewStrBufPlain(NULL, StrLength(Buf));
324                         while ((ch != StrBufNOTNULL) &&
325                                StrBufExtract_NextToken(Token, Buf, &ch, ',')) {
326                                 if (GetHash(LogDebugEntryTable, SKEY(Token), &vptr) && 
327                                     (vptr != NULL))
328                                 {
329                                         LogDebugEntry *E = (LogDebugEntry*)vptr;
330                                         E->F(1);
331                                 }
332                         }
333                 }
334                 FreeStrBuf(&Buf);
335         }
336         FreeStrBuf(&Token);
337         if (DoAll) {
338                 long HKLen;
339                 const char *ch;
340                 HashPos *Pos;
341
342                 Pos = GetNewHashPos(LogDebugEntryTable, 0);
343                 while (GetNextHashPos(LogDebugEntryTable, Pos, &HKLen, &ch, &vptr)) {
344                         LogDebugEntry *E = (LogDebugEntry*)vptr;
345                         E->F(1);
346                 }
347
348                 DeleteHashPos(&Pos);
349         }
350 }
351 void CtdlDestroyDebugTable(void)
352 {
353
354         DeleteHash(&LogDebugEntryTable);
355 }
356
357 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
358 {
359         ProtoFunctionHook *p;
360
361         if (ProtoHookList == NULL)
362                 ProtoHookList = NewHash (1, FourHash);
363
364
365         p = (ProtoFunctionHook *)
366                 malloc(sizeof(ProtoFunctionHook));
367
368         if (p == NULL) {
369                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
370                 exit(EXIT_FAILURE);
371         }
372         p->handler = handler;
373         p->cmd = cmd;
374         p->desc = desc;
375
376         Put(ProtoHookList, cmd, 4, p, NULL);
377         syslog(LOG_DEBUG, "extensions: registered server command %s (%s)", cmd, desc);
378 }
379
380 void CtdlDestroyProtoHooks(void)
381 {
382
383         DeleteHash(&ProtoHookList);
384 }
385
386
387 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
388 {
389
390         CleanupFunctionHook *newfcn;
391
392         newfcn = (CleanupFunctionHook *)
393             malloc(sizeof(CleanupFunctionHook));
394         newfcn->next = CleanupHookTable;
395         newfcn->h_function_pointer = fcn_ptr;
396         CleanupHookTable = newfcn;
397
398         syslog(LOG_DEBUG, "extensions: registered a new cleanup function");
399 }
400
401
402 void CtdlUnregisterCleanupHook(void (*fcn_ptr) (void))
403 {
404         CleanupFunctionHook *cur, *p, *last;
405         last = NULL;
406         cur = CleanupHookTable;
407         while (cur != NULL)
408         {
409                 if (fcn_ptr == cur->h_function_pointer)
410                 {
411                         syslog(LOG_DEBUG, "extensions: unregistered cleanup function");
412                         p = cur->next;
413
414                         free(cur);
415                         cur = NULL;
416
417                         if (last != NULL)
418                                 last->next = p;
419                         else 
420                                 CleanupHookTable = p;
421                         cur = p;
422                 }
423                 else {
424                         last = cur;
425                         cur = cur->next;
426                 }
427         }
428 }
429
430
431 void CtdlDestroyCleanupHooks(void)
432 {
433         CleanupFunctionHook *cur, *p;
434
435         cur = CleanupHookTable;
436         while (cur != NULL)
437         {
438                 syslog(LOG_DEBUG, "extensions: destroyed cleanup function");
439                 p = cur->next;
440                 free(cur);
441                 cur = p;
442         }
443         CleanupHookTable = NULL;
444 }
445
446 void CtdlRegisterEVCleanupHook(void (*fcn_ptr) (void))
447 {
448
449         CleanupFunctionHook *newfcn;
450
451         newfcn = (CleanupFunctionHook *)
452             malloc(sizeof(CleanupFunctionHook));
453         newfcn->next = EVCleanupHookTable;
454         newfcn->h_function_pointer = fcn_ptr;
455         EVCleanupHookTable = newfcn;
456
457         syslog(LOG_DEBUG, "extensions: registered a new cleanup function");
458 }
459
460
461 void CtdlUnregisterEVCleanupHook(void (*fcn_ptr) (void))
462 {
463         CleanupFunctionHook *cur, *p, *last;
464         last = NULL;
465         cur = EVCleanupHookTable;
466         while (cur != NULL)
467         {
468                 if (fcn_ptr == cur->h_function_pointer)
469                 {
470                         syslog(LOG_DEBUG, "extensions: unregistered cleanup function");
471                         p = cur->next;
472
473                         free(cur);
474                         cur = NULL;
475
476                         if (last != NULL)
477                                 last->next = p;
478                         else 
479                                 EVCleanupHookTable = p;
480                         cur = p;
481                 }
482                 else {
483                         last = cur;
484                         cur = cur->next;
485                 }
486         }
487 }
488
489
490 void CtdlDestroyEVCleanupHooks(void)
491 {
492         CleanupFunctionHook *cur, *p;
493
494         cur = EVCleanupHookTable;
495         while (cur != NULL)
496         {
497                 syslog(LOG_DEBUG, "extensions: destroyed cleanup function");
498                 p = cur->next;
499                 cur->h_function_pointer();
500                 free(cur);
501                 cur = p;
502         }
503         EVCleanupHookTable = NULL;
504 }
505
506
507
508
509 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType, int Priority)
510 {
511         SessionFunctionHook *newfcn;
512
513         newfcn = (SessionFunctionHook *)
514             malloc(sizeof(SessionFunctionHook));
515         newfcn->Priority = Priority;
516         newfcn->h_function_pointer = fcn_ptr;
517         newfcn->eventtype = EventType;
518
519         SessionFunctionHook **pfcn;
520         pfcn = &SessionHookTable;
521         while ((*pfcn != NULL) && 
522                ((*pfcn)->Priority < newfcn->Priority) &&
523                ((*pfcn)->next != NULL))
524                 pfcn = &(*pfcn)->next;
525                 
526         newfcn->next = *pfcn;
527         *pfcn = newfcn;
528         
529         syslog(LOG_DEBUG, "extensions: registered a new session function (type %d Priority %d)", EventType, Priority);
530 }
531
532
533 void CtdlUnregisterSessionHook(void (*fcn_ptr) (void), int EventType)
534 {
535         SessionFunctionHook *cur, *p, *last;
536         last = NULL;
537         cur = SessionHookTable;
538         while  (cur != NULL) {
539                 if ((fcn_ptr == cur->h_function_pointer) &&
540                     (EventType == cur->eventtype))
541                 {
542                         syslog(LOG_DEBUG, "extensions: unregistered session function (type %d)", EventType);
543                         p = cur->next;
544
545                         free(cur);
546                         cur = NULL;
547
548                         if (last != NULL)
549                                 last->next = p;
550                         else 
551                                 SessionHookTable = p;
552                         cur = p;
553                 }
554                 else {
555                         last = cur;
556                         cur = cur->next;
557                 }
558         }
559 }
560
561 void CtdlDestroySessionHooks(void)
562 {
563         SessionFunctionHook *cur, *p;
564
565         cur = SessionHookTable;
566         while (cur != NULL)
567         {
568                 syslog(LOG_DEBUG, "extensions: destroyed session function");
569                 p = cur->next;
570                 free(cur);
571                 cur = p;
572         }
573         SessionHookTable = NULL;
574 }
575
576
577 void CtdlRegisterUserHook(void (*fcn_ptr) (ctdluser *), int EventType)
578 {
579
580         UserFunctionHook *newfcn;
581
582         newfcn = (UserFunctionHook *)
583             malloc(sizeof(UserFunctionHook));
584         newfcn->next = UserHookTable;
585         newfcn->h_function_pointer = fcn_ptr;
586         newfcn->eventtype = EventType;
587         UserHookTable = newfcn;
588
589         syslog(LOG_DEBUG, "extensions: registered a new user function (type %d)",
590                    EventType);
591 }
592
593
594 void CtdlUnregisterUserHook(void (*fcn_ptr) (struct ctdluser *), int EventType)
595 {
596         UserFunctionHook *cur, *p, *last;
597         last = NULL;
598         cur = UserHookTable;
599         while (cur != NULL) {
600                 if ((fcn_ptr == cur->h_function_pointer) &&
601                     (EventType == cur->eventtype))
602                 {
603                         syslog(LOG_DEBUG, "extensions: unregistered user function (type %d)", EventType);
604                         p = cur->next;
605
606                         free(cur);
607                         cur = NULL;
608
609                         if (last != NULL)
610                                 last->next = p;
611                         else 
612                                 UserHookTable = p;
613                         cur = p;
614                 }
615                 else {
616                         last = cur;
617                         cur = cur->next;
618                 }
619         }
620 }
621
622 void CtdlDestroyUserHooks(void)
623 {
624         UserFunctionHook *cur, *p;
625
626         cur = UserHookTable;
627         while (cur != NULL)
628         {
629                 syslog(LOG_DEBUG, "extensions: destroyed user function");
630                 p = cur->next;
631                 free(cur);
632                 cur = p;
633         }
634         UserHookTable = NULL;
635 }
636
637
638 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *, recptypes *),
639                                 int EventType)
640 {
641
642         MessageFunctionHook *newfcn;
643
644         newfcn = (MessageFunctionHook *)
645             malloc(sizeof(MessageFunctionHook));
646         newfcn->next = MessageHookTable;
647         newfcn->h_function_pointer = handler;
648         newfcn->eventtype = EventType;
649         MessageHookTable = newfcn;
650
651         syslog(LOG_DEBUG, "extensions: registered a new message function (type %d)", EventType);
652 }
653
654
655 void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *, recptypes *),
656                 int EventType)
657 {
658         MessageFunctionHook *cur, *p, *last;
659         last = NULL;
660         cur = MessageHookTable;
661         while (cur != NULL) {
662                 if ((handler == cur->h_function_pointer) &&
663                     (EventType == cur->eventtype))
664                 {
665                         syslog(LOG_DEBUG, "extensions: unregistered message function (type %d)", EventType);
666                         p = cur->next;
667                         free(cur);
668                         cur = NULL;
669
670                         if (last != NULL)
671                                 last->next = p;
672                         else 
673                                 MessageHookTable = p;
674                         cur = p;
675                 }
676                 else {
677                         last = cur;
678                         cur = cur->next;
679                 }
680         }
681 }
682
683 void CtdlDestroyMessageHook(void)
684 {
685         MessageFunctionHook *cur, *p;
686
687         cur = MessageHookTable; 
688         while (cur != NULL)
689         {
690                 syslog(LOG_DEBUG, "extensions: destroyed message function (type %d)", cur->eventtype);
691                 p = cur->next;
692                 free(cur);
693                 cur = p;
694         }
695         MessageHookTable = NULL;
696 }
697
698
699 void CtdlRegisterRoomHook(int (*fcn_ptr)(struct ctdlroom *))
700 {
701         RoomFunctionHook *newfcn;
702
703         newfcn = (RoomFunctionHook *)
704             malloc(sizeof(RoomFunctionHook));
705         newfcn->next = RoomHookTable;
706         newfcn->fcn_ptr = fcn_ptr;
707         RoomHookTable = newfcn;
708
709         syslog(LOG_DEBUG, "extensions: registered a new room function");
710 }
711
712
713 void CtdlUnregisterRoomHook(int (*fcn_ptr)(struct ctdlroom *))
714 {
715         RoomFunctionHook *cur, *p, *last;
716         last = NULL;
717         cur = RoomHookTable;
718         while (cur != NULL)
719         {
720                 if (fcn_ptr == cur->fcn_ptr) {
721                         syslog(LOG_DEBUG, "extensions: unregistered room function");
722                         p = cur->next;
723
724                         free(cur);
725                         cur = NULL;
726
727                         if (last != NULL)
728                                 last->next = p;
729                         else 
730                                 RoomHookTable = p;
731                         cur = p;
732                 }
733                 else {
734                         last = cur;
735                         cur = cur->next;
736                 }
737         }
738 }
739
740
741 void CtdlDestroyRoomHooks(void)
742 {
743         RoomFunctionHook *cur, *p;
744
745         cur = RoomHookTable;
746         while (cur != NULL)
747         {
748                 syslog(LOG_DEBUG, "extensions: destroyed room function");
749                 p = cur->next;
750                 free(cur);
751                 cur = p;
752         }
753         RoomHookTable = NULL;
754 }
755
756 void CtdlRegisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
757 {
758         NetprocFunctionHook *newfcn;
759
760         newfcn = (NetprocFunctionHook *)
761             malloc(sizeof(NetprocFunctionHook));
762         newfcn->next = NetprocHookTable;
763         newfcn->h_function_pointer = handler;
764         NetprocHookTable = newfcn;
765
766         syslog(LOG_DEBUG, "extensions: registered a new netproc function");
767 }
768
769
770 void CtdlUnregisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
771 {
772         NetprocFunctionHook *cur, *p, *last;
773
774         cur = NetprocHookTable;
775         last = NULL;
776
777         while (cur != NULL) {
778                 if (handler == cur->h_function_pointer)
779                 {
780                         syslog(LOG_DEBUG, "extensions: unregistered netproc function");
781                         p = cur->next;
782                         free(cur);
783                         if (last != NULL) {
784                                 last->next = p;
785                         }
786                         else {
787                                 NetprocHookTable = p;
788                         }
789                         cur = p;
790                 }
791                 else {
792                         last = cur;
793                         cur = cur->next;
794                 }
795         }
796 }
797
798 void CtdlDestroyNetprocHooks(void)
799 {
800         NetprocFunctionHook *cur, *p;
801
802         cur = NetprocHookTable;
803         while (cur != NULL)
804         {
805                 syslog(LOG_DEBUG, "extensions: destroyed netproc function");
806                 p = cur->next;
807                 free(cur);
808                 cur = p;
809         }
810         NetprocHookTable = NULL;
811 }
812
813
814 void CtdlRegisterDeleteHook(void (*handler)(char *, long) )
815 {
816         DeleteFunctionHook *newfcn;
817
818         newfcn = (DeleteFunctionHook *)
819             malloc(sizeof(DeleteFunctionHook));
820         newfcn->next = DeleteHookTable;
821         newfcn->h_function_pointer = handler;
822         DeleteHookTable = newfcn;
823
824         syslog(LOG_DEBUG, "extensions: registered a new delete function");
825 }
826
827
828 void CtdlUnregisterDeleteHook(void (*handler)(char *, long) )
829 {
830         DeleteFunctionHook *cur, *p, *last;
831
832         last = NULL;
833         cur = DeleteHookTable;
834         while (cur != NULL) {
835                 if (handler == cur->h_function_pointer )
836                 {
837                         syslog(LOG_DEBUG, "extensions: unregistered delete function");
838                         p = cur->next;
839                         free(cur);
840
841                         if (last != NULL)
842                                 last->next = p;
843                         else
844                                 DeleteHookTable = p;
845
846                         cur = p;
847                 }
848                 else {
849                         last = cur;
850                         cur = cur->next;
851                 }
852         }
853 }
854
855
856 void CtdlDestroyDeleteHooks(void)
857 {
858         DeleteFunctionHook *cur, *p;
859
860         cur = DeleteHookTable;
861         while (cur != NULL)
862         {
863                 syslog(LOG_DEBUG, "extensions: destroyed delete function");
864                 p = cur->next;
865                 free(cur);
866                 cur = p;                
867         }
868         DeleteHookTable = NULL;
869 }
870
871
872 void CtdlRegisterFixedOutputHook(char *content_type, void (*handler)(char *, int) )
873 {
874         FixedOutputHook *newfcn;
875
876         newfcn = (FixedOutputHook *)
877             malloc(sizeof(FixedOutputHook));
878         newfcn->next = FixedOutputTable;
879         newfcn->h_function_pointer = handler;
880         safestrncpy(newfcn->content_type, content_type, sizeof newfcn->content_type);
881         FixedOutputTable = newfcn;
882
883         syslog(LOG_DEBUG, "extensions: registered a new fixed output function for %s", newfcn->content_type);
884 }
885
886
887 void CtdlUnregisterFixedOutputHook(char *content_type)
888 {
889         FixedOutputHook *cur, *p, *last;
890
891         last = NULL;
892         cur = FixedOutputTable;
893         while (cur != NULL) {
894                 /* This will also remove duplicates if any */
895                 if (!strcasecmp(content_type, cur->content_type)) {
896                         syslog(LOG_DEBUG, "extensions: unregistered fixed output function for %s", content_type);
897                         p = cur->next;
898                         free(cur);
899
900                         if (last != NULL)
901                                 last->next = p;
902                         else
903                                 FixedOutputTable = p;
904                         
905                         cur = p;
906                 }
907                 else
908                 {
909                         last = cur;
910                         cur = cur->next;
911                 }
912         }
913 }
914
915 void CtdlDestroyFixedOutputHooks(void)
916 {
917         FixedOutputHook *cur, *p;
918
919         cur = FixedOutputTable; 
920         while (cur != NULL)
921         {
922                 syslog(LOG_DEBUG, "extensions: destroyed fixed output function for %s", cur->content_type);
923                 p = cur->next;
924                 free(cur);
925                 cur = p;
926                 
927         }
928         FixedOutputTable = NULL;
929 }
930
931 /* returns nonzero if we found a hook and used it */
932 int PerformFixedOutputHooks(char *content_type, char *content, int content_length)
933 {
934         FixedOutputHook *fcn;
935
936         for (fcn = FixedOutputTable; fcn != NULL; fcn = fcn->next) {
937                 if (!strcasecmp(content_type, fcn->content_type)) {
938                         (*fcn->h_function_pointer) (content, content_length);
939                         return(1);
940                 }
941         }
942         return(0);
943 }
944
945
946 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *, char *), int order)
947 {
948
949         XmsgFunctionHook *newfcn;
950
951         newfcn = (XmsgFunctionHook *) malloc(sizeof(XmsgFunctionHook));
952         newfcn->next = XmsgHookTable;
953         newfcn->order = order;
954         newfcn->h_function_pointer = fcn_ptr;
955         XmsgHookTable = newfcn;
956         syslog(LOG_DEBUG, "extensions: registered a new x-msg function (priority %d)", order);
957 }
958
959
960 void CtdlUnregisterXmsgHook(int (*fcn_ptr) (char *, char *, char *, char *), int order)
961 {
962         XmsgFunctionHook *cur, *p, *last;
963
964         last = NULL;
965         cur = XmsgHookTable;
966         while (cur != NULL) {
967                 /* This will also remove duplicates if any */
968                 if (fcn_ptr == cur->h_function_pointer &&
969                     order == cur->order) {
970                         syslog(LOG_DEBUG, "extensions: unregistered x-msg function (priority %d)", order);
971                         p = cur->next;
972                         free(cur);
973
974                         if (last != NULL) {
975                                 last->next = p;
976                         }
977                         else {
978                                 XmsgHookTable = p;
979                         }
980                         cur = p;
981                 }
982                 else {
983                         last = cur;
984                         cur = cur->next;
985                 }
986         }
987 }
988
989
990 void CtdlDestroyXmsgHooks(void)
991 {
992         XmsgFunctionHook *cur, *p;
993
994         cur = XmsgHookTable;
995         while (cur != NULL)
996         {
997                 syslog(LOG_DEBUG, "extensions: destroyed x-msg function (priority %d)", cur->order);
998                 p = cur->next;
999                 free(cur);
1000                 cur = p;
1001         }
1002         XmsgHookTable = NULL;
1003 }
1004
1005
1006 void CtdlRegisterServiceHook(int tcp_port,
1007                              char *sockpath,
1008                              void (*h_greeting_function) (void),
1009                              void (*h_command_function) (void),
1010                              void (*h_async_function) (void),
1011                              const char *ServiceName)
1012 {
1013         ServiceFunctionHook *newfcn;
1014         char *message;
1015         char error[SIZ];
1016
1017         strcpy(error, "");
1018         newfcn = (ServiceFunctionHook *) malloc(sizeof(ServiceFunctionHook));
1019         message = (char*) malloc (SIZ + SIZ);
1020         
1021         newfcn->next = ServiceHookTable;
1022         newfcn->tcp_port = tcp_port;
1023         newfcn->sockpath = sockpath;
1024         newfcn->h_greeting_function = h_greeting_function;
1025         newfcn->h_command_function = h_command_function;
1026         newfcn->h_async_function = h_async_function;
1027         newfcn->ServiceName = ServiceName;
1028
1029         if (sockpath != NULL) {
1030                 newfcn->msock = ctdl_uds_server(sockpath, CtdlGetConfigInt("c_maxsessions"), error);
1031                 snprintf(message, SIZ, "extensions: unix domain socket '%s': ", sockpath);
1032         }
1033         else if (tcp_port <= 0) {       /* port -1 to disable */
1034                 syslog(LOG_INFO, "extensions: service %s has been manually disabled, skipping", ServiceName);
1035                 free (message);
1036                 free(newfcn);
1037                 return;
1038         }
1039         else {
1040                 newfcn->msock = ctdl_tcp_server(CtdlGetConfigStr("c_ip_addr"),
1041                                               tcp_port,
1042                                               CtdlGetConfigInt("c_maxsessions"), 
1043                                               error);
1044                 snprintf(message, SIZ, "extensions: TCP port %s:%d: (%s) ", 
1045                          CtdlGetConfigStr("c_ip_addr"), tcp_port, ServiceName);
1046         }
1047
1048         if (newfcn->msock > 0) {
1049                 ServiceHookTable = newfcn;
1050                 strcat(message, "registered.");
1051                 syslog(LOG_INFO, "%s", message);
1052         }
1053         else {
1054                 AddPortError(message, error);
1055                 strcat(message, "FAILED.");
1056                 syslog(LOG_ERR, "%s", message);
1057                 free(newfcn);
1058         }
1059         free(message);
1060 }
1061
1062
1063 void CtdlUnregisterServiceHook(int tcp_port, char *sockpath,
1064                         void (*h_greeting_function) (void),
1065                         void (*h_command_function) (void),
1066                         void (*h_async_function) (void)
1067                         )
1068 {
1069         ServiceFunctionHook *cur, *p, *last;
1070
1071         last = NULL;
1072         cur = ServiceHookTable;
1073         while (cur != NULL) {
1074                 /* This will also remove duplicates if any */
1075                 if (h_greeting_function == cur->h_greeting_function &&
1076                     h_command_function == cur->h_command_function &&
1077                     h_async_function == cur->h_async_function &&
1078                     tcp_port == cur->tcp_port && 
1079                     !(sockpath && cur->sockpath && strcmp(sockpath, cur->sockpath)) )
1080                 {
1081                         if (cur->msock > 0)
1082                                 close(cur->msock);
1083                         if (sockpath) {
1084                                 syslog(LOG_INFO, "extensions: closed UNIX domain socket %s", sockpath);
1085                                 unlink(sockpath);
1086                         } else if (tcp_port) {
1087                                 syslog(LOG_INFO, "extensions: closed TCP port %d", tcp_port);
1088                         } else {
1089                                 syslog(LOG_INFO, "extensions: unregistered service \"%s\"", cur->ServiceName);
1090                         }
1091                         p = cur->next;
1092                         free(cur);
1093                         if (last != NULL)
1094                                 last->next = p;
1095                         else
1096                                 ServiceHookTable = p;
1097                         cur = p;
1098                 }
1099                 else {
1100                         last = cur;
1101                         cur = cur->next;
1102                 }
1103         }
1104 }
1105
1106
1107 void CtdlShutdownServiceHooks(void)
1108 {
1109         /* sort of a duplicate of close_masters() but called earlier */
1110         ServiceFunctionHook *cur;
1111
1112         cur = ServiceHookTable;
1113         while (cur != NULL) 
1114         {
1115                 if (cur->msock != -1)
1116                 {
1117                         close(cur->msock);
1118                         cur->msock = -1;
1119                         if (cur->sockpath != NULL){
1120                                 syslog(LOG_INFO, "extensions: [%s] Closed UNIX domain socket %s", cur->ServiceName, cur->sockpath);
1121                                 unlink(cur->sockpath);
1122                         } else {
1123                                 syslog(LOG_INFO, "extensions: [%s] closing service", cur->ServiceName);
1124                         }
1125                 }
1126                 cur = cur->next;
1127         }
1128 }
1129
1130
1131 void CtdlDestroyServiceHook(void)
1132 {
1133         const char *Text;
1134         ServiceFunctionHook *cur, *p;
1135
1136         cur = ServiceHookTable;
1137         while (cur != NULL)
1138         {
1139                 if (cur->msock != -1)
1140                 {
1141                         close(cur->msock);
1142                         Text = "Closed";
1143                 }
1144                 else
1145                 {
1146                         Text = " Not closing again";
1147                 }
1148
1149                 if (cur->sockpath) {
1150                         syslog(LOG_INFO, "extensions: %s UNIX domain socket %s", Text, cur->sockpath);
1151                         unlink(cur->sockpath);
1152                 } else if (cur->tcp_port) {
1153                         syslog(LOG_INFO, "extensions: %s TCP port %d", Text, cur->tcp_port);
1154                 } else {
1155                         syslog(LOG_INFO, "extensions: destroyed service \"%s\"", cur->ServiceName);
1156                 }
1157                 p = cur->next;
1158                 free(cur);
1159                 cur = p;
1160         }
1161         ServiceHookTable = NULL;
1162 }
1163
1164 void CtdlRegisterSearchFuncHook(void (*fcn_ptr)(int *, long **, const char *), char *name)
1165 {
1166         SearchFunctionHook *newfcn;
1167
1168         if (!name || !fcn_ptr) {
1169                 return;
1170         }
1171         
1172         newfcn = (SearchFunctionHook *)
1173             malloc(sizeof(SearchFunctionHook));
1174         newfcn->next = SearchFunctionHookTable;
1175         newfcn->name = name;
1176         newfcn->fcn_ptr = fcn_ptr;
1177         SearchFunctionHookTable = newfcn;
1178
1179         syslog(LOG_DEBUG, "extensions: registered a new search function (%s)", name);
1180 }
1181
1182 void CtdlUnregisterSearchFuncHook(void (*fcn_ptr)(int *, long **, const char *), char *name)
1183 {
1184         SearchFunctionHook *cur, *p, *last;
1185         
1186         last = NULL;
1187         cur = SearchFunctionHookTable;
1188         while (cur != NULL) {
1189                 if (fcn_ptr &&
1190                     (cur->fcn_ptr == fcn_ptr) &&
1191                     name && !strcmp(name, cur->name))
1192                 {
1193                         syslog(LOG_DEBUG, "extensions: unregistered search function(%s)", name);
1194                         p = cur->next;
1195                         free (cur);
1196                         if (last != NULL)
1197                                 last->next = p;
1198                         else
1199                                 SearchFunctionHookTable = p;
1200                         cur = p;
1201                 }
1202                 else {
1203                         last = cur;
1204                         cur = cur->next;
1205                 }
1206         }
1207 }
1208
1209 void CtdlDestroySearchHooks(void)
1210 {
1211         SearchFunctionHook *cur, *p;
1212
1213         cur = SearchFunctionHookTable;
1214         SearchFunctionHookTable = NULL;
1215         while (cur != NULL) {
1216                 p = cur->next;
1217                 free(cur);
1218                 cur = p;
1219         }
1220 }
1221
1222 void CtdlModuleDoSearch(int *num_msgs, long **search_msgs, const char *search_string, const char *func_name)
1223 {
1224         SearchFunctionHook *fcn = NULL;
1225
1226         for (fcn = SearchFunctionHookTable; fcn != NULL; fcn = fcn->next) {
1227                 if (!func_name || !strcmp(func_name, fcn->name)) {
1228                         (*fcn->fcn_ptr) (num_msgs, search_msgs, search_string);
1229                         return;
1230                 }
1231         }
1232         *num_msgs = 0;
1233 }
1234
1235
1236 void PerformSessionHooks(int EventType)
1237 {
1238         SessionFunctionHook *fcn = NULL;
1239
1240         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
1241                 if (fcn->eventtype == EventType) {
1242                         if (EventType == EVT_TIMER) {
1243                                 pthread_setspecific(MyConKey, NULL);    /* for every hook */
1244                         }
1245                         (*fcn->h_function_pointer) ();
1246                 }
1247         }
1248 }
1249
1250 void PerformUserHooks(ctdluser *usbuf, int EventType)
1251 {
1252         UserFunctionHook *fcn = NULL;
1253
1254         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
1255                 if (fcn->eventtype == EventType) {
1256                         (*fcn->h_function_pointer) (usbuf);
1257                 }
1258         }
1259 }
1260
1261 int PerformMessageHooks(struct CtdlMessage *msg, recptypes *recps, int EventType)
1262 {
1263         MessageFunctionHook *fcn = NULL;
1264         int total_retval = 0;
1265
1266         /* Other code may elect to protect this message from server-side
1267          * handlers; if this is the case, don't do anything.
1268          */
1269         if (msg->cm_flags & CM_SKIP_HOOKS) {
1270                 return(0);
1271         }
1272
1273         /* Otherwise, run all the hooks appropriate to this event type.
1274          */
1275         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
1276                 if (fcn->eventtype == EventType) {
1277                         total_retval = total_retval + (*fcn->h_function_pointer) (msg, recps);
1278                 }
1279         }
1280
1281         /* Return the sum of the return codes from the hook functions.  If
1282          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
1283          * the save operation to abort.
1284          */
1285         return total_retval;
1286 }
1287
1288
1289 int PerformRoomHooks(struct ctdlroom *target_room)
1290 {
1291         RoomFunctionHook *fcn;
1292         int total_retval = 0;
1293
1294         syslog(LOG_DEBUG, "extensions: performing room hooks for <%s>", target_room->QRname);
1295
1296         for (fcn = RoomHookTable; fcn != NULL; fcn = fcn->next) {
1297                 total_retval = total_retval + (*fcn->fcn_ptr) (target_room);
1298         }
1299
1300         /* Return the sum of the return codes from the hook functions.
1301          */
1302         return total_retval;
1303 }
1304
1305
1306 int PerformNetprocHooks(struct CtdlMessage *msg, char *target_room)
1307 {
1308         NetprocFunctionHook *fcn;
1309         int total_retval = 0;
1310
1311         for (fcn = NetprocHookTable; fcn != NULL; fcn = fcn->next) {
1312                 total_retval = total_retval +
1313                         (*fcn->h_function_pointer) (msg, target_room);
1314         }
1315
1316         /* Return the sum of the return codes from the hook functions.
1317          * A nonzero return code will cause the message to *not* be imported.
1318          */
1319         return total_retval;
1320 }
1321
1322
1323 void PerformDeleteHooks(char *room, long msgnum)
1324 {
1325         DeleteFunctionHook *fcn;
1326
1327         for (fcn = DeleteHookTable; fcn != NULL; fcn = fcn->next) {
1328                 (*fcn->h_function_pointer) (room, msgnum);
1329         }
1330 }
1331
1332
1333
1334
1335
1336 int PerformXmsgHooks(char *sender, char *sender_email, char *recp, char *msg)
1337 {
1338         XmsgFunctionHook *fcn;
1339         int total_sent = 0;
1340         int p;
1341
1342         for (p=0; p<MAX_XMSG_PRI; ++p) {
1343                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
1344                         if (fcn->order == p) {
1345                                 total_sent +=
1346                                         (*fcn->h_function_pointer)
1347                                                 (sender, sender_email, recp, msg);
1348                         }
1349                 }
1350                 /* Break out of the loop if a higher-priority function
1351                  * successfully delivered the message.  This prevents duplicate
1352                  * deliveries to local users simultaneously signed onto
1353                  * remote services.
1354                  */
1355                 if (total_sent) break;
1356         }
1357         return total_sent;
1358 }
1359
1360
1361 /*
1362  * Dirty hack until we impliment a hook mechanism for this
1363  */
1364 void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response)
1365 {
1366 #ifdef HAVE_OPENSSL
1367         CtdlStartTLS (ok_response, nosup_response, error_response);
1368 #endif
1369 }
1370
1371 void DebugModulesEnable(const int n)
1372 {
1373         DebugModules = n;
1374 }
1375 void MarkersEnable(const int n)
1376 {
1377         EnableMarkers = n;
1378 }
1379 void DebugCitadelProtoEnable(const int n)
1380 {
1381         EnableCtlProto = n;
1382 }
1383
1384 CTDL_MODULE_INIT(modules)
1385 {
1386         if (!threading) {
1387                 CtdlRegisterDebugFlagHook(HKEY("modules"), DebugModulesEnable, &DebugModules);
1388                 CtdlRegisterDebugFlagHook(HKEY("periodicmarkers"), MarkersEnable, &EnableMarkers);
1389                 CtdlRegisterDebugFlagHook(HKEY("citadelprotocol"), DebugCitadelProtoEnable, &EnableCtlProto);
1390         }
1391         return "modules";
1392 }