help-gnats
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Patch for configurable classes in files.c


From: Marcelle Gannon
Subject: Patch for configurable classes in files.c
Date: Thu, 08 Feb 2001 15:15:51 +1100

I wrote this patch for gnats-3.113 after finding it crashed whenever a
'classes' file with a large number of different classes was used.  I
couldn't get cvs diff to work so I have attached my copy of files.c
instead.  The only difference between it and the latest version of
files.c (from the cvs repository) is in the function
get_class_enum_field() which I have rewritten to prevent the buffer
overrun that happened previously.   Hope this is useful.

--
Marcelle Gannon
Graduate Engineer
Redfern Broadband Networks
A member of the Redfern Photonics Pty Ltd group of companies

Lvl. 1,
1 Central Avenue,
Australian Technology Park,
Eveleigh NSW 1430
Australia

Tel: +61 (2) 9209 4860
Fax: +61 (2) 9209 4992
E-Mail: address@hidden

/* Read in and grok the various administrative files.
   Copyright (C) 1993-96, 1997 Free Software Foundation, Inc.
   Originally contributed by Tim Wicinski (address@hidden).

This file is part of GNU GNATS.

GNU GNATS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU GNATS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU GNATS; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA.  */

#include "config.h"
#include "gnats.h"

Responsible *responsible_chain;
States      *state_chain;
Classes     *class_chain;

static int get_adm_record               PARAMS((char*, char**, char*));
static int next_record                  PARAMS((char*, char**, int));

/* Find SITE in the submitters list, copying it into CPTR.  */
int 
find_submitter (cptr, site)
     Submitter *cptr;
     char *site;
{
  char *path = (char *) alloca (PATH_MAX);
  char **array = (char **) alloca (NUM_SUBMITTER_FIELDS * sizeof (char *));
  int err;

  if (site == NULL)
    return -1;

  sprintf (path, "%s/gnats-adm/%s", gnats_root, SUBMITTERS);
  err = get_adm_record (path, array, site);

  if (err == -1) 
    return err;

  memset (cptr, 0, sizeof (Submitter));

  if (err >= 5)
    cptr->notify = array[5];
  if (err >= 4)
    cptr->contact = array[4];
  if (err >= 3)
    {
      if (*array[3])
        cptr->rtime = atoi (array[3]);
      else
        cptr->rtime = -1;       /* -1 => no response time specified */
      xfree (array[3]);
    }
  if (err >= 2)
    cptr->type = array[2];
  if (err >= 1)
    cptr->fullname = array[1];
  if (err >= 0)
    cptr->key = array[0];

  if (cptr->contact == NULL)
    {
      log_msg (LOG_INFO, 1, "contact person not defined, setting to:",
               gnats_admin);
      cptr->contact = gnats_admin;
    }

  return err;
}

int 
find_category (cptr, category)
     Category *cptr;
     char *category;
{
  char *path = (char *) alloca (PATH_MAX);
  char **array = (char **) alloca (NUM_CATEGORY_FIELDS * sizeof (char *));
  int err;

  sprintf (path, "%s/gnats-adm/%s", gnats_root, CATEGORIES);
  err = get_adm_record (path, array, category);

  if (err == -1)
    return err;

  memset (cptr, 0, sizeof (Category));
  
  if (err >= 3)
    cptr->notify = array[3];
  if (err >= 2)
    cptr->person = array[2];
  if (err >= 1)
    cptr->fullname = array[1];
  if (err >= 0)
    cptr->key = array[0];

  return err;
}

/* Note: use get_responsible_address, not this!  */
static int
find_responsible (responsible, person)
     Responsible *responsible;
     char *person;
{
  Responsible *r;

  if (person == NULL || person[0] == '\0')
    return -1;

  /* If the chain has worked, then search it. Otherwise, do the 'file
     thing'. */
  if (responsible_chain != NULL)
    {
      for (r = responsible_chain; r; r = r->next)
        if (strcmp (r->key, person) == 0)
          {
            responsible->key = r->key;
            responsible->fullname = r->fullname;
            responsible->alias = r->alias;
            responsible->authentic = r->authentic;
            return 0;
          }
    }
  else
    {
      char *path = (char *) xmalloc (PATH_MAX);
      char **array = (char **) xmalloc (NUM_RESPONSIBLE_FIELDS * sizeof (char 
*));
      int err;

      /* Make sure this is cleared out before we write to it, so if
         they're missing an alias field for the responsible party,
         we don't end up with garbage.  */
      memset (array, 0, NUM_RESPONSIBLE_FIELDS * sizeof (char *));

      sprintf (path, "%s/gnats-adm/%s", gnats_root, RESPONSIBLE_FILE);
      err = get_adm_record (path, array, person);
      xfree (path);
      if (err == -1)
        {
          xfree ((char *) array);
          return err;
        }

      responsible->alias = array[2];
      responsible->fullname = array[1];
      responsible->key = array[0];
      responsible->authentic = 1;

      xfree ((char *) array);
      return 0;
    }

  return -1;
}

/* Read through the adm files, finding the line that matches the
   variable key.  Return the number of fields in PTR (the ones that matched
   KEY.  */
static int
get_adm_record (fname, ptr, key)
     char *fname;
     char **ptr;
     char *key;
{
  FILE *fp;
  char line[STR_MAX], temp[STR_MAX];
  char *l, *l2;
  char *keyed;
  int err = -1, keyedlen;

  /* append a delimiting ':' to the end of sting to make sure the match
     is fully correct.  */
  keyed = (char *) alloca (strlen(key) + 2);
  /* which is slower - strcpy/strcat or sprintf? */
  sprintf (keyed, "%s:", key);
  keyedlen = strlen(keyed); 

  fp = fopen (fname,"r");
  if (fp == NULL)
    return -1;

  while (read_string (line, fp) > 0)
    if (line[0] != '#' && line[0] != ' ' && line[0] != '\n' &&
        !strncasecmp(line, keyed, keyedlen))
      {
        /* We found a match, now get the whole line.  */
        for (l2 = line, l = get_next_field(l2, temp, ':');
             l || l2;
             (l2 = l) && (l = get_next_field(l, temp, ':')))
          ptr[++err] = (char *) strdup (temp);
        break;
      }

  fclose (fp);

  return err;
}

/* get_responsible_address - dredges the responsible party out of the
     appropriate places.  This routine should be NIS Correct, but it isn't.  */

Responsible *
get_responsible_address (person)
     char *person;
{
  Responsible* responsible = (Responsible *) xmalloc (sizeof (Responsible));
  struct passwd *passwd;
  char *p;

  /* First check the responsible file; if there's no entry, try the
     passwd file.  */
  if (find_responsible (responsible, person) != -1)
    {
      if (!responsible->alias || responsible->alias[0] == '\0')
        {
          xfree (responsible->alias);
          responsible->alias = strdup (responsible->key);
        }
    }
  else
    {
      /* We should always allow names to show up as responsible, even
         if they aren't listed in the passwd file---folks don't remember
         (or usually need) to change closed PRs if the listed person
         happens to leave the company.  */
      responsible->key = (char *) strdup (person);
      responsible->alias = (char *) strdup (responsible->key);

      if ((passwd = getpwnam (person)) != 0)
        {
          /* Some passwd entries have commas for finger programs that
             understand office phone numbers, etc.  Chop 'em off.  */
          p = (char *) strchr (passwd->pw_gecos, ',');
          if (p != NULL)
            *p = '\0';
          responsible->fullname = (char *) strdup (passwd->pw_gecos);
          responsible->authentic = 1;
        }
      else {
        responsible->fullname = strdup("");
        responsible->authentic = 0;
      }
    } 

  return responsible;
}

/*  init_responsibles - reads and parses the whole responsibles file
      into a big linked list.  */

int
init_responsibles ()
{

  Responsible *r, *r_start = NULL, *r_end = NULL;
  FILE *fp;

  char line[STR_MAX];
  char **array = (char **) alloca (NUM_RESPONSIBLE_FIELDS * sizeof (char *));
  char *path = (char *) alloca (PATH_MAX);

  memset (array, 0, NUM_RESPONSIBLE_FIELDS * sizeof (char *));
  sprintf (path, "%s/gnats-adm/%s", gnats_root, RESPONSIBLE_FILE);
  fp = fopen (path, "r");
  if (fp == NULL)
    return 0;

  while (read_string (line, fp) > 0)
    if (line[0] != '#' && line[0] != ' ' && line[0] != '\n')
      {
        int fields;

        fields = next_record (line, array, NUM_RESPONSIBLE_FIELDS);
        r = (Responsible *) xmalloc (sizeof (Responsible));
        r->key = array[0];
        if (fields > 1)
          r->fullname = array[1];
        else
          r->fullname = strdup ("");
        if (fields > 2)
          {
            r->alias = array[2];
            if (!*r->alias)
              {
                xfree (r->alias);
                r->alias = strdup (r->key);
              }
          }
        else
          r->alias = strdup (r->key);

        if (r_end == NULL)
          r_start = r_end = r;
        else
          {
            r_end->next = r;
            r_end = r;
          }

        r->next = NULL;
     }

  if (r_start != NULL)
    responsible_chain = r_start;

  fclose (fp);

  if (responsible_chain)
    return 1;
  else
    return 0;
}

/*  init_classes - reads and parses the whole classes file
      into a big linked list.  */

int
init_classes ()
{
  Classes *c, *c_start = NULL, *c_end = NULL;
  FILE   *fp;

  int         ntypes=1;
  static char *types[1] = { "class" };
  
  int         nclasses=6;
  static char *classes[6] = {
       "sw-bug",
       "doc-bug",
       "support",
       "change-request",
       "mistaken",
       "duplicate"
  };
  static char *descst[6] = {
       "Problem requiring a correction to software.",
       "Problem requiring a correction or improvement in documentation.",
       "A support problem or question.",
       "Suggested change in functionality.",
       "Not a problem, bad PR submission.",
       "Duplicate of another existing PR."
  };
  
  char line[STR_MAX];
  char **array = (char **) alloca (NUM_CLASS_FIELDS * sizeof (char *));
  char *path = (char *) alloca (PATH_MAX);
  int  i, nerrs = 0;

  memset (array, 0, NUM_CLASS_FIELDS * sizeof (char *));
  sprintf (path, "%s/gnats-adm/%s", gnats_root, CLASSES);
  fp = fopen (path, "r");

  if (fp == NULL)
    {
      for (i = 0; i < nclasses; i++)
        {
          c = (Classes *) xmalloc (sizeof (Classes));
          c->key         = classes[i];
          c->type        = strdup ("");
          c->description = descst[i];
          if (c_end == NULL)
              c_start = c_end = c;
          else
            {
              c_end->next = c;
              c_end = c;
            }
    
          c->next = NULL;
        }
    }
  else
    {
      while (read_string (line, fp) > 0)
        if (line[0] != '#' && line[0] != ' ' && line[0] != '\n')
          {
            int fields;
    
            fields = next_record (line, array, NUM_CLASS_FIELDS);
            c = (Classes *) xmalloc (sizeof (Classes));
            c->key = array[0];
            for (i = 0; i < strlen (c->key); i++)
              {
                if (! (((c->key[i] >= '0') && (c->key[i] <= '9'))
                    || ((c->key[i] >= 'A') && (c->key[i] <= 'Z'))
                    || ((c->key[i] >= 'a') && (c->key[i] <= 'z'))
                    ||  (c->key[i] == '-')
                    ||  (c->key[i] == '_')
                    ||  (c->key[i] == '.')))
                  {
                    /* It seems wise to enforce such restrictions starting now,
                     * even though at this time we only have concrete reasons to
                     * prohibit ctrl and 8-bit chars, '/', ':', '|', and '#'. 
                     */
                    nerrs++;
                    if (is_daemon)
                      {
                        punt (0, "%s: Illegal character `%c' in class name, in 
%s:\n%s\n",
                              program_name, c->key[i], path, line);
                      }
                    else
                      {
                        fprintf (stderr, "%s: Illegal character `%c' in class 
name, in %s:\n%s\n",
                                 program_name, c->key[i], path, line);
                      }
                  }
              }
            if (fields > 1)
              {
                c->type = array[1];
                if (strlen (c->type) > 0)
                  {
                    for (i = 0; i < ntypes; i++)
                      {
                        if (!strcmp (c->type, types[i]))
                          break;
                      }
                    if (i >= ntypes)
                      {
                        nerrs++;
                        if (is_daemon)
                          {
                            punt (0, "%s: Invalid class type in classes file 
%s:\n%s\n",
                                  program_name, path, line);
                          }
                        else
                          {
                            fprintf (stderr, "%s: Invalid class type in classes 
file %s:\n%s\n",
                                     program_name, path, line);
                          }
                      }
                  }
              }
            else
              c->type = strdup ("");
            if (fields > 2)
              {
                c->description = array[2];
              }
            else
              c->description = strdup ("") ;
        
            if (c_end == NULL)
              c_start = c_end = c;
            else
              {
                c_end->next = c;
                c_end = c;
              }
        
            c->next = NULL;
          }
      fclose (fp);
    }
        
  if (c_start != NULL) {
    class_chain = c_start;
  }
  
  if (nerrs != 0 || !class_chain)
    {
      if (is_daemon)
        {
          punt (1, "%s: Can not initialize the class chain: %s\n", 
program_name, path);
        }
      else
        {
          fprintf (stderr, "%s: Can not initialize the class chain: %s\n",
                   program_name, path);
          abort ();
        }
    }

  return 0;
}

/* check_class_type - return 1 if class is of type type, otherwise 0 */

int
check_class_type (class, type)
   char *class;
   char *type;
{
  Classes *c;
  
  if (class_chain != NULL)
    {
      for (c = class_chain; c; c = c->next)
        {
          if (strcmp (c->key, class) == 0)
            {
              return (!strcmp (c->type, type));
            }
        }
    }
  
  return (0);
}     

/* get_class_type - return the type of class */

char *
get_class_type (class)
   char *class;
{
  Classes *c;
  static char *null_str = "";
  
  if (class_chain != NULL)
    {
      for (c = class_chain; c; c = c->next)
        {
          if (strcmp (c->key, class) == 0)
            {
              return (c->type);
            }
        }
    }

  /* Huh? Invalid class. Oh well, somebody else will catch this. */
  return (null_str);
}     

/* get_class_enum_field - return a string of bar-separated classes */
/* Modifications by Marcelle Gannon, 19/01/01 to fix up buffer overrun */
/* problem to allow unlimited number of classes                    */
   
char *
get_class_enum_field ()
{
  Classes *c;
  char *line, *trimline;
  int totlen = 0;            /* count up the length of the string required 
first */
  c = class_chain;
  totlen += (strlen(c->key) + 1);         /* +1 for the \0  */
  for (c = c->next; c; c = c->next)
      totlen += (strlen(c->key) + 3);     /* +3 for the " | " characters */
  line = xmalloc(totlen * sizeof(char)); 
  line[0] = '\0';
  c = class_chain;
  strcat (line, c->key);
  for (c = c->next; c; c = c->next)
    {
      strcat (line,  " | ");
      strcat (line, c->key);
    }
  trimline = strdup(line);    /* probably don't need to do this step, included 
as */
  xfree(line);                 /* strdup was used in the previous version       
   */
  return trimline;
}

/*  init_states - reads and parses the whole states file
      into a big linked list.  */

int
init_states ()
{

  States *s, *s_start = NULL, *s_end = NULL;
  FILE   *fp;

  int         ntypes=2;
  static char *types[2] = { "open", "closed" };
  
  int         nstates=5;
  static char *states[5] = {
       "open",
       "analyzed",
       "suspended",
       "feedback",
       "closed"
  };
  static char *descst[5] = {
       "Default state for a new problem report.",
       "Problem examined, understood; difficulty of solution estimated.",
       "No solution yet, work on it also suspended for the time being.",
       "Problem solved, now awaiting originator's reaction to fix.",
       "This PR no longer active; it is resolved or otherwise defunct."
  };
  
  char line[STR_MAX];
  char **array = (char **) alloca (NUM_STATE_FIELDS * sizeof (char *));
  char *path = (char *) alloca (PATH_MAX);
  int  i, nerrs = 0;

  memset (array, 0, NUM_STATE_FIELDS * sizeof (char *));
  sprintf (path, "%s/gnats-adm/%s", gnats_root, STATES);
  fp = fopen (path, "r");

  if (fp == NULL)
    {
      for (i = 0; i < nstates; i++)
        {
          s = (States *) xmalloc (sizeof (States));
          s->key         = states[i];
          s->type        = strdup ("");
          s->description = descst[i];
          if (s_end == NULL)
              s_start = s_end = s;
          else
            {
              s_end->next = s;
              s_end = s;
            }
    
          s->next = NULL;
        }
    }
  else
    {
      while (read_string (line, fp) > 0)
        if (line[0] != '#' && line[0] != ' ' && line[0] != '\n')
          {
            int fields;
    
            fields = next_record (line, array, NUM_STATE_FIELDS);
            s = (States *) xmalloc (sizeof (States));
            s->key = array[0];
            for (i = 0; i < strlen (s->key); i++)
              {
                if (! (((s->key[i] >= '0') && (s->key[i] <= '9'))
                    || ((s->key[i] >= 'A') && (s->key[i] <= 'Z'))
                    || ((s->key[i] >= 'a') && (s->key[i] <= 'z'))
                    ||  (s->key[i] == '-')
                    ||  (s->key[i] == '_')
                    ||  (s->key[i] == '.')))
                  {
                    /* It seems wise to enforce such restrictions starting now,
                     * even though at this time we only have concrete reasons to
                     * prohibit ctrl and 8-bit chars, '/', ':', '|', and '#'. 
                     */
                    nerrs++;
                    if (is_daemon)
                      {
                        punt (0, "%s: Illegal character `%c' in state name, in 
%s:\n%s\n",
                              program_name, s->key[i], path, line);
                      }
                    else
                      {
                        fprintf (stderr, "%s: Illegal character `%c' in state 
name, in %s:\n%s\n",
                                 program_name, s->key[i], path, line);
                      }
                  }
              }
            if (fields > 1)
              {
                s->type = array[1];
                if (strlen (s->type) > 0)
                  {
                    for (i = 0; i < ntypes; i++)
                      {
                        if (!strcmp (s->type, types[i]))
                          break;
                      }
                    if (i >= ntypes)
                      {
                        nerrs++;
                        if (is_daemon)
                          {
                            punt (0, "%s: Invalid state type in states file 
%s:\n%s\n",
                                  program_name, path, line);
                          }
                        else
                          {
                            fprintf (stderr, "%s: Invalid state type in states 
file %s:\n%s\n",
                                     program_name, path, line);
                          }
                      }
                  }
              }
            else
              s->type = strdup ("");
            if (fields > 2)
              {
                s->description = array[2];
              }
            else
              s->description = strdup ("") ;
        
            if (s_end == NULL)
              s_start = s_end = s;
            else
              {
                s_end->next = s;
                s_end = s;
              }
        
            s->next = NULL;
          }
      fclose (fp);
    }
        
  if (s_start != NULL) {
    state_chain = s_start;
    /* Force the last state to be type closed */
    xfree (s_end->type);
    s_end->type = strdup ("closed");
  }
  
  if (nerrs != 0 || !state_chain)
    {
      if (is_daemon)
        {
          punt (1, "%s: Can not initialize the state chain: %s\n", 
program_name, path);
        }
      else
        {
          fprintf (stderr, "%s: Can not initialize the state chain: %s.\n",
                   program_name, path);
          abort ();
        }
    }

  return 0;
}

/* check_state_type - return 1 if state is of type type, otherwise 0 */

int
check_state_type (state, type)
   char *state;
   char *type;
{
  States *s;
  
  if (state_chain != NULL)
    {
      for (s = state_chain; s; s = s->next)
        {
          if (strcmp (s->key, state) == 0)
            {
              return (!strcmp (s->type, type));
            }
        }
    }
  
  return (0);
}     

/* get_state_type - return the type of state */

char *
get_state_type (state)
   char *state;
{
  States *s;
  static char *null_str = "";
  
  if (state_chain != NULL)
    {
      for (s = state_chain; s; s = s->next)
        {
          if (strcmp (s->key, state) == 0)
            {
              return (s->type);
            }
        }
    }

  /* Huh? Invalid state. Oh well, somebody else will catch this. */
  return (null_str);
}     

/* get_state_enum_field - return a string of bar-separated states */
   
char *
get_state_enum_field ()
{
  States *s;
  char line[STR_MAX];
  line[0] = '\0';
  s = state_chain;
  strcat (line, s->key);
  for (s = s->next; s; s = s->next)
    {
      strcat (line,  " | ");
      strcat (line, s->key);
    }
  return (strdup (line));
}

/* next_record - straight from get_adm_record, but w/out the searching
     for the right record. This builds up an array at a time.  */

static int
next_record (line, ptr, nfields)
   char *line;
   char **ptr;
   int  nfields;
{
  char *l, *l2;
  int err = 0;
  char temp[STR_MAX];

  for (l2 = line, l = get_next_field(l2, temp, ':');
       l || l2;
       (l2 = l) && (l = get_next_field(l, temp, ':')))
    {
      ptr[err++] = (char *) strdup (temp);
      if (err >= nfields - 1)
           break;
    }
  /* Put all of the remaining line into the last array position */
  ptr[err++] = (char *) strdup (l);
  /* erase whitespace at the end */
  l = ptr[err - 1] + strlen (ptr[err - 1]);
  while (l > ptr[err - 1] && (isspace (*(l-1))))
       *--l = '\0';

  return err;
}

void
free_responsible (responsible)
     Responsible *responsible;
{
  if (responsible->key && responsible->key != gnats_admin)
    xfree (responsible->key);
  if (responsible->fullname)
    xfree (responsible->fullname);
  if ((responsible->alias != NULL)
      && (responsible->alias != responsible->key)
      && (responsible->alias != gnats_admin))
    xfree (responsible->alias);
  return;
}

void
free_submitter (submitter)
     Submitter *submitter;
{
  if (submitter->key)
    xfree (submitter->key);
  if (submitter->fullname)
    xfree (submitter->fullname);
  if (submitter->type)
    xfree (submitter->type);
  if (submitter->contact && submitter->contact != gnats_admin)
    xfree (submitter->contact);
  if (submitter->notify && submitter->notify != gnats_admin)
    xfree (submitter->notify);
  return;
}

void
free_category (category)
     Category *category;
{
  if (category->key)
    xfree (category->key);
  if (category->fullname)
    xfree (category->fullname);
  if (category->person)
    xfree (category->person);
  if (category->notify && category->notify != gnats_admin)
    xfree (category->notify);
  return;
}

void
free_state (state)
     States *state;
{
  if (state->key)
    xfree (state->key);
  if (state->type)
    xfree (state->type);
  if (state->description)
    xfree (state->description);
  return;
}

char *
get_responsible_addr (full, strict, name)
    int full;
    int strict;
    char *name;
{
  Responsible *r;
  char *p, *address = (char *) NULL;
  
  p = (char *) strchr (name, ' ');
  if (p != (char *) NULL)
    *p = '\0';
  p = (char *) strchr (name, '(');
  if (p != (char *) NULL)
    *p = '\0';

  r = get_responsible_address (name);

  if (r && (r->authentic || !strict))
    {
      address = (char *) xmalloc (STR_MAX);
      if (full)
        sprintf (address, "%s:%s:%s", r->key, r->fullname, r->alias);
      else
        {
          if (r->alias[0]
              /* Make sure if the person putting the entry in accidentally
                 added a space after the colon, we don't accept that as
                 an address.  */
              && isalpha (r->alias[0]))
            sprintf (address, "%s", r->alias);
          else
            sprintf (address, "%s", r->key);
        }
    }

  return (address);
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]