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