示例#1
0
static void
sendhelpfile(struct Client *source_p, const char *path, const char *topic, const char *nick)
{
	FBFILE *file;
	char line[HELPLEN];

	if((file = fbopen(path, "r")) == NULL)
	{
		sendto_one(source_p, form_str(ERR_HELPNOTFOUND), me.name, nick, topic);
		return;
	}

	if(fbgets(line, sizeof(line), file) == NULL)
	{
		sendto_one(source_p, form_str(ERR_HELPNOTFOUND), me.name, nick, topic);
		return;
	}

	sendto_one(source_p, form_str(RPL_HELPSTART), me.name, nick, topic, line);

	while (fbgets(line, sizeof(line), file))
	{
		sendto_one(source_p, form_str(RPL_HELPTXT), me.name, nick, topic, line);
	}

	fbclose(file);
	sendto_one(source_p, form_str(RPL_ENDOFHELP), me.name, nick, topic);
	return;
}
示例#2
0
文件: m_help.c 项目: mdharris/ircd
static void 
sendhelpfile(struct Client *source_p, const char *path, const char *topic)
{
  FBFILE *file;
  char line[HELPLEN];
  char started = 0;
  int type;

  if ((file = fbopen(path, "r")) == NULL)
  {
    sendto_one(source_p, form_str(ERR_HELPNOTFOUND),
               me.name, source_p->name, topic);
    return;
  }

  if (fbgets(line, sizeof(line), file) == NULL)
  {
    sendto_one(source_p, form_str(ERR_HELPNOTFOUND),
               me.name, source_p->name, topic);
    return;
  }

  else if (line[0] != '#')
  {
    line[strlen(line) - 1] = '\0';	  
    sendto_one(source_p, form_str(RPL_HELPSTART),
             me.name, source_p->name, topic, line);
    started = 1;
  }

  while (fbgets(line, sizeof(line), file))
  {
    line[strlen(line) - 1] = '\0';
    if (line[0] != '#')
    {
      if (!started)
      {
        type = RPL_HELPSTART;
        started = 1;
      }
      else
        type = RPL_HELPTXT;
      
      sendto_one(source_p, form_str(RPL_HELPTXT),
                 me.name, source_p->name, topic, line);
    }
  }

  fbclose(file);
  sendto_one(source_p, form_str(RPL_HELPTXT),
             me.name, source_p->name, topic, "");
  sendto_one(source_p, form_str(RPL_ENDOFHELP),
             me.name, source_p->name, topic);
}
示例#3
0
void
parse_k_file(FBFILE *file)
{
  struct ConfItem *aconf;
  char* user_field=(char *)NULL;
  char* reason_field=(char *)NULL;
  char* host_field=(char *)NULL;
  char  line[BUFSIZE];
  char* p;

  while (fbgets(line, sizeof(line), file))
    {
      if ((p = strchr(line, '\n')) != NULL)
        *p = '\0';

      if ((*line == '\0') || (*line == '#'))
        continue;

      if ((user_field = getfield(line)) == NULL)
	continue;

      if ((host_field = getfield(NULL)) == NULL)
	continue;

      if ((reason_field = getfield(NULL)) == NULL)
	continue;
	  
      aconf = make_conf();
      aconf->status = CONF_KILL;
      conf_add_fields(aconf,host_field,reason_field,user_field,0,NULL);

      if (aconf->host != NULL)
	add_conf_by_address(aconf->host, CONF_KILL, aconf->user, aconf);
    }
}
示例#4
0
void parse_d_file(FBFILE *file)
{
  struct ConfItem *aconf;
  char* reason_field=(char *)NULL;
  char* host_field=(char *)NULL;
  char  line[BUFSIZE];
  char* p;

  while (fbgets(line, sizeof(line), file))
    {
      if ((p = strchr(line, '\n')))
        *p = '\0';

      if ((*line == '\0') || (line[0] == '#'))
        continue;

      if ((host_field = getfield(line)) == NULL)
	continue;

      if ((reason_field = getfield(NULL)) == NULL)
	continue;
	  
      aconf = make_conf();
      aconf->status = CONF_DLINE;
      conf_add_fields(aconf,host_field,reason_field,"",0,NULL);
      conf_add_d_conf(aconf);
    }
}
示例#5
0
/** Tell a user that they are banned, dumping the message from a file.
 * @param sptr Client being rejected
 * @param filename Send this file's contents to \a sptr
 */
static void killcomment(struct Client* sptr, const char* filename)
{
  FBFILE*     file = 0;
  char        line[80];
  struct stat sb;

  if (NULL == (file = fbopen(filename, "r"))) {
    send_reply(sptr, ERR_NOMOTD);
    send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
               ":Connection from your host is refused on this server.");
    return;
  }
  fbstat(&sb, file);
  while (fbgets(line, sizeof(line) - 1, file)) {
    char* end = line + strlen(line);
    while (end > line) {
      --end;
      if ('\n' == *end || '\r' == *end)
        *end = '\0';
      else
        break;
    }
    send_reply(sptr, RPL_MOTD, line);
  }
  send_reply(sptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP,
             ":Connection from your host is refused on this server.");
  fbclose(file);
}
示例#6
0
void
parse_resv_file(FBFILE *file)
{
  char *reason_field;
  char *host_field;
  char line[BUFSIZE];
  char *p;

  while(fbgets(line, sizeof(line), file))
  {
    if((p = strchr(line, '\n')))
      *p = '\0';

    if((*line == '\0') || (line[0] == '#'))
      continue;

    if((host_field = getfield(line)) == NULL)
      continue;

    if((reason_field = getfield(NULL)) == NULL)
      continue;

    if(IsChannelName(host_field))
      create_resv(host_field, reason_field, RESV_CHANNEL);
    else if(clean_resv_nick(host_field))
      create_resv(host_field, reason_field, RESV_NICK);
  }
}
示例#7
0
/* check_pidfile()
 *
 * inputs       - filename+path of pid file
 * output       - none
 * side effects - reads pid from pidfile and checks if ircd is in process
 *                list. if it is, gracefully exits
 * -kre
 */
static void
check_pidfile (const char *filename)
{
  FBFILE *fb;
  char buff[32];
  pid_t pidfromfile;

  /* Don't do logging here, since we don't have log() initialised */
  if ((fb = fbopen (filename, "r")))
    {
      if (fbgets (buff, 20, fb) == NULL)
	{
	  /* log(L_ERROR, "Error reading from pid file %s (%s)", filename,
	   * strerror(errno));
	   */
	}
      else
	{
	  pidfromfile = atoi (buff);

	  if (!kill (pidfromfile, 0))
	    {
	      /* log(L_ERROR, "Server is already running"); */
	      printf ("ircd: daemon is already running\n");
	      exit (-1);
	    }
	}

      fbclose (fb);
    }
  else if (errno != ENOENT)
    {
      /* log(L_ERROR, "Error opening pid file %s", filename); */
    }
}
示例#8
0
/* parse_resvconf()
 *
 * inputs - NONE
 * output - -1 if failure 0 if success
 * side effects - fills in irc_nsaddr_list
 */
static int
parse_resvconf(void)
{
  char *p;
  char *opt;
  char *arg;
  char input[MAXLINE];
  FBFILE *file;

  /* XXX "/etc/resolv.conf" should be from a define in setup.h perhaps
   * for cygwin support etc. this hardcodes it to unix for now -db
   */
  if ((file = fbopen("/etc/resolv.conf", "r")) == NULL)
    return(-1);

  while (fbgets(input, MAXLINE, file) != NULL)
  {
    /* blow away any newline */
    if ((p = strpbrk(input, "\r\n")) != NULL)
      *p = '\0';

    /* Ignore comment lines immediately */
    if (*input == '#')
      continue;

    p = input;
    /* skip until something thats not a space is seen */
    while (IsSpace(*p))
      p++;
    /* if at this point, have a '\0' then continue */
    if (*p == '\0')
      continue;

    /* skip until a space is found */
    opt = input;
    while (!IsSpace(*p))
      if (*p++ == '\0')
        continue;  /* no arguments?.. ignore this line */
    /* blow away the space character */
    *p++ = '\0';

    /* skip these spaces that are before the argument */
    while (IsSpace(*p))
      p++;
    /* Now arg should be right where p is pointing */
    arg = p;
    if ((p = strpbrk(arg, " \t")) != NULL)
      *p = '\0';  /* take the first word */

    if (strcasecmp(opt, "domain") == 0)
      strlcpy(irc_domain, arg, HOSTLEN);
    else if (strcasecmp(opt, "nameserver") == 0)
      add_nameserver(arg);
  }

  fbclose(file);
  return(0);
}
示例#9
0
void
parse_x_file(FBFILE * file)
{
	struct ConfItem *aconf;
	char *reason_field = NULL;
	char *host_field = NULL;
	char *port_field = NULL;
	char line[BUFSIZE];
	char *p;

	while (fbgets(line, sizeof(line), file))
	{
		if((p = strchr(line, '\n')))
			*p = '\0';

		if((*line == '\0') || (line[0] == '#'))
			continue;

		host_field = getfield(line);
		if(BadPtr(host_field))
			continue;

		port_field = getfield(NULL);
		if(BadPtr(port_field))
			continue;

		reason_field = getfield(NULL);
		if(BadPtr(reason_field))
			continue;

		aconf = make_conf();
		aconf->status = CONF_XLINE;
		conf_add_fields(aconf, host_field, reason_field, "", port_field, NULL);
		conf_add_x_conf(aconf);
	}
}
示例#10
0
/* Now, our job is a bit harder. I will scan through the SPOOF_FILE
 * and read all auths{} (assuming they are written in our line formatting..),
 * then rewrite them skipping the one to delete. --adx */
static void
mo_delspoof(struct Client *client_p, struct Client *source_p,
            int parc, char *parv[])
{
#ifdef SPOOF_FILE
  FBFILE *f, *fout;
  int ignore_it = 1, spoof_found = 0;
  char buffer[1024], *tmp;
#endif
  const char *user = NULL;
  char *host = NULL;

  if (MyConnect(source_p) && !IsOperAdmin(source_p))
  {
    sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, parv[0], "DELSPOOF");
    return;
  }

  if (parv[1] == NULL || !*parv[1])
  {
    if (MyConnect(source_p))
      sendto_one(source_p, ":%s NOTICE %s :Syntax: /DELSPOOF <user@host>",
                           me.name, source_p->name);
    return;
  }

  /* check user@host mask */
  (void) collapse(parv[1]);

  host = strchr(parv[1], '@');
  if (host != NULL)
  {
    user = parv[1];
    *host = '\0';
    host++;
  }
  else
  {
    user = "******";
    host = parv[1];
  }

#ifdef PROPAGATE_SPOOF
  sendto_server(client_p, source_p, NULL, NOCAPS, NOCAPS, LL_ICLIENT,
    ":%s DELSPOOF %s@%s", source_p->name, user, host);
#endif

#ifdef SPOOF_FILE
  if ((f = fbopen(SPOOF_FILE, "r")) == NULL)
  {
    sendto_realops_flags(UMODE_ALL, L_ALL,
                         "Could not open %s file, auth for %s@%s not deleted "
                         "(requested by %s)",
                         SPOOF_FILE, user, host, source_p->name);
    return;
  }

  if ((fout = fbopen(SPOOF_FILE ".new", "w")) == NULL)
  {
    sendto_realops_flags(UMODE_ALL, L_ALL,
                         "Could not create %s.new file, auth for %s@%s not "
                         "deleted (requested by %s)",
                         SPOOF_FILE, user, host, source_p->name);
    return;
  }

  while (fbgets(buffer, 1024, f))
  {
    if (!ircncmp(buffer, "auth {", 6))
    {
      /* don't process it yet.. we have to check whether the user="******"; field
       * matches the user@host mask which is being deleted
       */
      ignore_it = 1;
      continue;
    }

    /* a simple parser substitute... */
    for (tmp = buffer; *tmp == '\t' || *tmp == ' '; tmp++)
      ;
    if (!ircncmp(tmp, "user", 4))
    {
      for (tmp += 4; *tmp == '\t' || *tmp == ' '; tmp++)
        ;
      if (*tmp == '=') {
        for (++tmp; *tmp == '\t' || *tmp == ' '; tmp++)
          ;
        if (*tmp == '\"')
        {
          /* yuppi, we've just reached the user="******"; field */
          int matches;
          char *tmp2 = strchr(++tmp, '\"');

          if (tmp2 != NULL)
            *tmp2 = '\0';
          tmp2 = strchr(tmp, '@');

          /* is it matching our mask? */
          if (tmp2 == NULL)
            matches = !irccmp(user, "*") && !irccmp(host, tmp);
          else
          {
            *tmp2++ = '\0';
            matches = !irccmp(user, tmp) && !irccmp(host, tmp2);
          }

          if (!matches)
          {
            /* no.. so leave it unchanged */
            if (ignore_it)
            {
              ignore_it = 0;
              fbputs("auth {\n", fout, 7);
              /* user="******" should be the first field in the auth {}; block,
               * otherwise we could have problems...
               */
            }

            fbputs("\tuser = \"", fout, 9);
            if (tmp2 == NULL)
              fbputs("*", fout, 1);
            else
              fbputs(tmp, fout, strlen(tmp));
            fbputs("@", fout, 1);
            fbputs(tmp2, fout, strlen(tmp2));
            fbputs("\";\n", fout, 3);
          }
          else
          {
            /* we've got it! - omit and continue working */
            spoof_found = 1;
          }

          continue;
        }
      }
    }

    if (!ignore_it)
      fbputs(buffer, fout, strlen(buffer));
  }

  fbclose(f);
  fbclose(fout);

  if (!spoof_found)
  {
    if (MyConnect(source_p))
      sendto_one(source_p, ":%s NOTICE %s :No auth for %s@%s found",
                 me.name, source_p->name, user, host);
    unlink(SPOOF_FILE ".new");
    return;
  }

  unlink(SPOOF_FILE);
  rename(SPOOF_FILE ".new", SPOOF_FILE);
  rehash(0);
#endif

#ifdef LOG_SPOOF
  sendto_realops_flags(UMODE_ALL, L_ALL, "%s deleted auth for %s@%s",
                       source_p->name, user, host);
#endif
}
示例#11
0
/* parse_csv_file()
 *
 * inputs	- FILE pointer
 * 		- type of conf to parse
 * output	- none
 * side effects	-
 */
void
parse_csv_file(FBFILE *file, ConfType conf_type)
{
  struct ConfItem *conf;
  struct AccessItem *aconf;
  struct MatchItem *match_item;
  struct MatchItem *nresv;
  struct ResvChannel *cresv;
  char  *name_field=NULL;
  char  *user_field=NULL;
  char  *reason_field=NULL;
  char  *oper_reason=NULL;
  char  *host_field=NULL;
  char  *duration_field=NULL;
  char  *temp=NULL;
  char  line[IRCD_BUFSIZE];
  char  *p;

  while (fbgets(line, sizeof(line), file) != NULL)
  {
    duration_field = NULL;

    if ((p = strchr(line, '\n')) != NULL)
      *p = '\0';

    if ((line[0] == '\0') || (line[0] == '#'))
      continue;

    switch(conf_type)
    {
      case KLINE_TYPE:
        parse_csv_line(line, &user_field, &host_field, &reason_field,
            &oper_reason, &temp, &temp, &temp, &duration_field, NULL);
        conf = make_conf_item(KLINE_TYPE);
        aconf = map_to_conf(conf);

        if (host_field != NULL)
          DupString(aconf->host, host_field);
        if (reason_field != NULL)
          DupString(aconf->reason, reason_field);
        if (oper_reason != NULL)
          DupString(aconf->oper_reason, oper_reason);
        if (user_field != NULL)
          DupString(aconf->user, user_field);
        if (duration_field != NULL)
          aconf->hold = atoi(duration_field);
        if (aconf->host != NULL)
        {
          if(duration_field == NULL)
            add_conf_by_address(CONF_KILL, aconf);
          else
            add_temp_line(conf);
        }
        break;

      case RKLINE_TYPE:
        {
          const char *errptr = NULL;
          pcre *exp_user = NULL, *exp_host = NULL;

          parse_csv_line(line, &user_field, &host_field, &reason_field,
              &oper_reason, &temp, &temp, &temp, &duration_field, NULL);

          if (host_field == NULL || user_field == NULL)
            break;

          if (!(exp_user = ircd_pcre_compile(user_field, &errptr)) ||
              !(exp_host = ircd_pcre_compile(host_field, &errptr)))
          {
            sendto_realops_flags(UMODE_ALL, L_ALL,
                "Failed to add regular expression based K-Line: %s", errptr);
            break;
          }

          conf = make_conf_item(RKLINE_TYPE);
          aconf = map_to_conf(conf);

          aconf->regexuser = exp_user;
          aconf->regexhost = exp_host;

          DupString(aconf->user, user_field);
          DupString(aconf->host, host_field);

          if (reason_field != NULL)
            DupString(aconf->reason, reason_field);
          else
            DupString(aconf->reason, "No reason");

          if (oper_reason != NULL)
            DupString(aconf->oper_reason, oper_reason);

          if(duration_field != NULL)
          {
            aconf->hold = atoi(duration_field);
            add_temp_line(conf);
          }
        }
        break;

      case DLINE_TYPE:
        parse_csv_line(line, &host_field, &reason_field, &temp, &temp, &temp, 
            &temp, &duration_field, NULL);
        conf = make_conf_item(DLINE_TYPE);
        aconf = (struct AccessItem *)map_to_conf(conf);
        if (host_field != NULL)
          DupString(aconf->host, host_field);
        if (reason_field != NULL)
          DupString(aconf->reason, reason_field);
        if(duration_field != NULL)
        {
          aconf->hold = atoi(duration_field);
          add_temp_line(conf);
        }
        else
          conf_add_d_conf(aconf);
        break;

      case XLINE_TYPE:
        parse_csv_line(line, &name_field, &reason_field, &oper_reason, &temp,
            &temp, &temp, &temp, &duration_field, NULL);
        conf = make_conf_item(XLINE_TYPE);
        match_item = (struct MatchItem *)map_to_conf(conf);
        if (name_field != NULL)
          DupString(conf->name, name_field);
        if (reason_field != NULL)
          DupString(match_item->reason, reason_field);

        if(duration_field != NULL)
        {
          match_item->hold = atoi(duration_field);
          add_temp_line(conf);
        }
        break;

      case RXLINE_TYPE:
        {
          const char *errptr = NULL;
          pcre *exp_p = NULL;

          parse_csv_line(line, &name_field, &reason_field, &oper_reason, &temp,
              &temp, &temp, &temp, &duration_field, NULL);

          if (name_field == NULL)
            break;

          if (!(exp_p = ircd_pcre_compile(name_field, &errptr)))
          {
            sendto_realops_flags(UMODE_ALL, L_ALL,
                "Failed to add regular expression based X-Line: %s", errptr);
            break;
          }

          conf = make_conf_item(RXLINE_TYPE);
          conf->regexpname = exp_p;
          match_item = map_to_conf(conf);
          DupString(conf->name, name_field);

          if (reason_field != NULL)
            DupString(match_item->reason, reason_field);
          else
            DupString(match_item->reason, "No reason");

          if(duration_field != NULL)
          {
            match_item->hold = atoi(duration_field);
            add_temp_line(conf);
          }
        }
        break;

      case CRESV_TYPE:
        parse_csv_line(line, &name_field, &reason_field, &duration_field, NULL);
        conf = create_channel_resv(name_field, reason_field, 0);
        if(duration_field != NULL)
        {
          cresv = map_to_conf(conf);
          cresv->hold = atoi(duration_field);
          add_temp_line(conf);
        }
        break;

      case NRESV_TYPE:
        parse_csv_line(line, &name_field, &reason_field, &duration_field, NULL);
        conf = create_nick_resv(name_field, reason_field, 0);
        if(duration_field != NULL)
        {
          nresv = map_to_conf(conf);
          nresv->hold = atoi(duration_field);
          add_temp_line(conf);
        }
        break;

      case GLINE_TYPE:
      case GDENY_TYPE:
      case CONF_TYPE:
      case OPER_TYPE:
      case CLIENT_TYPE:
      case SERVER_TYPE:
      case CLUSTER_TYPE:
      case HUB_TYPE:
      case LEAF_TYPE:
      case ULINE_TYPE:
      case EXEMPTDLINE_TYPE:
      case CLASS_TYPE:
        break;
    }
  }
}
示例#12
0
/*
 * read_message_file() - original From CoMSTuD, added Aug 29, 1996
 *
 * inputs	- pointer to MessageFileptr
 * output	-
 * side effects	-
 */
int
read_message_file(MessageFile *MessageFileptr)
{
  struct stat sb;
  struct tm *local_tm;

  /* used to clear out old MessageFile entries */
  MessageFileLine *mptr = 0;
  MessageFileLine *next_mptr = 0;

  /* used to add new MessageFile entries */
  MessageFileLine *newMessageLine = 0;
  MessageFileLine *currentMessageLine = 0;

  char buffer[MESSAGELINELEN];
  char *p;
  FBFILE *file;

  for (mptr = MessageFileptr->contentsOfFile; mptr; mptr = next_mptr)
  {
    next_mptr = mptr->next;
    MyFree(mptr);
  }

  MessageFileptr->contentsOfFile = NULL;

  if (stat(MessageFileptr->fileName, &sb) < 0)
    return(-1);

  local_tm = localtime(&sb.st_mtime);

  if (local_tm)
    ircsprintf(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);

  if ((file = fbopen(MessageFileptr->fileName, "r")) == NULL)
    return(-1);

  while (fbgets(buffer, sizeof(buffer), file))
  {
    if ((p = strchr(buffer, '\n')) != NULL)
      *p = '\0';

    newMessageLine = (MessageFileLine *)MyMalloc(sizeof(MessageFileLine));
    strlcpy(newMessageLine->line, buffer, sizeof(newMessageLine->line));
    newMessageLine->next = NULL;

    if (MessageFileptr->contentsOfFile != NULL)
    {
      if (currentMessageLine)
        currentMessageLine->next = newMessageLine;

      currentMessageLine = newMessageLine;
    }
    else
    {
      MessageFileptr->contentsOfFile = newMessageLine;
      currentMessageLine = newMessageLine;
    }
  }

  fbclose(file);
  return(0);
}
示例#13
0
文件: motd.c 项目: DamnIO/DamnIRCd
/** 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[in] motd Specification for MOTD file.
 * @return Matching MotdCache entry.
 */
static struct MotdCache *
motd_cache(struct Motd *motd)
{
  FBFILE*		file;
  struct MotdCache*	cache;
  struct stat		sb;
  char			line[MOTD_LINESIZE + 2]; /* \r\n */
  char*			tmp;
  int			i;

  assert(0 != motd);
  assert(0 != motd->path);

  if (motd->cache)
    return motd->cache;

  /* try to find it in the list of cached files... */
  for (cache = MotdList.cachelist; cache; cache = cache->next) {
    if (!strcmp(cache->path, motd->path) &&
	cache->maxcount == motd->maxcount) { /* found one... */
      cache->ref++; /* increase reference count... */
      motd->cache = cache; /* remember cache... */
      return motd->cache; /* return it */
    }
  }

  /* gotta read in the file, now */
  if (!(file = fbopen(motd->path, "r"))) {
    Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motd->path,
	   strerror(errno)));
    return 0;
  }

  /* need the file's modification time */
  if (-1 == fbstat(&sb, file)) {
    fbclose(file);
    return 0;
  }

  /* Ok, allocate a structure; we'll realloc later to trim memory */
  cache = (struct MotdCache *)MyMalloc(sizeof(struct MotdCache) +
				       (MOTD_LINESIZE * (MOTD_MAXLINES - 1)));

  cache->ref = 1;
  DupString(cache->path, motd->path);
  cache->maxcount = motd->maxcount;

  cache->modtime = *localtime((time_t *) &sb.st_mtime); /* store modtime */

  cache->count = 0;
  while (cache->count < cache->maxcount && fbgets(line, sizeof(line), file)) {
    /* 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++;
  }

  fbclose(file); /* close the file */

  /* trim memory usage a little */
  motd->cache = (struct MotdCache*)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... */
  motd->cache->next = MotdList.cachelist;
  motd->cache->prev_p = &MotdList.cachelist;
  if (MotdList.cachelist)
    MotdList.cachelist->prev_p = &motd->cache->next;
  MotdList.cachelist = motd->cache;

  return motd->cache;
}