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