ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/motd.c
Revision: 3319
Committed: Tue Apr 15 15:41:50 2014 UTC (9 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 13011 byte(s)
Log Message:
- Removed useless header includes

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

Properties

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