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