/*
 * Scyther : An automatic verifier for security protocols.
 * Copyright (C) 2007 Cas Cremers
 * 
 * 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 the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * Initial intruder knowledge computation.
 */

#include "intruderknowledge.h"

//! Add a (copy of) a term to the intruder knowledge
void
addSTerm (const System sys, Term t, Termlist fromlist, Termlist tolist)
{
  Term t2;

  t2 = termLocal (t, fromlist, tolist);

  if (switches.check)
    {
      globalError++;
      eprintf ("[ Adding ");
      termPrint (t2);
      eprintf (" to the initial intruder knowledge]\n");
      globalError--;
    }

  knowledgeAddTerm (sys->know, t2);
}

//! Unfold the term for all possible options
void
addEnumTerm (const System sys, Term t, Term actor, Termlist todo,
	     Termlist fromlist, Termlist tolist)
{
  if (todo == NULL)
    {
      addSTerm (sys, t, fromlist, tolist);
    }
  else
    {
      if (termSubTerm (t, todo->term))
	{
	  // Occurs, we have to iterate
	  void iterateThis (Term to)
	  {
	    tolist = termlistPrepend (tolist, to);

	    addEnumTerm (sys, t, actor, todo->next, fromlist, tolist);

	    tolist = termlistDelTerm (tolist);
	  }

	  fromlist = termlistPrepend (fromlist, todo->term);
	  if (isTermEqual (todo->term, actor))
	    {
	      // Untrusted agents only
	      Termlist tl;

	      for (tl = sys->untrusted; tl != NULL; tl = tl->next)
		{
		  iterateThis (tl->term);
		}
	    }
	  else
	    {
	      // any agents
	      Termlist tl;

	      for (tl = sys->agentnames; tl != NULL; tl = tl->next)
		{
		  iterateThis (tl->term);
		}
	    }
	  fromlist = termlistDelTerm (fromlist);
	}
      else
	{
	  // Simply proceed to next
	  addEnumTerm (sys, t, actor, todo->next, fromlist, tolist);
	}
    }
}

//! Does t contain any of sublist?
int
anySubTerm (Term t, Termlist sublist)
{
  while (sublist != NULL)
    {
      if (termSubTerm (t, sublist->term))
	{
	  return true;
	}
      sublist = sublist->next;
    }
  return false;
}

void
initialIntruderKnowledge (const System sys)
{
  /*
     TODO this is buggy and leads to a segfault with Simon's example (bug report for wellformedness check)
     Disabled for now.
     ./scyther-linux simple-challenge-SEGFAULT.spdl
   */
  return;

  /*
   * display initial role knowledge
   */
  int deriveFromRole (Protocol p, Role r)
  {
    void addListKnowledge (Termlist tl, Term actor)
    {
      void addTermKnowledge (Term t)
      {
	if (anySubTerm (t, p->rolenames))
	  {
	    Term f;
	    // Has rolename subterms. We have to enumerate those.
	    /**
	     * Hack. Enumerating is not always good (or even desirable).
	     * If some I knows sk(I), sk should not be in the intruder knowledge.
	     * But for hash(I), we typically would have h; but if it is never used differently, it would suffice.
	     * To summarize, the operational semantics definition is perfectly fine, but maybe a bit strict sometimes.
	     *
	     * The hack is that if function application:
	    */
	    f = getTermFunction (t);
	    if (f != NULL)
	      {
		// it's a function, right. So we see whether it is public. It is if it does not contain the actor...
		if (!termSubTerm (t, actor))
		  {
		    // no actor, then nothing secret I guess.
		    addSTerm (sys, f, NULL, NULL);
		    return;
		  }
		else
		  {
		    // has actor. but does it contain even more?

		    int allagents (Term t)
		    {
		      if (!inTermlist (sys->agentnames, t))
			{
			  if (!inTermlist (p->rolenames, t))
			    {
			      return false;
			    }
			}
		      return true;
		    }

		    if (!term_iterate_leaves (TermOp (t), allagents))
		      {
			// something else as well, so that probably means a hash or something like that.
			addSTerm (sys, f, NULL, NULL);
			return;
		      }
		  }
	      }
	    // otherwise, we enumerate
	    addEnumTerm (sys, t, actor, p->rolenames, NULL, NULL);
	  }
	else
	  {
	    // No actor subterm. Simply add.
	    addSTerm (sys, t, NULL, NULL);
	  }
      }

      while (tl != NULL)
	{
	  addTermKnowledge (tl->term);
	  tl = tl->next;
	}
    }

    if (switches.check)
      {
	globalError++;
	eprintf ("Role ");
	termPrint (r->nameterm);
	eprintf (" knows ");
	termlistPrint (r->knows);
	eprintf ("\n");
	globalError--;
      }
    addListKnowledge (r->knows, r->nameterm);
    return true;
  }

  if (switches.check)
    {
      globalError++;
      eprintf ("Computing initial intruder knowledge.\n\n");
      eprintf ("Agent names      : ");
      termlistPrint (sys->agentnames);
      eprintf ("\n");
      eprintf ("Untrusted agents : ");
      termlistPrint (sys->untrusted);
      eprintf ("\n");
      globalError--;
    }

  iterateRoles (sys, deriveFromRole);
}
