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