/* scanner.C
 * John Viega
 *
 * Jan 28-29 2000
 */

#include "config.H"
#include "scanner.H"
#include "fatal.H"

void Scanner::ProcessIgnores()
{
  TokenContainer *cc = lexer->GetComments();
  int             i  = 0;
  // assert forall i[0:cc.size]: cc->GetToken(i)->GetTokenType() == COMMENT
    while(1)
    {
      CommentTok *tok = (CommentTok *)(cc->GetToken(i++));
      if(!tok) break;
      char *str = tok->GetValue();
      Lex  *l   = new Lex(str, strlen(str), lexer->GetSourceIdentifier(),
      			  tok->GetLineNo(), 1, 1);
      if(!l)
      	OutOfMemory();
      TokenContainer *tc = l->GetTokens();
      CheckOneContainer(tc, tok);
      delete l;
    }
}

void Scanner::CheckOneContainer(TokenContainer *tc, CommentTok *comment_tok)
{
  Token   *t = tc->GetToken(0);
  if(!t) return;
  TokenId id = t->GetTokenType();
  if(id != IDENTIFIER)
    return;
  if(strcasecmp(((IdTok *)t)->GetName(), "ITS4"))  // If value != ITS4
    return;
  t = tc->GetToken(1);
  if(!t) return;
  id = t->GetTokenType();
  if(id != OPERATOR)
    return;
  if(strcmp(((OperatorTok *)t)->GetOperatorName(), ":"))
    return;
  FigureOutCommand(tc, comment_tok);
}

void Scanner::CalculateEffectiveLineNumber(CommentTok *comment_tok, int &l1, int &l2)
{
  int start_line = comment_tok->GetLineNo();
  int end_line   = comment_tok->GetEndLineNo();
  int tok_ix     = comment_tok->GetTokenIndex();
  int start_yes  = 0;
  int end_yes    = 0;
  
  // Note:  This should ignore both lines:
  /* Non-commented-stuff */ /* 
                             * ITS4: IGNORE
                             */             /*Non-commented-stuff*/
  TokenContainer *tc = lexer->GetTokens();
  Token *t;
  int   i = 0;
  do { t = tc->GetToken(tok_ix-(++i)); } while(t && (t->GetTokenType() == COMMENT) &&
					       t->GetLineNo() == start_line);
  if(t)
    {
      if((t->GetLineNo() == start_line) && (t->GetTokenType() != COMMENT))
	{
	  start_yes = 1;
	}
    }
  i = 0;
  if(!start_yes)
    {
      do { t = tc->GetToken(tok_ix+(++i)); } 
      while(t && (t->GetTokenType() == COMMENT) && t->GetLineNo() == end_line);
      if(t)
	{
	  if((t->GetLineNo() == end_line) && (t->GetTokenType() != COMMENT))
	    {
	      end_yes = 1;
	    }
	}
    }
  if(start_yes || end_yes)
    { 
      l1 = start_line;
      l2 = end_line;
    }
  else 
    {
      l1 = l2 = end_line+1;
    }
}
void Scanner::FigureOutCommand(TokenContainer *tc, CommentTok *comment_tok)
{
  int i,x;
  LineIgnoreList *ilist = 0;

  Token *t = tc->GetToken(2);
  if(!t || (t->GetTokenType() != IDENTIFIER))
    {
      goto invalid;
    }
  if(strcasecmp(((IdTok *)t)->GetName(), "IGNORE"))
    {
      goto invalid;
    }
  int l1, l2;
  CalculateEffectiveLineNumber(comment_tok, l1, l2);
  ilist = new LineIgnoreList(l1, l2, comment_tok->GetTokenIndex());
  if(!ilist)
    OutOfMemory();
  i = 3;
  x = 0;
  while(1)
    {
      t = tc->GetToken(i++);
      if(!t)
      {
	AddToBigIgnoreList(ilist);
	return;
      }
      char *str;
      char *cpy;
      switch(t->GetTokenType())
	{
	case IDENTIFIER:
	  if(!ilist->ignore)
	    {
	      char **arr = new char* [tc->GetCurrentSize()];
	      if(!arr)
		OutOfMemory();
	      ilist->ignore = arr;
	    }
	  str = ((IdTok *)t)->GetName();
	  cpy = new char[strlen(str)+1];
	  if(!cpy)
	    OutOfMemory();
	  strcpy(cpy, str); /* its4: ignore strcpy */
	  ilist->ignore[x++] = cpy;
	  ilist->ignore[x] = 0;
	  continue;
	case OPERATOR:
	  if(!strcmp(((OperatorTok *)t)->GetOperatorName(), ","))
	    continue;
	  /* fallthrough */
	default:
	  goto invalid;
	}
    }
   invalid:
  delete ilist;
  fprintf(stderr, "%s:%d: WARNING: Invalid ITS4 command." NEWLINE,
	  lexer->GetSourceIdentifier(),  t->GetLineNo());
  return;
}

void Scanner::RunScan()
{
  ProcessIgnores();
  TokenContainer *tc = lexer->GetTokens();
  Token *tok;
  IdTok *itok;
  int i = 0;
  int ignore_stream = 0;
  while(1)
    {
      tok = tc->GetToken(i++);
      if(!tok) break;
      TokenId id = tok->GetTokenType();
      char    *name;
      switch(id)
	{
	case IDENTIFIER:
	  if(ignore_stream) continue;
	  itok = (IdTok *)tok;
	  name = itok->GetName();
	  if(!Paranoid())
	    {
	      tok = tc->GetToken(i);
	      if(tok && ((tok->GetTokenType() != OPERATOR) ||
		 strcmp(((OperatorTok *)tok)->GetOperatorName(), "(")))
		{
		  continue;
		}
	    }
	  CheckName(name, tc, i, itok->GetLineNo(), itok->GetEndLineNo());
	  continue;
	case PREPROC_START:
	  tok = tc->GetToken(i++);
	  if(tok && (tok->GetTokenType() != STRING))
	    {
	      if(strcmp(((StringTok *)tok)->GetContents(), "define"))
		{
		  ignore_stream = 1;
		}
	    }
	  continue;
	case PREPROC_END:
	  ignore_stream = 0;
	  continue;
	default:
	  continue;
	}
    }
}

void Scanner::CheckName(char *name, TokenContainer *tc, int i, int startline, int endline)
{
  // First look and see if we should be ignoring this due to an ITS4: command.

  if(IgnoreItOrNo(name, startline, endline, i-1))
    {
      return;
    }
  VulnInfo *v = GetVulnInfo(name);
  if(!v) return;
  if(GetInputScanning() && !v->input) return;
  if(!GetUseHandlers())
    {
      DefaultHandler(v, tc, i, lexer->GetSourceIdentifier());
      return;
    }
  switch(v->handler)
    {
    case 0:
      DefaultHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 1:
      StrcpyHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 2:
      SprintfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 3:
      SnprintfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 4:
      ScanfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 5:
      SscanfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 6:
      TOCTOU_A_Handler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 7:
      TOCTOU_B_Handler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 8:
      TOCTOU_C_Handler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 9:
      FprintfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 10:
      PrintfHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    case 11:
      SyslogHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    default:
      fprintf(stderr, "Undefined handler: %d\nUsing default.", v->handler);
      DefaultHandler(v, tc, i, lexer->GetSourceIdentifier());
      break;
    }
}

int Scanner::IgnoreItOrNo(char *id, int start_line, int end_line, int token_index)
{
  if(IgnoreIts4Commands()) return 0;
  LineIgnoreList *cur = ignore_ptr ? *ignore_ptr : 0;
  if(!cur) return 0;
  while(start_line > cur->line_end)
    {
      if(!cur->next)
	return 0; /* Don't ignore it. */
      ignore_ptr = &(cur->next);
      cur        = *ignore_ptr;
    }
 try_again:
  // If the next ignore item is still to come, return 0 (don't ignore).
  if(end_line < cur->line_start) return 0;
  if(cur->scope_ends_at_token)
    {
      if(cur->token_number < token_index)
	{
	  ignore_ptr = &(cur->next);
	  cur =        *ignore_ptr;
	  goto try_again;
	}
    }
  if(cur->scope_starts_at_token)
    {
      if(cur->token_number > token_index)
	return 0;
    }
  if(!(cur->ignore))
    return 1; // Ignore everything on this line.  
  int i = 0;
  char *s;
  while(1)
    {
      s = cur->ignore[i++];
      if(!s) return 0;
      if(!strcmp(s,id)) return 1;
    }
  /* NOTREACHED */
}

void Scanner::AddToBigIgnoreList(LineIgnoreList *p)
{
  if(!ignore_data_start)
    {
      ignore_data_end = ignore_data_start = p;
      return;
    }
  if(ignore_data_end->line_end == p->line_start)
    {
      if(!ignore_data_end->ignore && !p->ignore)
	{
	  // Extend scope, but add no new information.
	  ignore_data_end->line_end = p->line_end;
	  delete p;
	  return;
	}
      else
	{
	  ignore_data_end->scope_ends_at_token = 1;
	  p->scope_starts_at_token             = 1;
	}
    }
  ignore_data_end->next = p;
  ignore_data_end = p;
}

