ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/motd.c
Revision: 2164
Committed: Sun Jun 2 19:42:58 2013 UTC (12 years, 2 months ago) by michael
Content type: text/x-csrc
File size: 12528 byte(s)
Log Message:
- motd.h, motd.c: minor cleanups. use 'unsigned' whenever possible

File Contents

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

Properties

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