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