1 /*
2  * ----------------------------------------------------------------
3  * ircproxy - Connection Logging Functions
4  * ----------------------------------------------------------------
5  * Copyright (C) 1997-2009 Jonas Kvinge
6  *
7  * This file is part of ircproxy.
8  *
9  * ircproxy is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * ircproxy is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with ircproxy.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Additional permission under GNU GPL version 3 section 7
23  *
24  * If you modify ircproxy, or any covered work, by linking or
25  * combining it with openssl (or a modified version of that library),
26  * containing parts covered by the terms of the OpenSSL License and the
27  * SSLeay License, the licensors of ircproxy grant you additional
28  * permission to convey the resulting work.
29  *
30  * $Id: conn_log.c 54 2009-03-18 18:23:29Z jonasio $
31  *
32  */
33 
34 #define CONN_LOG_C
35 
36 #define NEED_SYS_TYPES_H 1		/* Extra types */
37 #define NEED_SYS_PARAM_H 1		/* Some systems need this */
38 #define NEED_LIMITS_H 0			/* Kernel limits */
39 #define NEED_STDARG_H 1			/* va_list, etc */
40 #define NEED_ERRNO_H 1			/* errno */
41 #define NEED_CTYPE_H 1			/* isdigit(), etc */
42 #define NEED_NETINET_IN_H 1		/* in_addr, sockaddr_in, etc */
43 #define NEED_ARPA_INET_H 0		/* inet_ntoa(), inet_aton(), etc */
44 #define NEED_STDIO_H 1			/* Standard C UNIX functions */
45 #define NEED_STDLIB_H 1			/* malloc(), exit(), atoi(), etc */
46 #define NEED_TIME_H 1			/* time(), etc */
47 #define NEED_SYSCTL_H 0			/* sysctl(), etc */
48 #define NEED_SYS_STAT_H 1		/* chmod(), mkdir(), etc */
49 #define NEED_SYS_UIO_H 0		/* iovec, etc */
50 #define NEED_FCNTL_H 1			/* open(), creat(), fcntl(), etc */
51 #define NEED_SYS_IOCTL_H 0		/* ioctl(), etc */
52 #define NEED_SYS_FILIO_H 0		/* Solaris need this for ioctl(), etc */
53 #define NEED_UNISTD_H 1			/* Unix standard functions */
54 #define NEED_STRING_H 1			/* C string functions */
55 #define NEED_SIGNAL_H 0			/* Signal functions */
56 #define NEED_SYS_SOCKET_H 0		/* Socket functions */
57 #define NEED_NETDB_H 0			/* Network database functions */
58 #define NEED_ARPA_NAMESER_H 0		/* Nameserver definitions */
59 #define NEED_GETUSERPW_HEADERS 0 	/* Functions to retrive system passwords */
60 #define NEED_ARES 0			/* Functions needed for ares */
61 #define NEED_SSL 1			/* Needed for SSL support */
62 
63 #include "includes.h"
64 #include "irc.h"
65 
66 #include "conf.h"
67 
68 #include "conn.h"
69 #include "conn_log.h"
70 
71 #include "chan.h"
72 
73 extern struct Conf_Struct ConfS;
74 extern unsigned short int Root;
75 
76 /* CONN_ADDLOG - JONAS (20.01.2008) */
77 
conn_addlog(struct Conn_Struct * ConnS,unsigned short int LogType,const char * const NamePT,const char * const LogFilePT,const char * const LogFileFullPathPT)78 struct ConnLog_Struct *conn_addlog(struct Conn_Struct *ConnS, unsigned short int LogType, const char *const NamePT, const char *const LogFilePT, const char *const LogFileFullPathPT) {
79 
80   struct ConnLog_Struct *ConnLog = NULL;
81   struct ConnLog_Struct *ConnLog_NEW = NULL;
82 
83   assert(ConnS != NULL);
84   assert(NamePT != NULL);
85   assert(LogFilePT != NULL);
86   assert(LogFileFullPathPT != NULL);
87 
88   ConnLog = conn_getlog(ConnS, LogFilePT);
89   if (ConnLog != NULL) {
90     aerrno = AEEXISTS;
91     return(ConnLog);
92   }
93 
94   ConnLog_NEW = malloc(sizeof(struct ConnLog_Struct));
95   if (ConnLog_NEW == NULL) {
96     aerrno = AEMALLOC;
97     return(NULL);
98   }
99   memset(ConnLog_NEW, 0, sizeof(struct ConnLog_Struct));
100 
101   ConnLog_NEW->LogType = LogType;
102   ConnLog_NEW->Name = strdup(NamePT);
103   ConnLog_NEW->LogFile = strdup(LogFilePT);
104   ConnLog_NEW->LogFileFullPath = strdup(LogFileFullPathPT);
105 
106   if ((ConnLog_NEW->Name == NULL) || (ConnLog_NEW->LogFile == NULL) || (ConnLog_NEW->LogFileFullPath == NULL)) {
107     free(ConnLog_NEW->Name);
108     free(ConnLog_NEW->LogFile);
109     free(ConnLog_NEW->LogFileFullPath);
110     free(ConnLog_NEW);
111     aerrno = AEMALLOC;
112     return(NULL);
113   }
114 
115   if (ConnS->Log_Head == NULL) {
116     ConnS->Log_Head = ConnLog_NEW;
117     ConnS->Log_Tail = ConnLog_NEW;
118   }
119   else {
120     ConnLog = ConnS->Log_Tail;
121     ConnLog->Next = ConnLog_NEW;
122     ConnLog_NEW->Prev = ConnLog;
123     ConnS->Log_Tail = ConnLog_NEW;
124   }
125 
126   ConnS->NumLogs++;
127 
128   aerrno = AESUCCESS;
129   return(ConnLog_NEW);
130 
131 }
132 
133 /* CONN_REMLOG FUNCTION - JONAS (20.01.2008) */
134 
conn_remlog(struct Conn_Struct * ConnS,struct ConnLog_Struct * ConnLog)135 void conn_remlog(struct Conn_Struct *ConnS, struct ConnLog_Struct *ConnLog) {
136 
137   assert(ConnS != NULL);
138   assert(ConnLog != NULL);
139 
140   if (ConnLog->Prev == NULL) { ConnS->Log_Head = ConnLog->Next; }
141   else { ConnLog->Prev->Next = ConnLog->Next; }
142 
143   if (ConnLog->Next == NULL) { ConnS->Log_Tail = ConnLog->Prev; }
144   else { ConnLog->Next->Prev = ConnLog->Prev; }
145 
146   free(ConnLog->Name);
147   free(ConnLog->LogFile);
148   free(ConnLog->LogFileFullPath);
149   free(ConnLog);
150 
151   ConnS->NumLogs--;
152 
153 }
154 
155 /* CONN_GETLOG FUNCTION - JONAS (20.01.2008) */
156 
conn_getlog(struct Conn_Struct * ConnS,const char * const LogFilePT)157 struct ConnLog_Struct *conn_getlog(struct Conn_Struct *ConnS, const char *const LogFilePT) {
158 
159   struct ConnLog_Struct *ConnLog = NULL;
160 
161   assert(ConnS != NULL);
162   assert(LogFilePT != NULL);
163 
164   for (ConnLog = ConnS->Log_Head ; ConnLog != NULL ; ConnLog = ConnLog->Next) {
165     if (strcasecmp(ConnLog->LogFile, LogFilePT) == FALSE) {
166       aerrno = AESUCCESS;
167       return(ConnLog);
168     }
169   }
170   aerrno = AENOMATCH;
171   return(NULL);
172 
173 }
174 
175 /* CONN_REMLOGS FUNCTION - JONAS (20.01.2008) */
176 
conn_destroylogs(struct Conn_Struct * ConnS)177 void conn_destroylogs(struct Conn_Struct *ConnS) {
178 
179   while (ConnS->Log_Head != NULL) { conn_remlog(ConnS, ConnS->Log_Head); }
180 
181 }
182 
183 /* CONN_LOG FUNCTION - JONAS (18.07.2001) */
184 
conn_log(struct Conn_Struct * ConnS,struct Chan_Struct * ChanS,const char * const ChanPT,const char * const NickPT,const char * const UserPT,const char * const HostPT,const char * const NUHPT,const char * const CommandPT,const char * const ParamPT,const char * const MessagePT)185 unsigned short int conn_log(struct Conn_Struct *ConnS, struct Chan_Struct *ChanS, const char *const ChanPT, const char *const NickPT, const char *const UserPT, const char *const HostPT, const char *const NUHPT, const char *const CommandPT, const char *const ParamPT, const char *const MessagePT) {
186 
187   signed long int Result = 0;
188   char *HomeDirPT = NULL;
189   char LogFileName[FILELEN+1] = "";
190   char LogPath[FILELEN+1] = "";
191   char LogFileNamePath[FILELEN+1] = "";
192   char **FilePTT = NULL;
193   FILE *FileFP = NULL;
194   FILE **FileFPP = &FileFP;
195 #if 0
196   struct tm *TMPT = NULL;
197   char Date[LINELEN+1] = "";
198 #endif
199   unsigned short int LogErrors = 0;
200   char *LogFileNamePT = NULL;
201 
202   assert(ConnS != NULL);
203 
204   Conn_ClearLogHomeDir(ConnS);
205 
206   if ((ConfS.UnixPasswd == TRUE) && (Root == TRUE)) {
207     HomeDirPT = sysgethomedirfromuser(ConnS->User);
208     if (HomeDirPT == NULL) {
209       ConnLogError("Unable to log messages for connection %s: Unable to retrive home directory for user \"%s\"", ConnS->Name, ConnS->User);
210       return(ERROR);
211     }
212     if (sysseteuidbyuser(ConnS->User) != SUCCESS) {
213       ConnLogError("Unable to log messages for connection %s: Unable to set effective user ID to \"%s\"", ConnS->Name, ConnS->User);
214       return(ERROR);
215     }
216     if (access(HomeDirPT, X_OK) != SUCCESS) {
217       ConnLogError("Unable to log messages for connection %s: Unable to access home directory for user \"%s\"", ConnS->Name, ConnS->User);
218       sysseteuidnormal();
219       return(ERROR);
220     }
221     Conn_SetLogHomeDir(ConnS);
222     snprintf(LogPath, FILELEN+1, "%s/ircproxy-logs", HomeDirPT);
223   }
224   else { snprintf(LogPath, FILELEN+1, "%s", ConfS.LogPath); }
225 
226   if (access(LogPath, X_OK) == ERROR) {
227     Result = mkdir(LogPath, (S_IRWXU|S_IXGRP|S_IXOTH));
228     if ((Result <= ERROR) && (errno != EEXIST)) {
229       sysprint(BITMASK_ERROR, "Unable to create directory %s: [%d] %s", LogPath, errno, strerror(errno));
230       if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
231       return(ERROR);
232     }
233   }
234 
235 #if 0
236   TMPT = localtime(&NOW);
237   strftime(Date, LINELEN, "%Y%m%d", TMPT);
238 #endif
239 
240   VariableDataS.ConnS = ConnS;
241   VariableDataS.ChanS = ChanS;
242   VariableDataS.Chan = ChanPT;
243   VariableDataS.Nick = NickPT;
244   VariableDataS.User = UserPT;
245   VariableDataS.Host = HostPT;
246   VariableDataS.NUH = NUHPT;
247   VariableDataS.Command = CommandPT;
248   VariableDataS.Param = ParamPT;
249   VariableDataS.Message = MessagePT;
250 
251   if (CommandPT == NULL) {
252     LogFileNamePT = conn_log_variable(ConfS.RawLogFileFormat, NULL);
253 #if 0
254     snprintf(LogFileName, FILELEN+1, "%s-%s-%s.log", ConnS->Name, ConnS->ServerHostName, Date);
255 #endif
256     FilePTT = &ConnS->RawLogFile;
257     FileFPP = &ConnS->RawLogFileFP;
258   }
259   else {
260     if (ChanPT == NULL) {
261       LogFileNamePT = conn_log_variable(ConfS.PrivLogFileFormat, NULL);
262 #if 0
263       snprintf(LogFileName, FILELEN+1, "%s-%s.log", ConnS->Name, Date);
264 #endif
265       FilePTT = &ConnS->PrivLogFile;
266       FileFPP = &ConnS->PrivLogFileFP;
267     }
268     else {
269       LogFileNamePT = conn_log_variable(ConfS.ChanLogFileFormat, NULL);
270 #if 0
271       snprintf(LogFileName, FILELEN+1, "%s-%s-%s.log", ConnS->Name, ChanPT, Date);
272 #endif
273       if (ChanS == NULL) {
274         FileFPP = &FileFP;
275       }
276       else {
277         FilePTT = &ChanS->LogFile;
278         FileFPP = &ChanS->LogFileFP;
279       }
280     }
281   }
282   strcpy(LogFileName, LogFileNamePT);
283   FREE(LogFileNamePT);
284   strfilefix(LogFileName);
285   snprintf(LogFileNamePath, FILELEN+1, "%s/%s", LogPath, LogFileName);
286 
287   if ((*FilePTT != NULL) && (strcmp(*FilePTT, LogFileName) != FALSE)) {
288     FREE(*FilePTT);
289     if (*FileFPP != NULL) {
290       fclose(*FileFPP);
291       *FileFPP = NULL;
292     }
293   }
294 
295   do {
296 
297     if (*FileFPP == NULL) {
298       *FileFPP = fopen(LogFileNamePath, "a");
299       if (*FileFPP == NULL) {
300         ConnLogError("Unable to open %s for writing: [%d] %s", LogFileNamePath, errno, strerror(errno));
301         if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
302         return(ERROR);
303       }
304       if (FilePTT != NULL) *FilePTT = strrealloc(*FilePTT, LogFileName);
305       Result = chmod(LogFileNamePath, S_IRUSR|S_IWUSR);
306       if (Result <= ERROR) {
307         ConnLogError("Unable to set file permission for %s: [%d] %s", LogFileNamePath, errno, strerror(errno));
308 #if 0
309         fclose(*FileFPP);
310         if (FilePTT != NULL) FREE(*FilePTT);
311         if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
312         return(ERROR);
313 #endif
314       }
315       Result = fprintf(*FileFPP, "%s *** Logging started.%s", dtstamp(), LINEFEED);
316     }
317 
318     if (CommandPT == NULL) { Result = fprintf(*FileFPP, "%s *** %s%s", dtstamp(), MessagePT, LINEFEED); }
319     else {
320       assert(CommandPT != NULL);
321       assert(NUHPT != NULL);
322       assert(NickPT != NULL);
323       assert(UserPT != NULL);
324       assert(HostPT != NULL);
325       if (strcasecmp(CommandPT, "PRIVMSG") == FALSE) { Result = fprintf(*FileFPP, "%s <%s> %s%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
326       else if (strcasecmp(CommandPT, "NOTICE") == FALSE) { Result = fprintf(*FileFPP, "%s -%s- %s%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
327       else if (strcasecmp(CommandPT, "ACTION") == FALSE) { Result = fprintf(*FileFPP, "%s * %s %s%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
328       else if (strcasecmp(CommandPT, "CTCP") == FALSE) {
329         if (MessagePT == NULL)  { Result = fprintf(*FileFPP, "%s %s CTCP %s%s", dtstamp(), NUHPT, ParamPT, LINEFEED); }
330         else { Result = fprintf(*FileFPP, "%s %s CTCP %s %s%s", dtstamp(), NUHPT, ParamPT, MessagePT, LINEFEED); }
331       }
332       else if (ChanPT != NULL) {
333         if (strcasecmp(CommandPT, "NICK") == FALSE) { Result = fprintf(*FileFPP, "%s %s (%s@%s) is now known as %s%s", dtstamp(), NickPT, UserPT, HostPT, ParamPT, LINEFEED); }
334         else if (strcasecmp(CommandPT, "QUIT") == FALSE) {
335           if (MessagePT == NULL) { Result = fprintf(*FileFPP, "%s %s quits%s", dtstamp(), NUHPT, LINEFEED); }
336           else { Result = fprintf(*FileFPP, "%s %s quits: %s%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
337         }
338         else if (strcasecmp(CommandPT, "MODE") == FALSE) { Result = fprintf(*FileFPP, "%s %s sets mode: %s%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
339         else if (strcasecmp(CommandPT, "JOIN") == FALSE) { Result = fprintf(*FileFPP, "%s %s has joined %s%s", dtstamp(), NUHPT, ChanPT, LINEFEED); }
340         else if (strcasecmp(CommandPT, "PART") == FALSE) {
341           if (MessagePT == NULL) { Result = fprintf(*FileFPP, "%s %s has left %s%s", dtstamp(), NUHPT, ChanPT, LINEFEED); }
342           else { Result = fprintf(*FileFPP, "%s %s has left %s: %s%s", dtstamp(), NUHPT, ChanPT, MessagePT, LINEFEED); }
343         }
344         else if (strcasecmp(CommandPT, "KICK") == FALSE) { Result = fprintf(*FileFPP, "%s %s was kicked by %s: %s%s", dtstamp(), ParamPT, NUHPT, MessagePT, LINEFEED); }
345         else if (strcasecmp(CommandPT, "TOPIC") == FALSE) { Result = fprintf(*FileFPP, "%s %s changed topic to: '%s'%s", dtstamp(), NUHPT, MessagePT, LINEFEED); }
346 
347       }
348     }
349 
350     if (Result <= ERROR) {
351       ++LogErrors;
352       fclose(*FileFPP);
353       *FileFPP = NULL;
354       if (FilePTT != NULL) FREE(*FilePTT);
355     }
356     else { break; }
357 
358   }
359   while (LogErrors < 2);
360 
361   if (FileFPP != NULL) fflush(*FileFPP);
362   if (FileFPP == &FileFP) fclose(*FileFPP);
363 
364   if (Conn_IsLogHomeDir(ConnS)) { sysseteuidnormal(); }
365 
366   if (LogErrors < 2) {
367     if (CommandPT == NULL) { conn_addlog(ConnS, CONN_LOG_TYPE_RAW, ConnS->ServerHostName, LogFileName, LogFileNamePath); }
368     else {
369       if (ChanPT == NULL) { conn_addlog(ConnS, CONN_LOG_TYPE_PRIV, ConnS->Name, LogFileName, LogFileNamePath); }
370       else { conn_addlog(ConnS, CONN_LOG_TYPE_CHAN, ChanPT, LogFileName, LogFileNamePath); }
371     }
372   }
373 
374 #if 0
375   for (Index = 0 ; Index < ConnS->Logs ; ++Index) {
376     if (strcasecmp(ConnS->LogFile, LogFileName) == FALSE) { break; }
377   }
378   if (Index == ConnS->Logs) {
379     ++ConnS->Logs;
380     TempPT = realloc(ConnS->LogFile, (sizeof(char *) * ConnS->Logs));
381     if (TempPT == NULL) {
382       sysprint(BITMASK_ERROR, "Memory allocation failure: [%d] %s", errno, strerror(errno));
383       return(ERROR);
384     }
385     ConnS->LogFile = TempPT;
386     ConnS->LogFile[Index] = strdup(LogFileNamePath);
387   }
388 #endif
389 
390   return(SUCCESS);
391 
392 }
393 
394 /* CONN_RAWLOG FUNCTION - JONAS (01.07.2000) */
395 
conn_rawlog(struct Conn_Struct * ConnS,const char * const LinePT,...)396 void conn_rawlog(struct Conn_Struct *ConnS, const char *const LinePT, ...) {
397 
398   char Line[IRC_MSGLEN+1] = "";
399   va_list Args;
400 
401   assert(ConnS != NULL);
402   assert(LinePT != NULL);
403 
404   va_start(Args, LinePT);
405   vsnprintf(Line, IRC_MSGLEN+1, LinePT, Args);
406   va_end(Args);
407 
408   conn_log(ConnS, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, Line);
409 
410 }
411 
412 /* CONN_LOG_VARIABLE FUNCTION - JONAS (30.09.2001) */
413 
conn_log_variable(const char * OldMessagePT,char * NewMessagePT)414 char *conn_log_variable(const char *OldMessagePT, char *NewMessagePT) {
415 
416   unsigned long int Len = 0;
417   unsigned long int Index = 0;
418   char *MessagePT = NULL;
419   char *TempPT = NULL;
420   const char *VarNamePT = NULL;
421   const char *VarDataPT = NULL;
422 
423   unsigned long int VSLen = 0;
424   unsigned long int VSIndex = 0;
425 
426   struct tm *TMPT = NULL;
427   char TimeYear[LINELEN+1] = "";
428   char TimeMonth[LINELEN+1] = "";
429   char TimeWeek[LINELEN+1] = "";
430   char TimeDay[LINELEN+1] = "";
431   char TimeHour[LINELEN+1] = "";
432   char TimeMin[LINELEN+1] = "";
433   char TimeSec[LINELEN+1] = "";
434 
435   struct Variable_Struct Variable[] = {
436 
437     {
438       "%mhost",
439       HOSTNAME
440     },
441     {
442       "%conn",
443       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->Name)
444     },
445     {
446       "%serverhost",
447       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->ServerHostName)
448     },
449     {
450       "%servername",
451       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->ServerName)
452     },
453     {
454       "%cnick",
455       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->Nick)
456     },
457     {
458       "%cuser",
459       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->User)
460     },
461     {
462       "%chost",
463       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->Host)
464     },
465     {
466       "%anick",
467       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->IRCNick)
468     },
469     {
470       "%auser",
471       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->User)
472     },
473     {
474       "%ahost",
475       ((VariableDataS.ConnS == NULL) ? NULL : VariableDataS.ConnS->Host)
476     },
477     {
478       "%nick",
479       VariableDataS.Nick
480     },
481     {
482       "%user",
483       VariableDataS.User
484     },
485     {
486       "%host",
487       VariableDataS.Host
488     },
489     {
490       "%nuh",
491       VariableDataS.NUH
492     },
493     {
494       "%chan",
495       VariableDataS.Chan
496     },
497     {
498       "%y",
499       TimeYear
500     },
501     {
502       "%m",
503       TimeMonth
504     },
505     {
506       "%w",
507       TimeWeek
508     },
509     {
510       "%d",
511       TimeDay
512     },
513     {
514       "%h",
515       TimeHour
516     },
517     {
518       "%n",
519       TimeMin
520     },
521     {
522       "%s",
523       TimeSec
524     },
525 
526   };
527 
528   VSLen = (sizeof(Variable) / sizeof(*Variable));
529 
530   TMPT = localtime(&NOW);
531   strftime(TimeYear, LINELEN, "%Y", TMPT);
532   strftime(TimeMonth, LINELEN, "%m", TMPT);
533   strftime(TimeWeek, LINELEN, "%W", TMPT);
534   strftime(TimeDay, LINELEN, "%d", TMPT);
535   strftime(TimeHour, LINELEN, "%H", TMPT);
536   strftime(TimeMin, LINELEN, "%M", TMPT);
537   strftime(TimeSec, LINELEN, "%S", TMPT);
538 
539   for (Len = 1 ; *OldMessagePT != 0 ;) {
540 
541     TempPT = strchr(OldMessagePT, '%');
542     if (TempPT == NULL) {
543       Len += strlen(OldMessagePT);
544       MessagePT = realloc(NewMessagePT, Len);
545       if (MessagePT == NULL) {
546         free(NewMessagePT);
547         return(NULL);
548       }
549       NewMessagePT = MessagePT;
550       MessagePT += Index;
551       strcpy(MessagePT, OldMessagePT);
552       OldMessagePT += strlen(OldMessagePT);
553       Index += strlen(OldMessagePT);
554     }
555     else {
556 
557       if (TempPT != OldMessagePT) {
558 
559         Len += (TempPT - OldMessagePT);
560         MessagePT = realloc(NewMessagePT, Len);
561         if (MessagePT == NULL) {
562           free(NewMessagePT);
563           return(NULL);
564         }
565         NewMessagePT = MessagePT;
566         MessagePT += Index;
567         strncpy(MessagePT, OldMessagePT, (TempPT - OldMessagePT));
568         Index += (TempPT - OldMessagePT);
569         OldMessagePT = TempPT;
570 
571       }
572       VarNamePT = NULL;
573       VarDataPT = NULL;
574       for (VSIndex = 0 ; VSIndex < VSLen ; VSIndex++) {
575         if ((Variable[VSIndex].Data != NULL) && (strncasecmp(OldMessagePT, Variable[VSIndex].Name, strlen(Variable[VSIndex].Name)) == FALSE)) {
576           VarNamePT = Variable[VSIndex].Name;
577           VarDataPT = Variable[VSIndex].Data;
578           break;
579         }
580       }
581       if ((VarNamePT != NULL) && (VarDataPT != NULL)) {
582         Len += strlen(VarDataPT);
583         MessagePT = realloc(NewMessagePT, Len);
584         if (MessagePT == NULL) {
585           free(NewMessagePT);
586           return(NULL);
587         }
588         NewMessagePT = MessagePT;
589         MessagePT += Index;
590         strcpy(MessagePT, VarDataPT);
591         OldMessagePT += strlen(VarNamePT);
592         Index += strlen(VarDataPT);
593       }
594       else {
595         Len += strlen(OldMessagePT);
596         MessagePT = realloc(NewMessagePT, Len);
597         if (MessagePT == NULL) {
598           free(NewMessagePT);
599           return(NULL);
600         }
601         NewMessagePT = MessagePT;
602         MessagePT += Index;
603         strcpy(MessagePT, OldMessagePT);
604         OldMessagePT += strlen(OldMessagePT);
605         Index += strlen(OldMessagePT);
606       }
607     }
608   }
609 
610   VariableDataS.ConnS = NULL;
611   VariableDataS.ChanS = NULL;
612   VariableDataS.Chan = NULL;
613   VariableDataS.Nick = NULL;
614   VariableDataS.User = NULL;
615   VariableDataS.Host = NULL;
616   VariableDataS.NUH = NULL;
617   VariableDataS.Command = NULL;
618   VariableDataS.Param = NULL;
619   VariableDataS.Message = NULL;
620 
621   return(NewMessagePT);
622 
623 }
624