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