summaryrefslogtreecommitdiff
path: root/src/motd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/motd.c')
-rw-r--r--src/motd.c561
1 files changed, 367 insertions, 194 deletions
diff --git a/src/motd.c b/src/motd.c
index 62293e4..ff93806 100644
--- a/src/motd.c
+++ b/src/motd.c
@@ -1,8 +1,8 @@
/*
* ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
- * motd.c: Message of the day functions.
*
- * Copyright (C) 2002 by the past and present ircd coders, and others.
+ * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
+ * Copyright (C) 2013 by the Hybrid Development Team.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,265 +18,438 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
- *
- * $Id$
+ */
+
+/*! \file motd.c
+ * \brief Message-of-the-day manipulation implementation.
+ * \version $Id$
*/
#include "stdinc.h"
#include "list.h"
-#include "motd.h"
#include "ircd.h"
-#include "fdlist.h"
-#include "s_bsd.h"
#include "conf.h"
#include "send.h"
+#include "s_serv.h"
#include "numeric.h"
#include "client.h"
#include "irc_string.h"
#include "memory.h"
-#include "s_serv.h"
+#include "log.h"
+#include "motd.h"
-/*
-** init_message_file
-**
-*/
-void
-init_message_file(MotdType motdType, const char *fileName, MessageFile *motd)
+
+/** Global list of messages of the day. */
+static struct
+{
+ struct Motd *local; /**< Local MOTD. */
+ struct Motd *remote; /**< Remote MOTD. */
+ dlink_list other; /**< MOTDs specified in configuration file. */
+ dlink_list cachelist; /**< List of MotdCache entries. */
+} MotdList;
+
+
+/*! \brief Create a struct Motd and initialize it.
+ * \param hostmask Hostmask (or connection class name) to filter on.
+ * \param path Path to MOTD file.
+ */
+static struct Motd *
+motd_create(const char *hostmask, const char *path)
{
- strlcpy(motd->fileName, fileName, sizeof(motd->fileName));
- motd->motdType = motdType;
- motd->contentsOfFile = NULL;
- motd->lastChangedDate[0] = '\0';
+ struct Motd *tmp = MyMalloc(sizeof(struct Motd));
+
+ if (EmptyString(hostmask))
+ tmp->type = MOTD_UNIVERSAL;
+ else if (class_find(hostmask, 1))
+ tmp->type = MOTD_CLASS;
+ else
+ tmp->type = MOTD_HOSTMASK;
+
+ if (hostmask != NULL)
+ tmp->hostmask = xstrdup(hostmask);
+
+ tmp->path = xstrdup(path);
+ tmp->maxcount = MOTD_MAXLINES;
+
+ return tmp;
}
-/*
-** send_message_file
-**
-** This function split off so a server notice could be generated on a
-** user requested motd, but not on each connecting client.
-*/
-int
-send_message_file(struct Client *source_p, MessageFile *motdToPrint)
+/*! brief\ This function reads a motd out of a file (if needed) and caches it.
+ * If a matching cache entry already exists, reuse it. Otherwise,
+ * allocate and populate a new MotdCache for it.
+ * \param motd Specification for MOTD file.
+ * \return Matching MotdCache entry.
+ */
+static struct MotdCache *
+motd_cache(struct Motd *motd)
{
- MessageFileLine *linePointer;
- MotdType motdType;
- const char *from, *to;
+ FILE *file = NULL;
+ struct MotdCache *cache = NULL;
+ struct stat sb;
+ char line[MOTD_LINESIZE + 2]; /* \r\n */
+ char *tmp = NULL;
+ int i;
+ dlink_node *ptr = NULL;
+
+ assert(motd);
+ assert(motd->path);
+
+ if (motd->cache)
+ return motd->cache;
- if (motdToPrint == NULL)
- return(-1);
+ /* try to find it in the list of cached files... */
+ DLINK_FOREACH(ptr, MotdList.cachelist.head)
+ {
+ cache = ptr->data;
+
+ if (!strcmp(cache->path, motd->path) && cache->maxcount == motd->maxcount)
+ {
+ cache->ref++; /* increase reference count... */
+ motd->cache = cache; /* remember cache... */
+ return motd->cache; /* return it */
+ }
+ }
+
+ /* gotta read in the file, now */
+ if ((file = fopen(motd->path, "r")) == NULL)
+ {
+ ilog(LOG_TYPE_IRCD, "Couldn't open \"%s\": %s", motd->path,
+ strerror(errno));
+ return 0;
+ }
- motdType = motdToPrint->motdType;
+ /* need the file's modification time */
+ if (stat(motd->path, &sb) == -1)
+ {
+ fclose(file);
+ return 0;
+ }
- from = ID_or_name(&me, source_p->from);
- to = ID_or_name(source_p, source_p->from);
+ /* Ok, allocate a structure; we'll realloc later to trim memory */
+ cache = MyMalloc(sizeof(struct MotdCache) + (MOTD_LINESIZE * (MOTD_MAXLINES - 1)));
+ cache->ref = 1;
+ cache->path = xstrdup(motd->path);
+ cache->maxcount = motd->maxcount;
+ cache->modtime = *localtime((time_t *)&sb.st_mtime); /* store modtime */
- switch (motdType)
+ while (cache->count < cache->maxcount && fgets(line, sizeof(line), file))
{
- case USER_MOTD:
- if (motdToPrint->contentsOfFile == NULL)
- sendto_one(source_p, form_str(ERR_NOMOTD), from, to);
- else
- {
- sendto_one(source_p, form_str(RPL_MOTDSTART),
- from, to, me.name);
-
- for (linePointer = motdToPrint->contentsOfFile; linePointer;
- linePointer = linePointer->next)
- {
- sendto_one(source_p, form_str(RPL_MOTD),
- from, to, linePointer->line);
- }
-
- sendto_one(source_p, form_str(RPL_ENDOFMOTD), from, to);
- }
- break;
-
- case USER_LINKS:
- if (motdToPrint->contentsOfFile != NULL)
- {
- for (linePointer = motdToPrint->contentsOfFile; linePointer;
- linePointer = linePointer->next)
- {
- sendto_one(source_p, ":%s 364 %s %s", /* XXX */
- from, to, linePointer->line);
- }
- }
- break;
-
- case ISSUPPORT:
- if (motdToPrint->contentsOfFile != NULL)
- {
- for (linePointer = motdToPrint->contentsOfFile; linePointer;
- linePointer = linePointer->next)
- {
- sendto_one(source_p, form_str(RPL_ISUPPORT),
- me.name, source_p->name, linePointer->line);
- }
- }
- break;
-
- default:
- break;
+ /* copy over line, stopping when we overflow or hit line end */
+ for (tmp = line, i = 0; i < (MOTD_LINESIZE - 1) && *tmp &&
+ *tmp != '\r' && *tmp != '\n'; tmp++, i++)
+ cache->motd[cache->count][i] = *tmp;
+ cache->motd[cache->count][i] = '\0';
+
+ cache->count++;
}
- return(0);
+ fclose(file); /* close the file */
+
+ /* trim memory usage a little */
+ motd->cache = MyMalloc(sizeof(struct MotdCache) +
+ (MOTD_LINESIZE * (cache->count - 1)));
+ memcpy(motd->cache, cache, sizeof(struct MotdCache) +
+ (MOTD_LINESIZE * (cache->count - 1)));
+ MyFree(cache);
+
+ /* now link it in... */
+ dlinkAdd(motd->cache, &motd->cache->node, &MotdList.cachelist);
+
+ return motd->cache;
}
-/*
- * read_message_file() - original From CoMSTuD, added Aug 29, 1996
- *
- * inputs - pointer to MessageFileptr
- * output -
- * side effects -
+/*! \brief Clear and dereference the Motd::cache element of \a motd.
+ * If the MotdCache::ref count goes to zero, free it.
+ * \param motd MOTD to uncache.
*/
-int
-read_message_file(MessageFile *MessageFileptr)
+static void
+motd_decache(struct Motd *motd)
{
- struct stat sb;
- struct tm *local_tm;
+ struct MotdCache *cache = NULL;
- /* used to clear out old MessageFile entries */
- MessageFileLine *mptr = 0;
- MessageFileLine *next_mptr = 0;
+ assert(motd);
- /* used to add new MessageFile entries */
- MessageFileLine *newMessageLine = 0;
- MessageFileLine *currentMessageLine = 0;
+ if ((cache = motd->cache) == NULL) /* we can be called for records with no cache */
+ return;
- char buffer[MESSAGELINELEN];
- char *p;
- FILE *file;
+ motd->cache = NULL; /* zero the cache */
- for (mptr = MessageFileptr->contentsOfFile; mptr; mptr = next_mptr)
+ if (!--cache->ref) /* reduce reference count... */
{
- next_mptr = mptr->next;
- MyFree(mptr);
+ dlinkDelete(&cache->node, &MotdList.cachelist);
+ MyFree(cache->path); /* free path info... */
+ MyFree(cache); /* very simple for a reason... */
}
+}
- MessageFileptr->contentsOfFile = NULL;
+/*! \brief Deallocate a MOTD structure.
+ * If it has cached content, uncache it.
+ * \param motd MOTD to destroy.
+ */
+static void
+motd_destroy(struct Motd *motd)
+{
+ assert(motd);
- if (stat(MessageFileptr->fileName, &sb) < 0)
- return(-1);
+ if (motd->cache) /* drop the cache */
+ motd_decache(motd);
- local_tm = localtime(&sb.st_mtime);
+ dlinkDelete(&motd->node, &MotdList.other);
+ MyFree(motd->path); /* we always must have a path */
+ MyFree(motd->hostmask);
+ MyFree(motd);
+}
- if (local_tm)
- sprintf(MessageFileptr->lastChangedDate,
- "%d/%d/%d %d:%02d",
- local_tm->tm_mday,
- local_tm->tm_mon + 1,
- 1900 + local_tm->tm_year,
- local_tm->tm_hour,
- local_tm->tm_min);
+/*! \brief Find the first matching MOTD block for a user.
+ * If the user is remote, always use remote MOTD.
+ * Otherwise, if there is a hostmask- or class-based MOTD that matches
+ * the user, use it.
+ * Otherwise, use the local MOTD.
+ * \param client_p Client to find MOTD for.
+ * \return Pointer to first matching MOTD for the client.
+ */
+static struct Motd *
+motd_lookup(struct Client *client_p)
+{
+ dlink_node *ptr = NULL;
+ const struct ClassItem *class = NULL;
- if ((file = fopen(MessageFileptr->fileName, "r")) == NULL)
- return(-1);
+ assert(client_p);
- while (fgets(buffer, sizeof(buffer), file))
- {
- if ((p = strchr(buffer, '\n')) != NULL)
- *p = '\0';
+ if (!MyClient(client_p)) /* not my user, always return remote motd */
+ return MotdList.remote;
- newMessageLine = (MessageFileLine *)MyMalloc(sizeof(MessageFileLine));
- strlcpy(newMessageLine->line, buffer, sizeof(newMessageLine->line));
- newMessageLine->next = NULL;
+ class = get_class_ptr(&client_p->localClient->confs);
+ assert(class);
- if (MessageFileptr->contentsOfFile != NULL)
- {
- if (currentMessageLine)
- currentMessageLine->next = newMessageLine;
+ /* check the motd blocks first */
+ DLINK_FOREACH(ptr, MotdList.other.head)
+ {
+ struct Motd *motd = ptr->data;
- currentMessageLine = newMessageLine;
- }
- else
+ switch (motd->type)
{
- MessageFileptr->contentsOfFile = newMessageLine;
- currentMessageLine = newMessageLine;
+ case MOTD_CLASS:
+ if (!match(motd->hostmask, class->name))
+ return motd;
+ case MOTD_HOSTMASK:
+ if (!match(motd->hostmask, client_p->host))
+ return motd;
+ default: break;
}
}
- fclose(file);
- return(0);
+ return MotdList.local; /* Ok, return the default motd */
}
-/*
- * init_MessageLine
- *
- * inputs - NONE
- * output - pointer to new MessageFile
- * side effects - Use this when an internal Message File is wanted
- * without reading an actual file. The MessageFile
- * is init'ed, but must have content added to it through
- * addto_MessageLine()
+/*! \brief Send the content of a MotdCache to a user.
+ * If \a cache is NULL, simply send ERR_NOMOTD to the client.
+ * \param source_p Client to send MOTD to.
+ * \param cache MOTD body to send to client.
*/
-
-MessageFile *
-init_MessageLine(void)
+static void
+motd_forward(struct Client *source_p, const struct MotdCache *cache)
{
- MessageFile *mf;
- MessageFileLine *mptr = NULL;
-
- mf = MyMalloc(sizeof(MessageFile));
- mf->motdType = ISSUPPORT; /* XXX maybe pass it alone in args? */
- mptr = MyMalloc(sizeof(MessageFileLine));
- mf->contentsOfFile = mptr;
- return(mf);
+ int i;
+ const char *from = ID_or_name(&me, source_p->from);
+ const char *to = ID_or_name(source_p, source_p->from);
+
+ assert(source_p);
+
+ if (!cache) /* no motd to send */
+ {
+ sendto_one(source_p, form_str(ERR_NOMOTD), from, to);
+ return;
+ }
+
+ /* send the motd */
+ sendto_one(source_p, form_str(RPL_MOTDSTART),
+ from, to, me.name);
+
+ for (i = 0; i < cache->count; i++)
+ sendto_one(source_p, form_str(RPL_MOTD),
+ from, to, cache->motd[i]);
+ sendto_one(source_p, form_str(RPL_ENDOFMOTD), from, to);
}
-/*
- * addto_MessageLine
- *
- * inputs - Pointer to existing MessageFile
- * - New string to add to this MessageFile
- * output - NONE
- * side effects - Use this when an internal MessageFile is wanted
- * without reading an actual file. Content is added
- * to this MessageFile through this function.
+/*! \brief Find the MOTD for a client and send it.
+ * \param client_p Client being greeted.
*/
+void
+motd_send(struct Client *client_p)
+{
+ assert(client_p);
+ motd_forward(client_p, motd_cache(motd_lookup(client_p)));
+}
+
+/*! \brief Send the signon MOTD to a user.
+ * If FEAT_NODEFAULTMOTD is true and a matching MOTD exists for the
+ * user, direct the client to type /MOTD to read it. Otherwise, call
+ * motd_forward() for the user.
+ * \param source_p Client that has just connected.
+ */
void
-addto_MessageLine(MessageFile *mf, const char *str)
+motd_signon(struct Client *source_p)
{
- MessageFileLine *mptr = mf->contentsOfFile;
- MessageFileLine *nmptr = NULL;
+ const struct MotdCache *cache = motd_cache(motd_lookup(source_p));
- if (mptr == NULL)
+ if (!ConfigFileEntry.short_motd || !cache)
+ motd_forward(source_p, cache);
+ else
{
- mptr = MyMalloc(sizeof(MessageFileLine));
- strcpy(mptr->line, str);
- mf->contentsOfFile = mptr;
+ sendto_one(source_p, ":%s NOTICE %s :*** Notice -- motd was last changed at %d/%d/%d %d:%02d",
+ me.name, source_p->name, cache->modtime.tm_year + 1900,
+ cache->modtime.tm_mon + 1,
+ cache->modtime.tm_mday,
+ cache->modtime.tm_hour,
+ cache->modtime.tm_min);
+ sendto_one(source_p,
+ ":%s NOTICE %s :*** Notice -- Please read the motd if you haven't "
+ "read it", me.name, source_p->name);
+ sendto_one(source_p, form_str(RPL_MOTDSTART),
+ me.name, source_p->name, me.name);
+ sendto_one(source_p, form_str(RPL_MOTD),
+ me.name, source_p->name,
+ "*** This is the short motd ***");
+ sendto_one(source_p, form_str(RPL_ENDOFMOTD),
+ me.name, source_p->name);
}
- else
+}
+
+/*! \brief Clear all cached MOTD bodies.
+ * The local and remote MOTDs are re-cached immediately.
+ */
+void
+motd_recache(void)
+{
+ dlink_node *ptr = NULL;
+
+ motd_decache(MotdList.local); /* decache local and remote MOTDs */
+ motd_decache(MotdList.remote);
+
+ DLINK_FOREACH(ptr, MotdList.other.head) /* now all the others */
+ motd_decache(ptr->data);
+
+ /* now recache local and remote MOTDs */
+ motd_cache(MotdList.local);
+ motd_cache(MotdList.remote);
+}
+
+/*! \brief Re-cache the local and remote MOTDs.
+ * If they already exist, they are deallocated first.
+ */
+void
+motd_init(void)
+{
+ if (MotdList.local) /* destroy old local... */
+ motd_destroy(MotdList.local);
+
+ MotdList.local = motd_create(0, MPATH);
+ motd_cache(MotdList.local); /* init local and cache it */
+
+ if (MotdList.remote) /* destroy old remote... */
+ motd_destroy(MotdList.remote);
+
+ MotdList.remote = motd_create(0, MPATH);
+ motd_cache(MotdList.remote); /* init remote and cache it */
+}
+
+/* \brief Add a new MOTD.
+ * \param hostmask Hostmask (or connection class name) to send this to.
+ * \param path Pathname of file to send.
+ */
+void
+motd_add(const char *hostmask, const char *path)
+{
+ struct Motd *motd = motd_create(hostmask, path); /* create the motd */
+
+ dlinkAdd(motd, &motd->node, &MotdList.other);
+}
+
+/*! \brief Clear out all MOTDs.
+ * Compared to motd_recache(), this destroys all hostmask- or
+ * class-based MOTDs rather than simply uncaching them.
+ * Re-cache the local and remote MOTDs.
+ */
+void
+motd_clear(void)
+{
+ dlink_node *ptr = NULL, *ptr_next = NULL;
+
+ motd_decache(MotdList.local); /* decache local and remote MOTDs */
+ motd_decache(MotdList.remote);
+
+ DLINK_FOREACH_SAFE(ptr, ptr_next, MotdList.other.head) /* destroy other MOTDs */
+ motd_destroy(ptr->data);
+
+ /* now recache local and remote MOTDs */
+ motd_cache(MotdList.local);
+ motd_cache(MotdList.remote);
+}
+
+/*! \brief Report list of non-default MOTDs.
+ * \param source_p Client requesting statistics.
+ */
+void
+motd_report(struct Client *source_p)
+{
+ const dlink_node *ptr = NULL;
+
+ DLINK_FOREACH(ptr, MotdList.other.head)
{
- while (mptr->next != NULL)
- mptr = mptr->next;
- nmptr = MyMalloc(sizeof(MessageFileLine));
- strcpy(nmptr->line, str);
- mptr->next = nmptr;
+ const struct Motd *motd = ptr->data;
+
+ sendto_one(source_p, form_str(RPL_STATSTLINE),
+ me.name, source_p->name,
+ motd->hostmask, motd->path);
}
}
-/*
- * destroy_MessageLine(MessageFile *mf)
- *
- * inputs - pointer to the MessageFile to destroy
- * output - NONE
- * side effects - All the MessageLines attached to the given mf
- * Are freed then one MessageLine is recreated
+/*! \brief Report MOTD memory usage to a client.
+ * \param source_p Client requesting memory usage.
*/
void
-destroy_MessageLine(MessageFile *mf)
+motd_memory_count(struct Client *source_p)
{
- MessageFileLine *mptr = mf->contentsOfFile;
- MessageFileLine *nmptr = NULL;
+ const dlink_node *ptr = NULL;
+ unsigned int mt = 0; /* motd count */
+ unsigned int mtc = 0; /* motd cache count */
+ size_t mtm = 0; /* memory consumed by motd */
+ size_t mtcm = 0; /* memory consumed by motd cache */
- if (mptr == NULL)
- return;
+ if (MotdList.local)
+ {
+ mt++;
+ mtm += sizeof(struct Motd);
+ mtm += MotdList.local->path ? (strlen(MotdList.local->path) + 1) : 0;
+ }
+
+ if (MotdList.remote)
+ {
+ mt++;
+ mtm += sizeof(struct Motd);
+ mtm += MotdList.remote->path ? (strlen(MotdList.remote->path) + 1) : 0;
+ }
- for (mptr = mf->contentsOfFile; mptr != NULL; mptr = nmptr)
+ DLINK_FOREACH(ptr, MotdList.other.head)
{
- nmptr = mptr->next;
- MyFree(mptr);
- }
- mf->contentsOfFile = NULL;
+ const struct MotdCache *motd = ptr->data;
+
+ mt++;
+ mtm += sizeof(struct Motd);
+ mtm += motd->path ? (strlen(motd->path) + 1) : 0;
+ }
+
+ DLINK_FOREACH(ptr, MotdList.cachelist.head)
+ {
+ const struct MotdCache *cache = ptr->data;
+
+ mtc++;
+ mtcm += sizeof(struct MotdCache) + (MOTD_LINESIZE * (cache->count - 1));
+ }
+
+ sendto_one(source_p, ":%s %d %s z :Motds %u(%u) Cache %u(%u)",
+ me.name, RPL_STATSDEBUG, source_p->name,
+ mt, mtm, mtc, mtcm);
}