ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/motd.c
Revision: 7032
Committed: Sun Jan 3 14:34:39 2016 UTC (9 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 12808 byte(s)
Log Message:
- Renamed MyCalloc to xcalloc

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

Properties

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