ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/motd.c
Revision: 5347
Committed: Sun Jan 11 12:42:20 2015 UTC (10 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 12971 byte(s)
Log Message:
- Update copyright years

File Contents

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

Properties

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