2dc5daaafc497eb11a756ab6b72e3a09b5bfc112
[ircu2.10.12-pk.git] / ircd / motd.c
1 /*
2  * IRC - Internet Relay Chat, ircd/motd.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
6  *
7  * See file AUTHORS in IRC package for additional names of
8  * the programmers.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 1, or (at your option)
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  * $Id$
25  */
26 #include "motd.h"
27 #include "class.h"
28 #include "client.h"
29 #include "fileio.h"
30 #include "ircd.h"
31 #include "ircd_alloc.h"
32 #include "ircd_features.h"
33 #include "ircd_reply.h"
34 #include "ircd_string.h"
35 #include "match.h"
36 #include "msg.h"
37 #include "numeric.h"
38 #include "numnicks.h"
39 #include "s_conf.h"
40 #include "s_debug.h"
41 #include "s_user.h"
42 #include "send.h"
43
44 #include <assert.h>
45 #include <errno.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <sys/stat.h>
49
50 static struct {
51   struct Motd*  local;
52   struct Motd*  remote;
53   struct Motd*  other;
54   struct Motd*  freelist;
55 } MotdList;
56
57 /* Create a struct Motd and initialize it */
58 static struct Motd *
59 motd_create(const char *hostmask, const char *path, int maxcount)
60 {
61   struct Motd* tmp;
62   int type = MOTD_UNIVERSAL;
63   const char* s;
64
65   assert(0 != path);
66
67   if (hostmask) { /* figure out if it's a class or hostmask */
68     type = MOTD_CLASS; /* all digits, convert to class */
69
70     for (s = hostmask; *s; s++)
71       if (!IsDigit(*s)) { /* not a digit, not a class... */
72         type = MOTD_HOSTMASK;
73         break;
74       }
75   }
76
77   /* allocate memory and initialize the structure */
78   if (MotdList.freelist) {
79     tmp = MotdList.freelist;
80     MotdList.freelist = tmp->next;
81   } else
82     tmp = (struct Motd *)MyMalloc(sizeof(struct Motd));
83
84   tmp->next = 0;
85   tmp->type = type;
86
87   switch (type) {
88   case MOTD_HOSTMASK:
89     DupString(tmp->id.hostmask, hostmask);
90     break;
91
92   case MOTD_CLASS:
93     tmp->id.class = atoi(hostmask);
94     break;
95   }
96
97   DupString(tmp->path, path);
98   tmp->maxcount = maxcount;
99   tmp->cache = 0;
100
101   return tmp;
102 }
103
104 /* This function reads a motd out of a file (if needed) and caches it */
105 static struct MotdCache *
106 motd_cache(struct Motd *motd)
107 {
108   FBFILE*               file;
109   struct MotdCache*     cache;
110   struct stat           sb;
111   char                  line[MOTD_LINESIZE + 2]; /* \r\n */
112   char*                 tmp;
113   int                   i;
114
115   assert(0 != motd);
116   assert(0 != motd->path);
117
118   if (motd->cache)
119     return motd->cache;
120
121   /* gotta read in the file, now */
122   if (!(file = fbopen(motd->path, "r"))) {
123     Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motd->path,
124            strerror(errno)));
125     return 0;
126   }
127
128   /* need the file's modification time */
129   if (-1 == fbstat(&sb, file)) {
130     fbclose(file);
131     return 0;
132   }
133
134   /* Ok, allocate a structure; we'll realloc later to trim memory */
135   cache = (struct MotdCache *)MyMalloc(sizeof(struct MotdCache) +
136                                        (MOTD_LINESIZE * (MOTD_MAXLINES - 1)));
137
138   cache->modtime = *localtime((time_t *) &sb.st_mtime); /* store modtime */
139
140   cache->count = 0;
141   while (cache->count < motd->maxcount && fbgets(line, sizeof(line), file)) {
142     /* copy over line, stopping when we overflow or hit line end */
143     for (tmp = line, i = 0;
144          i < (MOTD_LINESIZE - 1) && *tmp && *tmp != '\r' && *tmp != '\n';
145          tmp++, i++)
146       cache->motd[cache->count][i] = *tmp;
147     cache->motd[cache->count][i] = '\0';
148
149     cache->count++;
150   }
151
152   fbclose(file); /* close the file */
153
154   /* trim memory usage a little */
155   motd->cache = (struct MotdCache *)MyRealloc(cache, sizeof(struct MotdCache) +
156                                               (MOTD_LINESIZE *
157                                                (cache->count - 1)));
158
159   return motd->cache;
160 }
161
162 static void
163 motd_decache(struct Motd *motd)
164 {
165   struct MotdCache* cache;
166
167   assert(0 != motd);
168
169   if (!(cache = motd->cache)) /* we can be called for records with no cache */
170     return;
171
172   motd->cache = 0; /* zero the cache */
173
174   MyFree(cache); /* very simple for a reason... */
175 }
176
177 /* This function destroys a struct Motd, destroying the cache if needed */
178 static void
179 motd_destroy(struct Motd *motd)
180 {
181   assert(0 != motd);
182
183   MyFree(motd->path); /* we always must have a path */
184   if (motd->type == MOTD_HOSTMASK) /* free a host mask if any */
185     MyFree(motd->id.hostmask);
186   if (motd->cache) /* drop the cache */
187     motd_decache(motd);
188
189   motd->next = MotdList.freelist;
190   MotdList.freelist = motd;
191 }
192
193 /* We use this routine to look up the struct Motd to send to any given
194  * user.
195  */
196 static struct Motd *
197 motd_lookup(struct Client *cptr)
198 {
199   struct Motd *ptr;
200   int class = -1;
201
202   assert(0 != cptr);
203
204   if (!MyUser(cptr)) /* not my user, always return remote motd */
205     return MotdList.remote;
206
207   class = get_client_class(cptr);
208
209   /* check the T-lines first */
210   for (ptr = MotdList.other; ptr; ptr = ptr->next) {
211     if (ptr->type == MOTD_CLASS && ptr->id.class == class)
212       return ptr;
213     else if (ptr->type == MOTD_HOSTMASK &&
214              !match(ptr->id.hostmask, cli_sockhost(cptr)))
215       return ptr;
216   }
217
218   return MotdList.local; /* Ok, return the default motd */
219 }
220
221 /* Here is a routine that takes a MotdCache and sends it to a user */
222 static int
223 motd_forward(struct Client *cptr, struct MotdCache *cache)
224 {
225   int i;
226
227   assert(0 != cptr);
228
229   if (!cache) /* no motd to send */
230     return send_reply(cptr, ERR_NOMOTD);
231
232   /* send the motd */
233   send_reply(cptr, RPL_MOTDSTART, cli_name(&me));
234   send_reply(cptr, SND_EXPLICIT | RPL_MOTD, ":- %d-%d-%d %d:%02d",
235              cache->modtime.tm_year + 1900, cache->modtime.tm_mon + 1,
236              cache->modtime.tm_mday, cache->modtime.tm_hour,
237              cache->modtime.tm_min);
238
239   for (i = 0; i < cache->count; i++)
240     send_reply(cptr, RPL_MOTD, cache->motd[i]);
241
242   return send_reply(cptr, RPL_ENDOFMOTD); /* end */
243 }
244
245 /* This routine is used to send the MOTD off to a user. */
246 int
247 motd_send(struct Client* cptr)
248 {
249   assert(0 != cptr);
250
251   return motd_forward(cptr, motd_cache(motd_lookup(cptr)));
252 }
253
254 /* This routine sends the MOTD or something to newly-registered users. */
255 void
256 motd_signon(struct Client* cptr)
257 {
258   struct MotdCache *cache;
259
260   cache = motd_cache(motd_lookup(cptr));
261
262   if (!feature_bool(FEAT_NODEFAULTMOTD))
263     motd_forward(cptr, cache);
264   else {
265     send_reply(cptr, RPL_MOTDSTART, cli_name(&me));
266     send_reply(cptr, SND_EXPLICIT | RPL_MOTD, ":\002Type /MOTD to read the "
267                "AUP before continuing using this service.\002");
268     send_reply(cptr, SND_EXPLICIT | RPL_MOTD, ":The message of the day was "
269                "last changed: %d-%d-%d %d:%d", cache->modtime.tm_year + 1900,
270                cache->modtime.tm_mon + 1, cache->modtime.tm_mday,
271                cache->modtime.tm_hour, cache->modtime.tm_min);
272     send_reply(cptr, RPL_ENDOFMOTD);
273   }
274 }
275
276 /* motd_recache causes all the MOTD caches to be cleared */
277 void
278 motd_recache(void)
279 {
280   struct Motd* tmp;
281
282   motd_decache(MotdList.local); /* decache local and remote MOTDs */
283   motd_decache(MotdList.remote);
284
285   for (tmp = MotdList.other; tmp; tmp = tmp->next) /* now all the others */
286     motd_decache(tmp);
287
288   /* now recache local and remote MOTDs */
289   motd_cache(MotdList.local);
290   motd_cache(MotdList.remote);
291 }
292
293 /* motd_init initializes the MOTD routines, including reading the
294  * ircd.motd and remote.motd files into cache
295  */
296 void
297 motd_init(void)
298 {
299   MotdList.local = motd_create(0, feature_str(FEAT_MPATH), MOTD_MAXLINES);
300   motd_cache(MotdList.local); /* init local and cache it */
301
302   MotdList.remote = motd_create(0, feature_str(FEAT_RPATH), MOTD_MAXREMOTE);
303   motd_cache(MotdList.remote); /* init remote and cache it */
304
305   MotdList.other = 0; /* no T-lines processed yet */
306 }
307
308 /* This routine adds a MOTD */
309 void
310 motd_add(const char *hostmask, const char *path)
311 {
312   struct Motd *tmp;
313
314   tmp = motd_create(hostmask, path, MOTD_MAXLINES); /* create the motd */
315
316   tmp->next = MotdList.other; /* link it into the list */
317   MotdList.other = tmp;
318 }
319
320 /* This routine clears the list of MOTDs */
321 void
322 motd_clear(void)
323 {
324   struct Motd *ptr, *next;
325
326   motd_decache(MotdList.local); /* decache local and remote MOTDs */
327   motd_decache(MotdList.remote);
328
329   if (MotdList.other) /* destroy other MOTDs */
330     for (ptr = MotdList.other; ptr; ptr = next) {
331       next = ptr->next;
332       motd_destroy(ptr);
333     }
334
335   MotdList.other = 0;
336
337   /* now recache local and remote MOTDs */
338   motd_cache(MotdList.local);
339   motd_cache(MotdList.remote);
340 }
341
342 /* This is called to report T-lines */
343 void
344 motd_report(struct Client *to)
345 {
346   struct Motd *ptr;
347
348   for (ptr = MotdList.other; ptr; ptr = ptr->next) {
349     if (ptr->type == MOTD_CLASS) /* class requires special handling */
350       send_reply(to, SND_EXPLICIT | RPL_STATSTLINE, "T %d %s", ptr->id.class,
351                  ptr->path);
352     else if (ptr->type == MOTD_HOSTMASK)
353       send_reply(to, RPL_STATSTLINE, 'T', ptr->id.hostmask, ptr->path);
354   }
355 }