/[svn]/ircd-hybrid/branches/8.1.x/src/motd.c
ViewVC logotype

Contents of /ircd-hybrid/branches/8.1.x/src/motd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2319 - (show annotations)
Sat Jun 22 20:53:35 2013 UTC (9 years, 1 month ago) by michael
File MIME type: text/x-chdr
File size: 13396 byte(s)
- motd.c: fixed possible core with "STATS z" as reported by Fawkes

1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 *
4 * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
5 * Copyright (C) 2013 by the Hybrid Development Team.
6 *
7 * This program is free 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 2 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
20 * USA
21 */
22
23 /*! \file motd.c
24 * \brief Message-of-the-day manipulation implementation.
25 * \version $Id$
26 */
27
28 #include "stdinc.h"
29 #include "list.h"
30 #include "ircd.h"
31 #include "conf.h"
32 #include "send.h"
33 #include "s_serv.h"
34 #include "numeric.h"
35 #include "client.h"
36 #include "irc_string.h"
37 #include "memory.h"
38 #include "log.h"
39 #include "motd.h"
40 #include "hostmask.h"
41
42
43 /** Global list of messages of the day. */
44 static struct
45 {
46 struct Motd *local; /**< Local MOTD. */
47 struct Motd *remote; /**< Remote MOTD. */
48 dlink_list other; /**< MOTDs specified in configuration file. */
49 dlink_list cachelist; /**< List of MotdCache entries. */
50 } MotdList;
51
52
53 /*! \brief Create a struct Motd and initialize it.
54 * \param hostmask Hostmask (or connection class name) to filter on.
55 * \param path Path to MOTD file.
56 */
57 static struct Motd *
58 motd_create(const char *hostmask, const char *path)
59 {
60 struct Motd *tmp = MyMalloc(sizeof(struct Motd));
61
62 if (EmptyString(hostmask))
63 tmp->type = MOTD_UNIVERSAL;
64 else if (class_find(hostmask, 1))
65 tmp->type = MOTD_CLASS;
66 else
67 {
68 switch (parse_netmask(hostmask, &tmp->address, &tmp->addrbits))
69 {
70 case HM_IPV4:
71 tmp->type = MOTD_IPMASKV4;
72 break;
73 #ifdef IPV6
74 case HM_IPV6:
75 tmp->type = MOTD_IPMASKV6;
76 break;
77 #endif
78 default: /* HM_HOST */
79 tmp->type = MOTD_HOSTMASK;
80 break;
81 }
82 }
83
84 if (hostmask != NULL)
85 tmp->hostmask = xstrdup(hostmask);
86
87 tmp->path = xstrdup(path);
88 tmp->maxcount = MOTD_MAXLINES;
89
90 return tmp;
91 }
92
93 /*! brief\ This function reads a motd out of a file (if needed) and caches it.
94 * If a matching cache entry already exists, reuse it. Otherwise,
95 * allocate and populate a new MotdCache for it.
96 * \param motd Specification for MOTD file.
97 * \return Matching MotdCache entry.
98 */
99 static struct MotdCache *
100 motd_cache(struct Motd *motd)
101 {
102 FILE *file = NULL;
103 struct MotdCache *cache = NULL;
104 struct stat sb;
105 char line[MOTD_LINESIZE + 2]; /* \r\n */
106 char *tmp = NULL;
107 unsigned int i = 0;
108 dlink_node *ptr = NULL;
109
110 assert(motd);
111 assert(motd->path);
112
113 if (motd->cache)
114 return motd->cache;
115
116 /* try to find it in the list of cached files... */
117 DLINK_FOREACH(ptr, MotdList.cachelist.head)
118 {
119 cache = ptr->data;
120
121 if (!strcmp(cache->path, motd->path) && cache->maxcount == motd->maxcount)
122 {
123 cache->ref++; /* increase reference count... */
124 motd->cache = cache; /* remember cache... */
125 return motd->cache; /* return it */
126 }
127 }
128
129 /* need the file's modification time */
130 if (stat(motd->path, &sb) == -1)
131 {
132 ilog(LOG_TYPE_IRCD, "Couldn't stat \"%s\": %s", motd->path,
133 strerror(errno));
134 return 0;
135 }
136
137 /* gotta read in the file, now */
138 if ((file = fopen(motd->path, "r")) == NULL)
139 {
140 ilog(LOG_TYPE_IRCD, "Couldn't open \"%s\": %s", motd->path,
141 strerror(errno));
142 return 0;
143 }
144
145 /* Ok, allocate a structure; we'll realloc later to trim memory */
146 cache = MyMalloc(sizeof(struct MotdCache) + (MOTD_LINESIZE * (MOTD_MAXLINES - 1)));
147 cache->ref = 1;
148 cache->path = xstrdup(motd->path);
149 cache->maxcount = motd->maxcount;
150 cache->modtime = *localtime((time_t *)&sb.st_mtime); /* store modtime */
151
152 while (cache->count < cache->maxcount && fgets(line, sizeof(line), file))
153 {
154 /* copy over line, stopping when we overflow or hit line end */
155 for (tmp = line, i = 0; i < (MOTD_LINESIZE - 1) && *tmp && *tmp != '\r' && *tmp != '\n'; ++tmp, ++i)
156 cache->motd[cache->count][i] = *tmp;
157 cache->motd[cache->count][i] = '\0';
158
159 cache->count++;
160 }
161
162 fclose(file); /* close the file */
163
164 /* trim memory usage a little */
165 motd->cache = MyMalloc(sizeof(struct MotdCache) +
166 (MOTD_LINESIZE * (cache->count - 1)));
167 memcpy(motd->cache, cache, sizeof(struct MotdCache) +
168 (MOTD_LINESIZE * (cache->count - 1)));
169 MyFree(cache);
170
171 /* now link it in... */
172 dlinkAdd(motd->cache, &motd->cache->node, &MotdList.cachelist);
173
174 return motd->cache;
175 }
176
177 /*! \brief Clear and dereference the Motd::cache element of \a motd.
178 * If the MotdCache::ref count goes to zero, free it.
179 * \param motd MOTD to uncache.
180 */
181 static void
182 motd_decache(struct Motd *motd)
183 {
184 struct MotdCache *cache = NULL;
185
186 assert(motd);
187
188 if ((cache = motd->cache) == NULL) /* we can be called for records with no cache */
189 return;
190
191 motd->cache = NULL; /* zero the cache */
192
193 if (--cache->ref == 0) /* reduce reference count... */
194 {
195 dlinkDelete(&cache->node, &MotdList.cachelist);
196 MyFree(cache->path); /* free path info... */
197 MyFree(cache); /* very simple for a reason... */
198 }
199 }
200
201 /*! \brief Deallocate a MOTD structure.
202 * If it has cached content, uncache it.
203 * \param motd MOTD to destroy.
204 */
205 static void
206 motd_destroy(struct Motd *motd)
207 {
208 assert(motd);
209
210 if (motd->cache) /* drop the cache */
211 motd_decache(motd);
212
213 MyFree(motd->path); /* we always must have a path */
214 MyFree(motd->hostmask);
215 MyFree(motd);
216 }
217
218 /*! \brief Find the first matching MOTD block for a user.
219 * If the user is remote, always use remote MOTD.
220 * Otherwise, if there is a hostmask- or class-based MOTD that matches
221 * the user, use it.
222 * Otherwise, use the local MOTD.
223 * \param client_p Client to find MOTD for.
224 * \return Pointer to first matching MOTD for the client.
225 */
226 static struct Motd *
227 motd_lookup(struct Client *client_p)
228 {
229 dlink_node *ptr = NULL;
230 const struct ClassItem *class = NULL;
231
232 assert(client_p);
233
234 if (!MyClient(client_p)) /* not my user, always return remote motd */
235 return MotdList.remote;
236
237 class = get_class_ptr(&client_p->localClient->confs);
238 assert(class);
239
240 /* check the motd blocks first */
241 DLINK_FOREACH(ptr, MotdList.other.head)
242 {
243 struct Motd *motd = ptr->data;
244
245 switch (motd->type)
246 {
247 case MOTD_CLASS:
248 if (!match(motd->hostmask, class->name))
249 return motd;
250 break;
251 case MOTD_HOSTMASK:
252 if (!match(motd->hostmask, client_p->host))
253 return motd;
254 break;
255 case MOTD_IPMASKV4:
256 if (client_p->localClient->aftype == AF_INET)
257 if (match_ipv4(&client_p->localClient->ip, &motd->address, motd->addrbits))
258 return motd;
259 break;
260 #ifdef IPV6
261 case MOTD_IPMASKV6:
262 if (client_p->localClient->aftype == AF_INET6)
263 if (match_ipv6(&client_p->localClient->ip, &motd->address, motd->addrbits))
264 return motd;
265 break;
266 #endif
267 default: break;
268 }
269 }
270
271 return MotdList.local; /* Ok, return the default motd */
272 }
273
274 /*! \brief Send the content of a MotdCache to a user.
275 * If \a cache is NULL, simply send ERR_NOMOTD to the client.
276 * \param source_p Client to send MOTD to.
277 * \param cache MOTD body to send to client.
278 */
279 static void
280 motd_forward(struct Client *source_p, const struct MotdCache *cache)
281 {
282 unsigned int i = 0;
283 const char *from = ID_or_name(&me, source_p->from);
284 const char *to = ID_or_name(source_p, source_p->from);
285
286 assert(source_p);
287
288 if (!cache) /* no motd to send */
289 {
290 sendto_one(source_p, form_str(ERR_NOMOTD), from, to);
291 return;
292 }
293
294 /* send the motd */
295 sendto_one(source_p, form_str(RPL_MOTDSTART),
296 from, to, me.name);
297
298 for (; i < cache->count; ++i)
299 sendto_one(source_p, form_str(RPL_MOTD),
300 from, to, cache->motd[i]);
301 sendto_one(source_p, form_str(RPL_ENDOFMOTD), from, to);
302 }
303
304 /*! \brief Find the MOTD for a client and send it.
305 * \param client_p Client being greeted.
306 */
307 void
308 motd_send(struct Client *client_p)
309 {
310 assert(client_p);
311
312 motd_forward(client_p, motd_cache(motd_lookup(client_p)));
313 }
314
315 /*! \brief Send the signon MOTD to a user.
316 * If FEAT_NODEFAULTMOTD is true and a matching MOTD exists for the
317 * user, direct the client to type /MOTD to read it. Otherwise, call
318 * motd_forward() for the user.
319 * \param source_p Client that has just connected.
320 */
321 void
322 motd_signon(struct Client *source_p)
323 {
324 const struct MotdCache *cache = motd_cache(motd_lookup(source_p));
325
326 if (!ConfigFileEntry.short_motd || !cache)
327 motd_forward(source_p, cache);
328 else
329 {
330 sendto_one(source_p, ":%s NOTICE %s :*** Notice -- motd was last changed at %d/%d/%d %d:%02d",
331 me.name, source_p->name, cache->modtime.tm_year + 1900,
332 cache->modtime.tm_mon + 1,
333 cache->modtime.tm_mday,
334 cache->modtime.tm_hour,
335 cache->modtime.tm_min);
336 sendto_one(source_p,
337 ":%s NOTICE %s :*** Notice -- Please read the motd if you haven't "
338 "read it", me.name, source_p->name);
339 sendto_one(source_p, form_str(RPL_MOTDSTART),
340 me.name, source_p->name, me.name);
341 sendto_one(source_p, form_str(RPL_MOTD),
342 me.name, source_p->name,
343 "*** This is the short motd ***");
344 sendto_one(source_p, form_str(RPL_ENDOFMOTD),
345 me.name, source_p->name);
346 }
347 }
348
349 /*! \brief Clear all cached MOTD bodies.
350 * The local and remote MOTDs are re-cached immediately.
351 */
352 void
353 motd_recache(void)
354 {
355 dlink_node *ptr = NULL;
356
357 motd_decache(MotdList.local); /* decache local and remote MOTDs */
358 motd_decache(MotdList.remote);
359
360 DLINK_FOREACH(ptr, MotdList.other.head) /* now all the others */
361 motd_decache(ptr->data);
362
363 /* now recache local and remote MOTDs */
364 motd_cache(MotdList.local);
365 motd_cache(MotdList.remote);
366 }
367
368 /*! \brief Re-cache the local and remote MOTDs.
369 * If they already exist, they are deallocated first.
370 */
371 void
372 motd_init(void)
373 {
374 if (MotdList.local) /* destroy old local... */
375 motd_destroy(MotdList.local);
376
377 MotdList.local = motd_create(NULL, MPATH);
378 motd_cache(MotdList.local); /* init local and cache it */
379
380 if (MotdList.remote) /* destroy old remote... */
381 motd_destroy(MotdList.remote);
382
383 MotdList.remote = motd_create(NULL, MPATH);
384 motd_cache(MotdList.remote); /* init remote and cache it */
385 }
386
387 /* \brief Add a new MOTD.
388 * \param hostmask Hostmask (or connection class name) to send this to.
389 * \param path Pathname of file to send.
390 */
391 void
392 motd_add(const char *hostmask, const char *path)
393 {
394 struct Motd *motd = motd_create(hostmask, path); /* create the motd */
395
396 dlinkAdd(motd, &motd->node, &MotdList.other);
397 }
398
399 /*! \brief Clear out all MOTDs.
400 * Compared to motd_recache(), this destroys all hostmask- or
401 * class-based MOTDs rather than simply uncaching them.
402 * Re-cache the local and remote MOTDs.
403 */
404 void
405 motd_clear(void)
406 {
407 dlink_node *ptr = NULL, *ptr_next = NULL;
408
409 motd_decache(MotdList.local); /* decache local and remote MOTDs */
410 motd_decache(MotdList.remote);
411
412 DLINK_FOREACH_SAFE(ptr, ptr_next, MotdList.other.head) /* destroy other MOTDs */
413 {
414 dlinkDelete(ptr, &MotdList.other);
415 motd_destroy(ptr->data);
416 }
417
418 /* now recache local and remote MOTDs */
419 motd_cache(MotdList.local);
420 motd_cache(MotdList.remote);
421 }
422
423 /*! \brief Report list of non-default MOTDs.
424 * \param source_p Client requesting statistics.
425 */
426 void
427 motd_report(struct Client *source_p)
428 {
429 const dlink_node *ptr = NULL;
430
431 DLINK_FOREACH(ptr, MotdList.other.head)
432 {
433 const struct Motd *motd = ptr->data;
434
435 sendto_one(source_p, form_str(RPL_STATSTLINE),
436 me.name, source_p->name,
437 motd->hostmask, motd->path);
438 }
439 }
440
441 /*! \brief Report MOTD memory usage to a client.
442 * \param source_p Client requesting memory usage.
443 */
444 void
445 motd_memory_count(struct Client *source_p)
446 {
447 const dlink_node *ptr = NULL;
448 unsigned int mt = 0; /* motd count */
449 unsigned int mtc = 0; /* motd cache count */
450 size_t mtm = 0; /* memory consumed by motd */
451 size_t mtcm = 0; /* memory consumed by motd cache */
452
453 if (MotdList.local)
454 {
455 mt++;
456 mtm += sizeof(struct Motd);
457 mtm += MotdList.local->path ? (strlen(MotdList.local->path) + 1) : 0;
458 }
459
460 if (MotdList.remote)
461 {
462 mt++;
463 mtm += sizeof(struct Motd);
464 mtm += MotdList.remote->path ? (strlen(MotdList.remote->path) + 1) : 0;
465 }
466
467 DLINK_FOREACH(ptr, MotdList.other.head)
468 {
469 const struct Motd *motd = ptr->data;
470
471 mt++;
472 mtm += sizeof(struct Motd);
473 mtm += motd->path ? (strlen(motd->path) + 1) : 0;
474 }
475
476 DLINK_FOREACH(ptr, MotdList.cachelist.head)
477 {
478 const struct MotdCache *cache = ptr->data;
479
480 mtc++;
481 mtcm += sizeof(struct MotdCache) + (MOTD_LINESIZE * (cache->count - 1));
482 }
483
484 sendto_one(source_p, ":%s %d %s z :Motds %u(%u) Cache %u(%u)",
485 me.name, RPL_STATSDEBUG, source_p->name,
486 mt, mtm, mtc, mtcm);
487 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28