/*
 * 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.
 */

/**
 * @file role.c 
 * \brief role related logic.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "term.h"
#include "termlist.h"
#include "knowledge.h"
#include "system.h"
#include "debug.h"
#include "error.h"
#include "role.h"

extern int protocolCount;	// from system.c

//! Allocate memory the size of a roledef struct.
Roledef
makeRoledef ()
{
  return (Roledef) malloc (sizeof (struct roledef));
}

//! Print a role event.
/**
 * If print_actor is true, the actor is included (OS version), otherwise it is left out (short stuff)
 */
void
roledefPrintGeneric (Roledef rd, int print_actor)
{
  if (rd == NULL)
    {
      eprintf ("[Empty roledef]");
      return;
    }
  if (rd->type == READ && rd->internal)
    {
      /* special case: internal read == choose ! */
      eprintf ("CHOOSE(");
      termPrint (rd->message);
      eprintf (")");
      return;
    }
  if (rd->type == READ)
    eprintf ("RECV");
  if (rd->type == SEND)
    eprintf ("SEND");
  if (rd->type == CLAIM)
    eprintf ("CLAIM");
  if (rd->label != NULL)
    {
      //! Print label
      Term label;

      /* Old version: sometimes prints protocol stuff (really unique labels)
         label = deVar (rd->label);
         if (protocolCount < 2 && realTermTuple (label))
         {
         // Only one protocol, so we don't need to show the extra label info
         label = TermOp2 (label);
         }
       */
      label = deVar (rd->label);
      if (realTermTuple (label))
	{
	  label = TermOp2 (label);
	}

      eprintf ("_");
      termPrint (label);
    }
  eprintf ("(");
  if (!(rd->from == NULL && rd->to == NULL))
    {
      if (print_actor || rd->type == READ)
	{
	  termPrint (rd->from);
	  eprintf (",");
	}
      if (rd->type == CLAIM)
	eprintf (" ");
      if (print_actor || rd->type != READ)
	{
	  termPrint (rd->to);
	  eprintf (", ");
	}
    }
  termPrint (rd->message);
  eprintf (" )");
}

//! Print a roledef
void
roledefPrint (Roledef rd)
{
  roledefPrintGeneric (rd, 1);
}

//! Print a roledef, but shorten it
void
roledefPrintShort (Roledef rd)
{
  roledefPrintGeneric (rd, 0);
}


//! Duplicate a single role event node.
/**
 *\sa roledefDelete()
 */
Roledef
roledefDuplicate1 (const Roledef rd)
{
  Roledef newrd;

  if (rd == NULL)
    return NULL;
  newrd = makeRoledef ();
  memcpy (newrd, rd, sizeof (struct roledef));
  newrd->next = NULL;
  return newrd;
}

//! Duplicate a role event list.
/**
 *\sa roledefDelete()
 */
Roledef
roledefDuplicate (Roledef rd)
{
  Roledef newrd;

  if (rd == NULL)
    return NULL;
  newrd = roledefDuplicate1 (rd);
  newrd->next = roledefDuplicate (rd->next);
  return newrd;
}

//! Delete a role event or event list.
/**
 *\sa roledefDuplicate()
 */
void
roledefDelete (Roledef rd)
{
  if (rd == NULL)
    return;
  roledefDelete (rd->next);
  free (rd);
  return;
}

//! Destroy a role event or event list.
void
roledefDestroy (Roledef rd)
{
  if (rd == NULL)
    return;
  roledefDestroy (rd->next);
  termDelete (rd->from);
  termDelete (rd->to);
  termDelete (rd->message);
  free (rd);
  return;
}

//! Make a new role event with the specified parameters.
/**
 *@return A pointer to a new role event with the given parameters.
 */
Roledef
roledefInit (int type, Term label, Term from, Term to, Term msg, Claimlist cl)
{
  Roledef newEvent;

  newEvent = makeRoledef ();
  newEvent->internal = 0;
  newEvent->type = type;
  newEvent->label = label;
  newEvent->from = from;
  newEvent->to = to;
  newEvent->message = msg;
  newEvent->forbidden = NULL;	// no forbidden stuff
  newEvent->knowPhase = -1;	// we haven't explored any knowledge yet
  newEvent->claiminfo = cl;	// only for claims
  if (type == READ)
    newEvent->bound = 0;	// bound goal (Used for arachne only). Technically involves choose events as well.
  else
    newEvent->bound = 1;	// other stuff does not need to be bound
  newEvent->next = NULL;
  newEvent->lineno = 0;
  return newEvent;
}

//! Add a role event to an existing list, with the given parameters.
/**
 *\sa roledefInit()
 */
Roledef
roledefAdd (Roledef rd, int type, Term label, Term from, Term to, Term msg,
	    Claimlist cl)
{
  Roledef scan;

  if (rd == NULL)
    return roledefInit (type, label, from, to, msg, cl);

  scan = rd;
  while (scan->next != NULL)
    scan = scan->next;
  scan->next = roledefInit (type, label, from, to, msg, cl);
  return rd;
}

//! Create an empty role structure with a name.
Role
roleCreate (Term name)
{
  Role r;

  r = malloc (sizeof (struct role));
  r->nameterm = name;
  r->roledef = NULL;
  r->locals = NULL;
  r->variables = NULL;
  r->declaredvars = NULL;
  r->declaredconsts = NULL;
  r->initiator = 1;		//! Will be determined later, if a read is the first action (in compiler.c)
  r->singular = false;		// by default, a role is not singular
  r->next = NULL;
  r->knows = NULL;
  r->lineno = 0;
  return r;
}

//! Print a role.
void
rolePrint (Role r)
{
  Roledef rd;

  if (r == NULL)
    return;

  indent ();
  eprintf ("[[Role : ");
  termPrint (r->nameterm);
  eprintf ("]]\n");
  locVarPrint (r->locals);

  rd = r->roledef;
  while (rd != NULL)
    {
      roledefPrint (rd);
      eprintf ("\n");
      rd = rd->next;
    }
}

//! Print a list of roles.
void
rolesPrint (Role r)
{
  if (r == NULL)
    {
      eprintf ("Empty role.");
    }
  else
    {
      while (r != NULL)
	{
	  rolePrint (r);
	  r = r->next;
	}
    }
}

//! Iterate over the events in a roledef list
/**
 * Function gets roledef pointer
 */
int
roledef_iterate_events (Roledef rd, int (*func) ())
{
  while (rd != NULL)
    {
      if (!func (rd))
	return 0;
      rd = rd->next;
    }
  return 1;
}

//! Roledef length
/**
 * Would be faster hard-coded,
 * but this just shows the use of the iteration.
 */
int
roledef_length (const Roledef rd)
{
  int count = 0;
  int countplus (Roledef rd)
  {
    count++;
    return 1;
  }
  roledef_iterate_events (rd, countplus);
  return count;
}

//! Yield roledef pointer for a given index
Roledef
roledef_shift (Roledef rd, int i)
{
  while (i > 0 && rd != NULL)
    {
      rd = rd->next;
      i--;
    }
  return rd;
}

//! Check whether a term is a subterm of a roledef
int
roledefSubTerm (Roledef rd, Term tsub)
{
  if (rd == NULL)
    {
      return false;
    }
  else
    {
      return (termSubTerm (rd->from, tsub) ||
	      termSubTerm (rd->to, tsub) || termSubTerm (rd->message, tsub));
    }
}

/*
 * Some stuff directly from the semantics
 */

//! Is a term readable (from some knowledge set)
/**
 * Returns value of predicate
 */
int
Readable (Knowledge know, Term t)
{
  if (isTermVariable (t))
    {
      // Variable pattern
      return true;
    }
  if (!isTermLeaf (t))
    {
      if (isTermTuple (t))
	{
	  // Tuple pattern
	  Knowledge knowalt;
	  int both;

	  both = false;
	  knowalt = knowledgeDuplicate (know);
	  knowledgeAddTerm (knowalt, TermOp2 (t));
	  if (Readable (knowalt, TermOp1 (t)))
	    {
	      // Yes, left half works
	      knowledgeDelete (knowalt);
	      knowalt = knowledgeDuplicate (know);
	      knowledgeAddTerm (knowalt, TermOp1 (t));
	      if (Readable (knowalt, TermOp2 (t)))
		{
		  both = true;
		}
	    }
	  knowledgeDelete (knowalt);
	  return both;
	}
      else
	{
	  // Encryption pattern
	  // But we exclude functions
	  if (getTermFunction (t) == NULL)
	    {
	      // Real encryption pattern
	      Term inv;
	      int either;

	      // Left disjunct
	      if (inKnowledge (know, t))
		{
		  return true;
		}
	      // Right disjunct
	      inv = inverseKey (know->inverses, TermKey (t));
	      either = false;
	      if (inKnowledge (know, inv))
		{
		  if (Readable (know, TermOp (t)))
		    {
		      either = true;
		    }
		}
	      termDelete (inv);
	      return either;
	    }
	}
    }
  return inKnowledge (know, t);
}

//! Well-formed error reporting.
void
wfeError (Knowledge know, Roledef rd, char *errorstring, Term was,
	  Term shouldbe)
{
  globalError++;
  eprintf ("Well-formedness error.\n");
  roledefPrint (rd);
  eprintf ("\nKnowing ");
  knowledgePrintShort (know);
  eprintf ("\n");
  if (was != NULL || shouldbe != NULL)
    {
      eprintf ("while parsing ");
      termPrint (was);
      if (shouldbe != NULL)
	{
	  eprintf (" which should have been ");
	  termPrint (shouldbe);
	}
      eprintf ("\n");
    }
  globalError--;
  error (errorstring);
}


//! Is an event well-formed
/**
 * Returns the new knowledge or NULL if it was not well-formed.
 */
Knowledge
WellFormedEvent (Term role, Knowledge know, Roledef rd)
{
  if (rd == NULL)
    {
      return know;
    }
  if (rd->type == READ)
    {
      // Read
      if (!isTermEqual (role, rd->to))
	{
	  wfeError (know, rd, "Receiving role incorrect.", rd->to, role);
	  return NULL;
	}
      if (!inKnowledge (know, rd->from))
	{
	  wfeError (know, rd, "Unknown sender role.", rd->from, NULL);
	  return NULL;

	}
      if (!Readable (know, rd->message))
	{
	  wfeError (know, rd, "Cannot read message pattern.", rd->message,
		    NULL);
	  return NULL;
	}
      knowledgeAddTerm (know, rd->message);
      return know;
    }
  if (rd->type == SEND)
    {
      // Send
      if (!isTermEqual (role, rd->from))
	{
	  wfeError (know, rd, "Sending role incorrect.", rd->from, role);
	  return NULL;
	}
      if (!inKnowledge (know, rd->to))
	{
	  wfeError (know, rd, "Unknown receiving role.", rd->to, NULL);
	  return NULL;

	}
      if (!inKnowledge (know, rd->message))
	{
	  wfeError (know, rd, "Unable to construct message.", rd->message,
		    NULL);
	  return NULL;
	}
      return know;
    }
  if (rd->type == CLAIM)
    {
      // Claim
      if (!isTermEqual (role, rd->from))
	{
	  wfeError (know, rd, "Claiming role incorrect.", rd->from, role);
	  return NULL;
	}
      return know;
    }
  // Unknown, false
  globalError++;
  roledefPrint (rd);
  globalError--;
  error ("I don't know this event");
  return NULL;
}
