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