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