Fixed a stack smash in the wiki module
[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 struct CleanupFunctionHook *CleanupHookTable = NULL;
50 struct SessionFunctionHook *SessionHookTable = NULL;
51 struct UserFunctionHook *UserHookTable = NULL;
52 struct XmsgFunctionHook *XmsgHookTable = NULL;
53 struct MessageFunctionHook *MessageHookTable = NULL;
54 struct NetprocFunctionHook *NetprocHookTable = NULL;
55 struct DeleteFunctionHook *DeleteHookTable = NULL;
56 struct ServiceFunctionHook *ServiceHookTable = NULL;
57 struct FixedOutputHook *FixedOutputTable = NULL;
58 struct RoomFunctionHook *RoomHookTable = NULL;
59 struct SearchFunctionHook *SearchFunctionHookTable = NULL;
60
61 struct ProtoFunctionHook {
62         void (*handler) (char *cmdbuf);
63         const char *cmd;
64         const char *desc;
65 };
66
67 HashList *ProtoHookList = NULL;
68
69
70 #define ERR_PORT (1 << 1)
71
72
73 static char *portlist = NULL;
74 static size_t nSizPort = 0;
75
76 static char *errormessages = NULL;
77 size_t nSizErrmsg = 0;
78
79
80 long   DetailErrorFlags;
81
82 char *ErrSubject = "Startup Problems";
83 char *ErrGeneral = "Citadel had trouble on starting up. %s This means, citadel won't be the service provider for a specific service you configured it to.\n\n"
84 "If you don't want citadel to provide these services, turn them off in WebCit via %s%s\n\n%s\n\n"
85 "To make both ways actualy take place restart the citserver with \"sendcommand down\"\n\n"
86 "The errors returned by the system were:\n%s\n"
87 "You can recheck the above if you follow this faq item:\n"
88 "http://www.citadel.org/doku.php/faq:mastering_your_os:net#netstat";
89
90
91 char *ErrPortShort = "We couldn't bind all ports you configured to be provided by citadel server.";
92 char *ErrPortWhere = "Admin->System Preferences->Network.\n\nThe failed ports and sockets are: ";
93 char *ErrPortHint = "If you want citadel to provide you with that functionality, "
94 "check the output of \"netstat -lnp\" on linux Servers or \"netstat -na\" on *BSD"
95 " and stop the programm, that binds these ports.\n You should eventually remove "
96 " their initscripts in /etc/init.d so that you won't get this trouble once more.\n"
97 " After that goto Administration -> Shutdown Citadel to make Citadel retry to bind this port.\n";
98
99
100 void LogPrintMessages(long err)
101 {
102         char *List, *DetailList, *Short, *Where, *Hint, *Message; 
103         int n = nSizPort + nSizErrmsg + 5;
104
105         Message = (char*) malloc(n * SIZ);
106         
107         DetailErrorFlags = DetailErrorFlags & ~err;
108
109         switch (err)
110         {
111         case ERR_PORT:
112                 Short = ErrPortShort;
113                 Where = ErrPortWhere;
114                 Hint  = ErrPortHint;
115                 List  = portlist;
116                 DetailList = errormessages;
117                 break;
118         default:
119                 Short = "";
120                 Where = "";
121                 Hint  = "";
122                 List  = "";
123                 DetailList = "";
124         }
125
126
127         snprintf(Message, n * SIZ, ErrGeneral, Short, Where, List, Hint, DetailList);
128
129         syslog(LOG_EMERG, "%s", Message);
130         syslog(LOG_EMERG, "%s", ErrSubject);
131         quickie_message("Citadel", NULL, NULL, AIDEROOM, Message, FMT_FIXED, ErrSubject);
132         if (errormessages!=NULL) free (errormessages);
133         errormessages = NULL;
134         if (portlist!=NULL) free (portlist);
135         portlist = NULL;
136         free(Message);
137 }
138
139
140
141 void AppendString(char **target, char *append, size_t *len, size_t rate)
142 {
143         size_t oLen = 0;
144         long AddLen;
145         long RelPtr = 0;
146
147         AddLen = strlen(append);
148
149         if (*len == 0)
150         {
151                 *len = rate;
152
153                 *target = (char*)malloc (*len * SIZ);
154         }
155         else 
156         {
157                 oLen = strlen(*target);
158                 RelPtr = strlen(*target);
159                 if (oLen + AddLen + 2 > *len * SIZ)
160                 {
161                         char *Buff = *target;
162                         size_t NewSiz = *len + 10;
163                         *target = malloc (NewSiz * SIZ);
164                         memcpy (*target, Buff, NewSiz * SIZ);
165                         *len = NewSiz;
166                 }
167         }
168         memcpy (*target + oLen, append, AddLen);
169         (*target)[oLen + AddLen + 1] = '\n';
170         (*target)[oLen + AddLen + 2] = '\0';
171 }
172
173 void AddPortError(char *Port, char *ErrorMessage)
174 {
175         char *pos;
176         long len;
177
178         DetailErrorFlags |= ERR_PORT;
179
180         AppendString(&errormessages, ErrorMessage, &nSizErrmsg, 10);
181         AppendString(&portlist, Port, &nSizPort, 2);
182
183         pos = strchr (portlist, ':');
184         if (pos != NULL) *pos = ';';
185         
186         len = strlen (errormessages);
187         if (nSizErrmsg * SIZ > len + 3)
188         {
189                 errormessages[len] = ';';
190                 errormessages[len+1] = ' ';
191                 errormessages[len+2] = '\0';
192         }       
193 }
194
195
196 int DLoader_Exec_Cmd(char *cmdbuf)
197 {
198         void *vP;
199         struct ProtoFunctionHook *p;
200
201         if (GetHash(ProtoHookList, cmdbuf, 4, &vP) && (vP != NULL)) {
202                 p = (struct ProtoFunctionHook*) vP;
203                 p->handler(&cmdbuf[5]);
204                 return 1;
205         }
206         return 0;
207 }
208
209 long FourHash(const char *key, long length) 
210 {
211         int i;
212         int ret = 0;
213         const unsigned char *ptr = (const unsigned char*)key;
214
215         for (i = 0; i < 4; i++, ptr ++) 
216                 ret = (ret << 8) | 
217                         ( ((*ptr >= 'a') &&
218                            (*ptr <= 'z'))? 
219                           *ptr - 'a' + 'A': 
220                           *ptr);
221
222         return ret;
223 }
224
225 void CtdlRegisterProtoHook(void (*handler) (char *), char *cmd, char *desc)
226 {
227         struct ProtoFunctionHook *p;
228
229         if (ProtoHookList == NULL)
230                 ProtoHookList = NewHash (1, FourHash);
231
232
233         p = (struct ProtoFunctionHook *)
234                 malloc(sizeof(struct ProtoFunctionHook));
235
236         if (p == NULL) {
237                 fprintf(stderr, "can't malloc new ProtoFunctionHook\n");
238                 exit(EXIT_FAILURE);
239         }
240         p->handler = handler;
241         p->cmd = cmd;
242         p->desc = desc;
243
244         Put(ProtoHookList, cmd, 4, p, NULL);
245         syslog(LOG_INFO, "Registered server command %s (%s)\n", cmd, desc);
246 }
247
248 void CtdlDestroyProtoHooks(void)
249 {
250
251         DeleteHash(&ProtoHookList);
252 }
253
254
255 void CtdlRegisterCleanupHook(void (*fcn_ptr) (void))
256 {
257
258         struct CleanupFunctionHook *newfcn;
259
260         newfcn = (struct CleanupFunctionHook *)
261             malloc(sizeof(struct CleanupFunctionHook));
262         newfcn->next = CleanupHookTable;
263         newfcn->h_function_pointer = fcn_ptr;
264         CleanupHookTable = newfcn;
265
266         syslog(LOG_INFO, "Registered a new cleanup function\n");
267 }
268
269
270 void CtdlUnregisterCleanupHook(void (*fcn_ptr) (void))
271 {
272         struct CleanupFunctionHook *cur, *p;
273
274         for (cur = CleanupHookTable; cur != NULL; cur = cur->next) {
275                 /* This will also remove duplicates if any */
276                 while (cur != NULL &&
277                                 fcn_ptr == cur->h_function_pointer) {
278                         syslog(LOG_INFO, "Unregistered cleanup function\n");
279                         p = cur->next;
280                         if (cur == CleanupHookTable) {
281                                 CleanupHookTable = p;
282                         }
283                         free(cur);
284                         cur = p;
285                 }
286         }
287 }
288
289 void CtdlDestroyCleanupHooks(void)
290 {
291         struct CleanupFunctionHook *cur, *p;
292
293         cur = CleanupHookTable;
294         while (cur != NULL)
295         {
296                 syslog(LOG_INFO, "Destroyed cleanup function\n");
297                 p = cur->next;
298                 free(cur);
299                 cur = p;
300         }
301         CleanupHookTable = NULL;
302 }
303
304
305 void CtdlRegisterSessionHook(void (*fcn_ptr) (void), int EventType)
306 {
307
308         struct SessionFunctionHook *newfcn;
309
310         newfcn = (struct SessionFunctionHook *)
311             malloc(sizeof(struct SessionFunctionHook));
312         newfcn->next = SessionHookTable;
313         newfcn->h_function_pointer = fcn_ptr;
314         newfcn->eventtype = EventType;
315         SessionHookTable = newfcn;
316
317         syslog(LOG_INFO, "Registered a new session function (type %d)\n",
318                 EventType);
319 }
320
321
322 void CtdlUnregisterSessionHook(void (*fcn_ptr) (void), int EventType)
323 {
324         struct SessionFunctionHook *cur, *p;
325
326         for (cur = SessionHookTable; cur != NULL; cur = cur->next) {
327                 /* This will also remove duplicates if any */
328                 while (cur != NULL &&
329                                 fcn_ptr == cur->h_function_pointer &&
330                                 EventType == cur->eventtype) {
331                         syslog(LOG_INFO, "Unregistered session function (type %d)\n",
332                                         EventType);
333                         p = cur->next;
334                         if (cur == SessionHookTable) {
335                                 SessionHookTable = p;
336                         }
337                         free(cur);
338                         cur = p;
339                 }
340         }
341 }
342
343 void CtdlDestroySessionHooks(void)
344 {
345         struct SessionFunctionHook *cur, *p;
346
347         cur = SessionHookTable;
348         while (cur != NULL)
349         {
350                 syslog(LOG_INFO, "Destroyed session function\n");
351                 p = cur->next;
352                 free(cur);
353                 cur = p;
354         }
355         SessionHookTable = NULL;
356 }
357
358
359 void CtdlRegisterUserHook(void (*fcn_ptr) (struct ctdluser *), int EventType)
360 {
361
362         struct UserFunctionHook *newfcn;
363
364         newfcn = (struct UserFunctionHook *)
365             malloc(sizeof(struct UserFunctionHook));
366         newfcn->next = UserHookTable;
367         newfcn->h_function_pointer = fcn_ptr;
368         newfcn->eventtype = EventType;
369         UserHookTable = newfcn;
370
371         syslog(LOG_INFO, "Registered a new user function (type %d)\n",
372                 EventType);
373 }
374
375
376 void CtdlUnregisterUserHook(void (*fcn_ptr) (struct ctdluser *), int EventType)
377 {
378         struct UserFunctionHook *cur, *p;
379
380         for (cur = UserHookTable; cur != NULL; cur = cur->next) {
381                 /* This will also remove duplicates if any */
382                 while (cur != NULL &&
383                                 fcn_ptr == cur->h_function_pointer &&
384                                 EventType == cur->eventtype) {
385                         syslog(LOG_INFO, "Unregistered user function (type %d)\n",
386                                         EventType);
387                         p = cur->next;
388                         if (cur == UserHookTable) {
389                                 UserHookTable = p;
390                         }
391                         free(cur);
392                         cur = p;
393                 }
394         }
395 }
396
397 void CtdlDestroyUserHooks(void)
398 {
399         struct UserFunctionHook *cur, *p;
400
401         cur = UserHookTable;
402         while (cur != NULL)
403         {
404                 syslog(LOG_INFO, "Destroyed user function \n");
405                 p = cur->next;
406                 free(cur);
407                 cur = p;
408         }
409         UserHookTable = NULL;
410 }
411
412
413 void CtdlRegisterMessageHook(int (*handler)(struct CtdlMessage *),
414                                 int EventType)
415 {
416
417         struct MessageFunctionHook *newfcn;
418
419         newfcn = (struct MessageFunctionHook *)
420             malloc(sizeof(struct MessageFunctionHook));
421         newfcn->next = MessageHookTable;
422         newfcn->h_function_pointer = handler;
423         newfcn->eventtype = EventType;
424         MessageHookTable = newfcn;
425
426         syslog(LOG_INFO, "Registered a new message function (type %d)\n",
427                 EventType);
428 }
429
430
431 void CtdlUnregisterMessageHook(int (*handler)(struct CtdlMessage *),
432                 int EventType)
433 {
434         struct MessageFunctionHook *cur, *p;
435
436         for (cur = MessageHookTable; cur != NULL; cur = cur->next) {
437                 /* This will also remove duplicates if any */
438                 while (cur != NULL &&
439                                 handler == cur->h_function_pointer &&
440                                 EventType == cur->eventtype) {
441                         syslog(LOG_INFO, "Unregistered message function (type %d)\n",
442                                         EventType);
443                         p = cur->next;
444                         if (cur == MessageHookTable) {
445                                 MessageHookTable = p;
446                         }
447                         free(cur);
448                         cur = p;
449                 }
450         }
451 }
452
453 void CtdlDestroyMessageHook(void)
454 {
455         struct MessageFunctionHook *cur, *p;
456
457         cur = MessageHookTable; 
458         while (cur != NULL)
459         {
460                 syslog(LOG_INFO, "Destroyed message function (type %d)\n", cur->eventtype);
461                 p = cur->next;
462                 free(cur);
463                 cur = p;
464         }
465         MessageHookTable = NULL;
466 }
467
468
469 void CtdlRegisterRoomHook(int (*fcn_ptr)(struct ctdlroom *))
470 {
471         struct RoomFunctionHook *newfcn;
472
473         newfcn = (struct RoomFunctionHook *)
474             malloc(sizeof(struct RoomFunctionHook));
475         newfcn->next = RoomHookTable;
476         newfcn->fcn_ptr = fcn_ptr;
477         RoomHookTable = newfcn;
478
479         syslog(LOG_INFO, "Registered a new room function\n");
480 }
481
482
483 void CtdlUnregisterRoomHook(int (*fcn_ptr)(struct ctdlroom *))
484 {
485         struct RoomFunctionHook *cur, *p;
486
487         for (cur = RoomHookTable; cur != NULL; cur = cur->next) {
488                 while (cur != NULL && fcn_ptr == cur->fcn_ptr) {
489                         syslog(LOG_INFO, "Unregistered room function\n");
490                         p = cur->next;
491                         if (cur == RoomHookTable) {
492                                 RoomHookTable = p;
493                         }
494                         free(cur);
495                         cur = p;
496                 }
497         }
498 }
499
500
501 void CtdlDestroyRoomHooks(void)
502 {
503         struct RoomFunctionHook *cur, *p;
504
505         cur = RoomHookTable;
506         while (cur != NULL)
507         {
508                 syslog(LOG_INFO, "Destroyed room function\n");
509                 p = cur->next;
510                 free(cur);
511                 cur = p;
512         }
513         RoomHookTable = NULL;
514 }
515
516 void CtdlRegisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
517 {
518         struct NetprocFunctionHook *newfcn;
519
520         newfcn = (struct NetprocFunctionHook *)
521             malloc(sizeof(struct NetprocFunctionHook));
522         newfcn->next = NetprocHookTable;
523         newfcn->h_function_pointer = handler;
524         NetprocHookTable = newfcn;
525
526         syslog(LOG_INFO, "Registered a new netproc function\n");
527 }
528
529
530 void CtdlUnregisterNetprocHook(int (*handler)(struct CtdlMessage *, char *) )
531 {
532         struct NetprocFunctionHook *cur, *p;
533
534         for (cur = NetprocHookTable; cur != NULL; cur = cur->next) {
535                 /* This will also remove duplicates if any */
536                 while (cur != NULL &&
537                                 handler == cur->h_function_pointer ) {
538                         syslog(LOG_INFO, "Unregistered netproc function\n");
539                         p = cur->next;
540                         if (cur == NetprocHookTable) {
541                                 NetprocHookTable = p;
542                         }
543                         free(cur);
544                         cur = p;
545                 }
546         }
547 }
548
549 void CtdlDestroyNetprocHooks(void)
550 {
551         struct NetprocFunctionHook *cur, *p;
552
553         cur = NetprocHookTable;
554         while (cur != NULL)
555         {
556                 syslog(LOG_INFO, "Destroyed netproc function\n");
557                 p = cur->next;
558                 free(cur);
559                 cur = p;
560         }
561         NetprocHookTable = NULL;
562 }
563
564
565 void CtdlRegisterDeleteHook(void (*handler)(char *, long) )
566 {
567         struct DeleteFunctionHook *newfcn;
568
569         newfcn = (struct DeleteFunctionHook *)
570             malloc(sizeof(struct DeleteFunctionHook));
571         newfcn->next = DeleteHookTable;
572         newfcn->h_function_pointer = handler;
573         DeleteHookTable = newfcn;
574
575         syslog(LOG_INFO, "Registered a new delete function\n");
576 }
577
578
579 void CtdlUnregisterDeleteHook(void (*handler)(char *, long) )
580 {
581         struct DeleteFunctionHook *cur, *p;
582
583         for (cur = DeleteHookTable; cur != NULL; cur = cur->next) {
584                 /* This will also remove duplicates if any */
585                 while (cur != NULL &&
586                                 handler == cur->h_function_pointer ) {
587                         syslog(LOG_INFO, "Unregistered delete function\n");
588                         p = cur->next;
589                         if (cur == DeleteHookTable) {
590                                 DeleteHookTable = p;
591                         }
592                         free(cur);
593                         cur = p;
594                 }
595         }
596 }
597 void CtdlDestroyDeleteHooks(void)
598 {
599         struct DeleteFunctionHook *cur, *p;
600
601         cur = DeleteHookTable;
602         while (cur != NULL)
603         {
604                 syslog(LOG_INFO, "Destroyed delete function\n");
605                 p = cur->next;
606                 free(cur);
607                 cur = p;                
608         }
609         DeleteHookTable = NULL;
610 }
611
612
613
614
615 void CtdlRegisterFixedOutputHook(char *content_type, void (*handler)(char *, int) )
616 {
617         struct FixedOutputHook *newfcn;
618
619         newfcn = (struct FixedOutputHook *)
620             malloc(sizeof(struct FixedOutputHook));
621         newfcn->next = FixedOutputTable;
622         newfcn->h_function_pointer = handler;
623         safestrncpy(newfcn->content_type, content_type, sizeof newfcn->content_type);
624         FixedOutputTable = newfcn;
625
626         syslog(LOG_INFO, "Registered a new fixed output function for %s\n", newfcn->content_type);
627 }
628
629
630 void CtdlUnregisterFixedOutputHook(char *content_type)
631 {
632         struct FixedOutputHook *cur, *p;
633
634         for (cur = FixedOutputTable; cur != NULL; cur = cur->next) {
635                 /* This will also remove duplicates if any */
636                 while (cur != NULL && (!strcasecmp(content_type, cur->content_type))) {
637                         syslog(LOG_INFO, "Unregistered fixed output function for %s\n", content_type);
638                         p = cur->next;
639                         if (cur == FixedOutputTable) {
640                                 FixedOutputTable = p;
641                         }
642                         free(cur);
643                         cur = p;
644                 }
645         }
646 }
647
648 void CtdlDestroyFixedOutputHooks(void)
649 {
650         struct FixedOutputHook *cur, *p;
651
652         cur = FixedOutputTable; 
653         while (cur != NULL)
654         {
655                 syslog(LOG_INFO, "Destroyed fixed output function for %s\n", cur->content_type);
656                 p = cur->next;
657                 free(cur);
658                 cur = p;
659                 
660         }
661         FixedOutputTable = NULL;
662 }
663
664 /* returns nonzero if we found a hook and used it */
665 int PerformFixedOutputHooks(char *content_type, char *content, int content_length)
666 {
667         struct FixedOutputHook *fcn;
668
669         for (fcn = FixedOutputTable; fcn != NULL; fcn = fcn->next) {
670                 if (!strcasecmp(content_type, fcn->content_type)) {
671                         (*fcn->h_function_pointer) (content, content_length);
672                         return(1);
673                 }
674         }
675         return(0);
676 }
677
678
679
680
681
682 void CtdlRegisterXmsgHook(int (*fcn_ptr) (char *, char *, char *, char *), int order)
683 {
684
685         struct XmsgFunctionHook *newfcn;
686
687         newfcn = (struct XmsgFunctionHook *) malloc(sizeof(struct XmsgFunctionHook));
688         newfcn->next = XmsgHookTable;
689         newfcn->order = order;
690         newfcn->h_function_pointer = fcn_ptr;
691         XmsgHookTable = newfcn;
692         syslog(LOG_INFO, "Registered a new x-msg function (priority %d)\n", order);
693 }
694
695
696 void CtdlUnregisterXmsgHook(int (*fcn_ptr) (char *, char *, char *, char *), int order)
697 {
698         struct XmsgFunctionHook *cur, *p;
699
700         for (cur = XmsgHookTable; cur != NULL; cur = cur->next) {
701                 /* This will also remove duplicates if any */
702                 while (cur != NULL &&
703                                 fcn_ptr == cur->h_function_pointer &&
704                                 order == cur->order) {
705                         syslog(LOG_INFO, "Unregistered x-msg function "
706                                         "(priority %d)\n", order);
707                         p = cur->next;
708                         if (cur == XmsgHookTable) {
709                                 XmsgHookTable = p;
710                         }
711                         free(cur);
712                         cur = p;
713                 }
714         }
715 }
716
717 void CtdlDestroyXmsgHooks(void)
718 {
719         struct XmsgFunctionHook *cur, *p;
720
721         cur = XmsgHookTable;
722         while (cur != NULL)
723         {
724                 syslog(LOG_INFO, "Destroyed x-msg function "
725                         "(priority %d)\n", cur->order);
726                 p = cur->next;
727                         
728                 free(cur);
729                 cur = p;
730         }
731         XmsgHookTable = NULL;
732 }
733
734
735 void CtdlRegisterServiceHook(int tcp_port,
736                              char *sockpath,
737                              void (*h_greeting_function) (void),
738                              void (*h_command_function) (void),
739                              void (*h_async_function) (void),
740                              const char *ServiceName)
741 {
742         struct ServiceFunctionHook *newfcn;
743         char *message;
744         char error[SIZ];
745
746         strcpy(error, "");
747         newfcn = (struct ServiceFunctionHook *) malloc(sizeof(struct ServiceFunctionHook));
748         message = (char*) malloc (SIZ + SIZ);
749         
750         newfcn->next = ServiceHookTable;
751         newfcn->tcp_port = tcp_port;
752         newfcn->sockpath = sockpath;
753         newfcn->h_greeting_function = h_greeting_function;
754         newfcn->h_command_function = h_command_function;
755         newfcn->h_async_function = h_async_function;
756         newfcn->ServiceName = ServiceName;
757
758         if (sockpath != NULL) {
759                 newfcn->msock = ctdl_uds_server(sockpath, config.c_maxsessions, error);
760                 snprintf(message, SIZ, "Unix domain socket '%s': ", sockpath);
761         }
762         else if (tcp_port <= 0) {       /* port -1 to disable */
763                 syslog(LOG_INFO, "Service %s has been manually disabled, skipping\n", ServiceName);
764                 free (message);
765                 free(newfcn);
766                 return;
767         }
768         else {
769                 newfcn->msock = ctdl_tcp_server(config.c_ip_addr,
770                                               tcp_port,
771                                               config.c_maxsessions, 
772                                               error);
773                 snprintf(message, SIZ, "TCP port %s:%d: (%s) ", 
774                          config.c_ip_addr, tcp_port, ServiceName);
775         }
776
777         if (newfcn->msock > 0) {
778                 ServiceHookTable = newfcn;
779                 strcat(message, "registered.");
780                 syslog(LOG_INFO, "%s\n", message);
781         }
782         else {
783                 AddPortError(message, error);
784                 strcat(message, "FAILED.");
785                 syslog(LOG_CRIT, "%s\n", message);
786                 free(newfcn);
787         }
788         free(message);
789 }
790
791
792 void CtdlUnregisterServiceHook(int tcp_port, char *sockpath,
793                         void (*h_greeting_function) (void),
794                         void (*h_command_function) (void),
795                         void (*h_async_function) (void)
796                         )
797 {
798         struct ServiceFunctionHook *cur, *p;
799
800         cur = ServiceHookTable;
801         while (cur != NULL) {
802                 /* This will also remove duplicates if any */
803                 while (cur != NULL &&
804                                 !(sockpath && cur->sockpath &&
805                                         strcmp(sockpath, cur->sockpath)) &&
806                                 h_greeting_function == cur->h_greeting_function &&
807                                 h_command_function == cur->h_command_function &&
808                                 h_async_function == cur->h_async_function &&
809                                 tcp_port == cur->tcp_port) {
810                         close(cur->msock);
811                         if (sockpath) {
812                                 syslog(LOG_INFO, "Closed UNIX domain socket %s\n",
813                                                 sockpath);
814                         } else if (tcp_port) {
815                                 syslog(LOG_INFO, "Closed TCP port %d\n", tcp_port);
816                         } else {
817                                 syslog(LOG_INFO, "Unregistered service \"%s\"\n", cur->ServiceName);
818                         }
819                         p = cur->next;
820                         if (cur == ServiceHookTable) {
821                                 ServiceHookTable = p;
822                         }
823                         free(cur);
824                         cur = p;
825                 }
826         }
827 }
828
829
830 void CtdlShutdownServiceHooks(void)
831 {
832         /* sort of a duplicate of close_masters() but called earlier */
833         struct ServiceFunctionHook *cur;
834
835         cur = ServiceHookTable;
836         while (cur != NULL) 
837         {
838                 if (cur->msock != -1)
839                 {
840                         close(cur->msock);
841                         cur->msock = -1;
842                         if (cur->sockpath != NULL){
843                                 syslog(LOG_INFO, "[%s] Closed UNIX domain socket %s\n",
844                                               cur->ServiceName,
845                                               cur->sockpath);
846                         } else {
847                                 syslog(LOG_INFO, "[%s] closing service\n", 
848                                               cur->ServiceName);
849                         }
850                 }
851                 cur = cur->next;
852         }
853 }
854
855 void CtdlDestroyServiceHook(void)
856 {
857         struct ServiceFunctionHook *cur, *p;
858
859         cur = ServiceHookTable;
860         while (cur != NULL)
861         {
862                 close(cur->msock);
863                 if (cur->sockpath) {
864                         syslog(LOG_INFO, "Closed UNIX domain socket %s\n",
865                                 cur->sockpath);
866                 } else if (cur->tcp_port) {
867                         syslog(LOG_INFO, "Closed TCP port %d\n", cur->tcp_port);
868                 } else {
869                         syslog(LOG_INFO, "Destroyed service \"%s\"\n", cur->ServiceName);
870                 }
871                 p = cur->next;
872                 free(cur);
873                 cur = p;
874         }
875         ServiceHookTable = NULL;
876 }
877
878 void CtdlRegisterSearchFuncHook(void (*fcn_ptr)(int *, long **, const char *), char *name)
879 {
880         struct SearchFunctionHook *newfcn;
881
882         if (!name || !fcn_ptr) {
883                 return;
884         }
885         
886         newfcn = (struct SearchFunctionHook *)
887             malloc(sizeof(struct SearchFunctionHook));
888         newfcn->next = SearchFunctionHookTable;
889         newfcn->name = name;
890         newfcn->fcn_ptr = fcn_ptr;
891         SearchFunctionHookTable = newfcn;
892
893         syslog(LOG_INFO, "Registered a new search function (%s)\n", name);
894 }
895
896 void CtdlUnregisterSearchFuncHook(void (*fcn_ptr)(int *, long **, const char *), char *name)
897 {
898         struct SearchFunctionHook *cur, *p;
899         
900         for (cur = SearchFunctionHookTable; cur != NULL; cur = cur->next) {
901                 while (fcn_ptr && (cur->fcn_ptr == fcn_ptr) && name && !strcmp(name, cur->name)) {
902                         syslog(LOG_INFO, "Unregistered search function(%s)\n", name);
903                         p = cur->next;
904                         if (cur == SearchFunctionHookTable) {
905                                 SearchFunctionHookTable = p;
906                         }
907                         free (cur);
908                         cur = p;
909                 }
910         }
911 }
912
913 void CtdlDestroySearchHooks(void)
914 {
915         struct SearchFunctionHook *cur, *p;
916
917         cur = SearchFunctionHookTable;
918         SearchFunctionHookTable = NULL;
919         while (cur != NULL) {
920                 p = cur->next;
921                 free(cur);
922                 cur = p;
923         }
924 }
925
926 void CtdlModuleDoSearch(int *num_msgs, long **search_msgs, const char *search_string, const char *func_name)
927 {
928         struct SearchFunctionHook *fcn = NULL;
929
930         for (fcn = SearchFunctionHookTable; fcn != NULL; fcn = fcn->next) {
931                 if (!func_name || !strcmp(func_name, fcn->name)) {
932                         (*fcn->fcn_ptr) (num_msgs, search_msgs, search_string);
933                         return;
934                 }
935         }
936         *num_msgs = 0;
937 }
938
939
940 void PerformSessionHooks(int EventType)
941 {
942         struct SessionFunctionHook *fcn = NULL;
943
944         for (fcn = SessionHookTable; fcn != NULL; fcn = fcn->next) {
945                 if (fcn->eventtype == EventType) {
946                         if (EventType == EVT_TIMER) {
947                                 pthread_setspecific(MyConKey, NULL);    /* for every hook */
948                         }
949                         (*fcn->h_function_pointer) ();
950                 }
951         }
952 }
953
954 void PerformUserHooks(struct ctdluser *usbuf, int EventType)
955 {
956         struct UserFunctionHook *fcn = NULL;
957
958         for (fcn = UserHookTable; fcn != NULL; fcn = fcn->next) {
959                 if (fcn->eventtype == EventType) {
960                         (*fcn->h_function_pointer) (usbuf);
961                 }
962         }
963 }
964
965 int PerformMessageHooks(struct CtdlMessage *msg, int EventType)
966 {
967         struct MessageFunctionHook *fcn = NULL;
968         int total_retval = 0;
969
970         /* Other code may elect to protect this message from server-side
971          * handlers; if this is the case, don't do anything.
972         syslog(LOG_DEBUG, "** Event type is %d, flags are %d\n", EventType, msg->cm_flags);
973          */
974         if (msg->cm_flags & CM_SKIP_HOOKS) {
975                 syslog(LOG_DEBUG, "Skipping hooks\n");
976                 return(0);
977         }
978
979         /* Otherwise, run all the hooks appropriate to this event type.
980          */
981         for (fcn = MessageHookTable; fcn != NULL; fcn = fcn->next) {
982                 if (fcn->eventtype == EventType) {
983                         total_retval = total_retval + (*fcn->h_function_pointer) (msg);
984                 }
985         }
986
987         /* Return the sum of the return codes from the hook functions.  If
988          * this is an EVT_BEFORESAVE event, a nonzero return code will cause
989          * the save operation to abort.
990          */
991         return total_retval;
992 }
993
994
995 int PerformRoomHooks(struct ctdlroom *target_room)
996 {
997         struct RoomFunctionHook *fcn;
998         int total_retval = 0;
999
1000         syslog(LOG_DEBUG, "Performing room hooks for <%s>\n", target_room->QRname);
1001
1002         for (fcn = RoomHookTable; fcn != NULL; fcn = fcn->next) {
1003                 total_retval = total_retval + (*fcn->fcn_ptr) (target_room);
1004         }
1005
1006         /* Return the sum of the return codes from the hook functions.
1007          */
1008         return total_retval;
1009 }
1010
1011
1012 int PerformNetprocHooks(struct CtdlMessage *msg, char *target_room)
1013 {
1014         struct NetprocFunctionHook *fcn;
1015         int total_retval = 0;
1016
1017         for (fcn = NetprocHookTable; fcn != NULL; fcn = fcn->next) {
1018                 total_retval = total_retval +
1019                         (*fcn->h_function_pointer) (msg, target_room);
1020         }
1021
1022         /* Return the sum of the return codes from the hook functions.
1023          * A nonzero return code will cause the message to *not* be imported.
1024          */
1025         return total_retval;
1026 }
1027
1028
1029 void PerformDeleteHooks(char *room, long msgnum)
1030 {
1031         struct DeleteFunctionHook *fcn;
1032
1033         for (fcn = DeleteHookTable; fcn != NULL; fcn = fcn->next) {
1034                 (*fcn->h_function_pointer) (room, msgnum);
1035         }
1036 }
1037
1038
1039
1040
1041
1042 int PerformXmsgHooks(char *sender, char *sender_email, char *recp, char *msg)
1043 {
1044         struct XmsgFunctionHook *fcn;
1045         int total_sent = 0;
1046         int p;
1047
1048         for (p=0; p<MAX_XMSG_PRI; ++p) {
1049                 for (fcn = XmsgHookTable; fcn != NULL; fcn = fcn->next) {
1050                         if (fcn->order == p) {
1051                                 total_sent +=
1052                                         (*fcn->h_function_pointer)
1053                                                 (sender, sender_email, recp, msg);
1054                         }
1055                 }
1056                 /* Break out of the loop if a higher-priority function
1057                  * successfully delivered the message.  This prevents duplicate
1058                  * deliveries to local users simultaneously signed onto
1059                  * remote services.
1060                  */
1061                 if (total_sent) break;
1062         }
1063         return total_sent;
1064 }
1065
1066
1067 /*
1068  * Dirty hack until we impliment a hook mechanism for this
1069  */
1070 void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response)
1071 {
1072 #ifdef HAVE_OPENSSL
1073         CtdlStartTLS (ok_response, nosup_response, error_response);
1074 #endif
1075 }