ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/motd.c
Revision: 9608
Committed: Sun Sep 6 06:33:46 2020 UTC (4 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 12443 byte(s)
Log Message:
- Stylistic changes

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

Properties

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