1 /* $Id: chanlog.c 341 2006-01-12 21:56:48Z tsaviran $
2  * -------------------------------------------------------
3  * Copyright (C) 2002-2006 Tommi Saviranta <wnd@iki.fi>
4  *	(C) 2002 Lee Hardy <lee@leeh.co.uk>
5  * -------------------------------------------------------
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 #include <config.h>
19 #endif /* ifdef HAVE_CONFIG_H */
20 
21 #ifdef CHANLOG
22 
23 #include "chanlog.h"
24 #include "llist.h"
25 #include "common.h"
26 #include "channels.h"
27 #include "client.h"
28 #include "miau.h"
29 #include "tools.h"
30 #include "messages.h"
31 #include "etc.h"
32 #include "log.h"
33 
34 #include <stdio.h>
35 #include <stdarg.h>
36 #include <string.h>
37 
38 
39 
40 int		global_logtype;
41 llist_list	chanlog_list;
42 
43 
44 
45 /*
46  * Creates a logging entry in log_list.
47  */
48 void
chanlog_add_rule(char * channels,char * file,int type)49 chanlog_add_rule(char *channels, char *file, int type)
50 {
51 	char	*chan;
52 	int	multi = 0;
53 
54 	/* Nothing to log. */
55 	if (channels == NULL || channels[0] == '\0') {
56 		return;
57 	}
58 
59 	/*
60 	 * The "global" logentry, mark the logtype and then open logfiles
61 	 * for channels we dont have specific entries for.
62 	 */
63 	if (xstrcmp(channels, "*") == 0) {
64 		global_logtype = type;
65 		return;
66 	}
67 
68 	/*
69 	 * If we're doing multi channels, mark as such so we don't use a
70 	 * specified logfile, revert to #channel.log.
71 	 */
72 	if (strchr(channels, ',') != NULL) {
73 		multi = 1;
74 	}
75 
76 	for (chan = strtok(channels, ","); chan != NULL;
77 			chan = strtok(NULL, ",")) {
78 		llist_node *node;
79 		struct chanlogentry *logptr;
80 
81 		/* Check we haven't already done this channel */
82 		for (node = chanlog_list.head; node != NULL;
83 				node = node->next) {
84 			logptr = (struct chanlogentry *) node->data;
85 			if (xstrcasecmp(chan, logptr->channel) == 0) {
86 				return;
87 			}
88 		}
89 
90 		logptr = (struct chanlogentry *)
91 			xcalloc(sizeof(struct chanlogentry), 1);
92 		node = llist_create(logptr);
93 		llist_add(node, &chanlog_list);
94 
95 		logptr->channel = xstrdup(chan);
96 		logptr->type = type;
97 
98 		/*
99 		 * If we were not given a logfilename or we're creating more
100 		 * than one entry at once, create a logfilename.
101 		 */
102 		if (file == NULL || multi) {
103 			char *file;
104 			size_t size;
105 			/* termination and validity guaranteed */
106 			size = strlen(LOGDIR) + strlen(chan)
107 				+ strlen(cfg.logsuffix) + 3;
108 			file = (char *) xmalloc(size);
109 			snprintf(file, size, LOGDIR"/%s%s",
110 					chan, cfg.logsuffix);
111 			file[size - 1] = '\0';
112 			logptr->filename = file;
113 		} else { /* basically "if (file != NULL)" */
114 			size_t size;
115 			/* If filename is relative, add LOGDIR. */
116 			/* termination and validity guaranteed */
117 			size = strlen(LOGDIR) + strlen(file) + 3;
118 			logptr->filename = (char *) xmalloc(size);
119 			snprintf(logptr->filename, size, LOGDIR"/%s",
120 					file);
121 			logptr->filename[size - 1] = '\0';
122 		}
123 	}
124 } /* void chanlog_add_rule(char *channels, char *file, int type) */
125 
126 
127 
128 /*
129  * Delete all logging rules.
130  */
131 void
chanlog_del_rules(void)132 chanlog_del_rules(void)
133 {
134 	LLIST_WALK_H(chanlog_list.head, struct chanlogentry *);
135 		xfree(data->channel);
136 		xfree(data->filename);
137 		xfree(data);
138 
139 		llist_delete(node, &chanlog_list);
140 	LLIST_WALK_F;
141 
142 } /* void chanlog_del_rules(void) */
143 
144 
145 
146 /*
147  * Open a logfile.
148  */
149 void
chanlog_open(channel_type * channel)150 chanlog_open(channel_type *channel)
151 {
152 	/*
153 	 * Should have no need for this.
154 	 *
155 	if (channel->log->logfile != NULL) {
156 		return;
157 	}
158 	*/
159 
160 	/* See if a rule applies directly to this channel. */
161 	LLIST_WALK_H(chanlog_list.head, struct chanlogentry *);
162 		if (xstrcasecmp(channel->simple_name, data->channel) == 0) {
163 			channel->log = (struct channel_log *)
164 				xcalloc(sizeof(struct channel_log), 1);
165 			/* filename termination and validity guaranteed */
166 			channel->log->file = fopen(data->filename, "a");
167 
168 			if (channel->log->file == NULL) {
169 				log_cannot_write(data->filename);
170 				return;
171 			}
172 
173 			channel->log->type = data->type;
174 			break;
175 		}
176 	LLIST_WALK_F;
177 
178 	/* Didn't find a direct match. Use global logtype. */
179 	if (channel->log == NULL && global_logtype) {
180 		size_t plen;
181 		char *p;
182 		char *lowchan;
183 
184 		/* convert simple channel name to lowercase */
185 		lowchan = xstrdup(channel->simple_name);
186 		lowcase(lowchan);
187 
188 		channel->log = (struct channel_log *)
189 			xcalloc(sizeof(struct channel_log), 1);
190 
191 		/* termination and validity guaranteed */
192 		plen = strlen(LOGDIR) + strlen(lowchan)
193 			+ strlen(cfg.logsuffix) + 3;
194 		p = (char *) xmalloc(plen);
195 		snprintf(p, plen, LOGDIR"/%s%s", lowchan, cfg.logsuffix);
196 		p[plen - 1] = '\0';
197 		channel->log->file = fopen(p, "a");
198 		xfree(p);
199 		xfree(lowchan);
200 
201 		if (channel->log->file == NULL) {
202 			return;
203 		}
204 
205 		channel->log->type = global_logtype;
206 	}
207 
208 	/* Still unaware where to log ? */
209 	if (channel->log == NULL) {
210 		return;
211 	}
212 
213 	/* ...and start logging. */
214 	chanlog_write_entry(channel, LOGM_LOGOPEN,
215 			get_timestamp(NULL, TIMESTAMP_LONG));
216 } /* void chanlog_open(channel_type *channel) */
217 
218 
219 
220 /*
221  * Close logfile and free resources.
222  */
223 void
chanlog_close(channel_type * channel)224 chanlog_close(channel_type *channel)
225 {
226 	if (channel->log != NULL) {
227 		chanlog_write_entry(channel, LOGM_LOGCLOSE,
228 				get_timestamp(NULL, TIMESTAMP_LONG));
229 		if (channel->log->file != NULL) {
230 		fclose(channel->log->file);
231 		}
232 
233 		FREE(channel->log);
234 	}
235 } /* void chanlog_close(channel_type *channel) */
236 
237 
238 
239 /*
240  * Writes a logging entry to the logfile.
241  */
242 void
chanlog_write_entry(channel_type * chptr,char * format,...)243 chanlog_write_entry(channel_type *chptr, char *format, ...)
244 {
245 	char	buffer[1024];
246 	va_list	va;
247 
248 	/* No logfile for this channel. */
249 	if (chptr->log == NULL || chptr->log->file == NULL) {
250 		return;
251 	}
252 
253 	/* Probably not logging when client is attached/detached. */
254 	if ((c_clients.connected == 0 &&
255 			! (chptr->log->type & LOG_DETACHED)) ||
256 			(c_clients.connected > 0 &&
257 				! (chptr->log->type & LOG_ATTACHED))) {
258 		return;
259 	}
260 
261 	va_start(va, format);
262 	vsnprintf(buffer, 1024, format, va);
263 	va_end(va);
264 	buffer[1023] = '\0';
265 
266 	fprintf(chptr->log->file, "%s", buffer);
267 	fflush(chptr->log->file);
268 } /* void chanlog_write_entry(channel_type *chptr, char *format, ...) */
269 
270 
271 
272 /*
273  * Writes a logging entry to all matching logfiles.
274  */
275 void
chanlog_write_entry_all(int type,char * format,...)276 chanlog_write_entry_all(int type, char *format, ...)
277 {
278 	char		buffer[1024];
279 	va_list		va;
280 
281 	va_start(va, format);
282 	vsnprintf(buffer, 1024, format, va);
283 	va_end(va);
284 	buffer[1023] = '\0';
285 
286 	LLIST_WALK_H(active_channels.head, channel_type *);
287 		/*
288 		 * We could have it this way...
289 		 *
290 		if (data->log == NULL || data->log->file == NULL) {
291 			LLIST_WALK_CONTINUE;
292 		}
293 
294 		if (data->log->type & type) {
295 			log_write_entry(data, "%s", buffer);
296 		}
297 		 *
298 		 * ...but this should compile shorter. ;-)
299 		 */
300 		if (! (data->log == NULL || data->log->file == NULL)
301 				&& data->log->type & type) {
302 			chanlog_write_entry(data, "%s", buffer);
303 		}
304 	LLIST_WALK_F;
305 } /* void chanlog_write_entry_all(int type, char *format, ...) */
306 
307 
308 
309 int
chanlog_has_log(const channel_type * chan,int type)310 chanlog_has_log(const channel_type *chan, int type)
311 {
312 	if (chan == NULL || chan->log == NULL) {
313 		return 0;
314 	} else {
315 		return (chan->log->type & type) == type;
316 	}
317 } /* int chanlog_has_log(const channel_type *chan, int type) */
318 
319 
320 
321 #endif /* ifdef CHANLOG */
322