#ifndef LOGIN_H
#define LOGIN_H

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * $Id: login.c,v 1.2 2007/04/24 01:43:29 swift Exp $
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Copyright University of Washington.   Written by Dana Swift.
 *
 * This software was developed at the University of Washington using funds
 * generously provided by the US Office of Naval Research, the National
 * Science Foundation, and NOAA.
 *  
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version.
 * 
 * This library 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 Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
/** RCS log of revisions to the C source code.
 *
 * \begin{verbatim}
 * $Log: login.c,v $
 * Revision 1.2  2007/04/24 01:43:29  swift
 * Added acknowledgement and funding attribution.
 *
 * Revision 1.1  2004/12/29 23:11:27  swift
 * Modified LogEntry() to use strings stored in the CODE segment.  This saves
 * lots of space in the DATA segment and significantly speeds code start-up.
 *
 * \end{verbatim}
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
#define loginChangeLog "$RCSfile: login.c,v $  $Revision: 1.2 $   $Date: 2007/04/24 01:43:29 $"

#include <serial.h>

/* function prototypes for external functions */
int login(const struct SerialPort *port, const char *user, const char *pwd);
int logout(const struct SerialPort *port);

#endif /* LOGIN_H */

#include <ctype.h>
#include <string.h>
#include <chat.h>
#include <expect.h>
#include <logger.h>

/* define the prompt to set on the host computer */
#define CMD "cmd>"

/* function prototypes */
unsigned int sleep(unsigned int seconds);

/*------------------------------------------------------------------------*/
/* function to log into a remote computer via the serial port             */
/*------------------------------------------------------------------------*/
/**
   This function logs into a remote computer connected via a serial port.
   It reads from the serial port searching for the log-in and password
   prompts.  Each prompt is responded to with a user-specified response
   strings.

      \begin{verbatim}
      input:
         
         port...A structure that contains pointers to machine dependent
                primitive IO functions.  See the comment section of the
                SerialPort structure for details.  The function checks to be
                sure this pointer is not NULL.

         user...The string transmitted when responding to the login prompt.

         pwd....The string transmitted when responding to the password prompt.

      output:

         This function returns a positive number if the login was
         successful.  Zero is returned if the login failed.  A negative
         number is returned if the function arguments are determined to be
         ill-defined. 
      
      \end{verbatim}
*/
int login(const struct SerialPort *port,const char *user, const char *pwd)
{
   /* function name for log entries */
   static cc FuncName[] = "login()";
   
   /* initialize the return value of this function */
   int status = -1;
  
   /* validate the serialport */
   if (!port) 
   {
      /* create the message */
      static cc msg[]="Serial port not ready.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }
 
   /* verify user name */
   else if (!user)
   {
      /* create the message */
      static cc msg[]="NULL pointer to the user name.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }

   /* verify the password */
   else if (!pwd)
   {
      /* create the message */
      static cc msg[]="NULL pointer to the password.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }

   /* validate the serial port's putb() function */
   else if (!port->putb) 
   {
      /* create the message */
      static cc msg[]="NULL putb() function for serial port.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }

   else
   {
      /* define the timeout period (sec) */
      const time_t timeout=30;

      /* define the login prompt */
      const char *login_prompt="login:";

      /* define the password prompt */
      const char *pwd_prompt="Password:";
      
      /* re-initialize the function's return value */
      status=0;

      /* flush the IO buffers as an initialization step */
      if ((status=pflushio(port))<=0)
      {
         /* create the message */
         static cc msg[]="Attempt to flush IO buffers failed.\n";

         /* make the logentry */
         LogEntry(FuncName,msg);
      }

      else
      {
         int i; char User[32],Pwd[32];

         /* copy and terminate the user and password */
         snprintf(User,sizeof(User)-1,"%s\r",user);
         snprintf(Pwd,sizeof(Pwd)-1,"%s\r",pwd);
         
         /* give 3 chances to login before aborting */
         for (status=0, i=0; status<=0 && i<3; i++)
         {
            /* check if carrier-dectect enabled and CD line not asserted */
            if (port->cd && !port->cd())
            {
               /* create the message */
               static cc msg[]="No carrier detected.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);

               break;
            }
 
            /* write a line-feed to the serial port to initiate the login sequence */
            if (port->putb('\n')<=0)
            {
               /* create the message */
               static cc msg[]="Attempt to write to serial port failed.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);
            }
      
            /* look for the login prompt and enter the user name */
            else if (expect(port,login_prompt,User,timeout,"\n")<=0)
            {
               /* create the message */
               static cc msg[]="Login prompt not received.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);
            }
      
            /* look for the password prompt */
            else if (expect(port,pwd_prompt,Pwd,timeout,"\n")<=0)
            {
               /* create the message */
               static cc msg[]="Password prompt not received.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);
            }

            /* verify that the login was successful */
            else if (expect(port,"Last login:","",timeout,"\n")<=0)
            {
               /* create the message */
               static cc msg[]="Login failed.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);
            }
            
            /* re-initialize return value */
            else
            {
               /* create the message */
               static cc msg[]="Login successful.\n";

               /* make the logentry */
               LogEntry(FuncName,msg);

               /* indicate success */
               status=1;
            }
         }

         /* give 3 chances to reset the command prompt */
         for (i=0; status>0 && i<3; i++)
         {
            /* check if carrier-dectect enabled and CD line not asserted */
            if (port->cd && !port->cd())
            {
               /* create the message */
               static cc msg[]="No carrier detected.\n";
               
               /* make the logentry */
               LogEntry(FuncName,msg);

               /* indicate failure */
               status=0; break;
            }

            if (chat(port,"set prompt = \"" CMD "\"\n",CMD,5,"\n")>0) {sleep(1); break;}
            else
            {
               /* create the message */
               static cc msg[]="Attempt to set the command prompt failed.\n";
               
               /* make the logentry */
               LogEntry(FuncName,msg);
            }
         }
      }
   }
   
   return status;
}

/*------------------------------------------------------------------------*/
/* function to logout of the remote computer                              */
/*------------------------------------------------------------------------*/
/**
   This function logs out of the remote computer attached to the serial
   port.  Unfortunately, there is no way to verify that the logout was
   actually successful.

      \begin{verbatim}
      input:
               
         port...A structure that contains pointers to machine dependent
                primitive IO functions.  See the comment section of the
                SerialPort structure for details.  The function checks to be
                sure this pointer is not NULL.

      output:

         This function returns a positive number if successful.  Zero is
         returned if the attempt failed.  A negative number is returned if
         the function argument was determined to be ill-defined.
      
      \end{verbatim}
*/
int logout(const struct SerialPort *port)
{
   /* function name for log entries */
   static cc FuncName[] = "logout()";

   /* initialize return value */
   int status = -1;
   
   /* validate the serialport */
   if (!port)
   {
      /* create the message */
      static cc msg[]="Serial port not ready.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }

   /* validate the serial port's pputb() function */
   else if (!port->putb)
   {
      /* create the message */
      static cc msg[]="NULL pputb() function for serial port.\n";

      /* make the logentry */
      LogEntry(FuncName,msg);
   }
   
   /* write the logout command */
   else
   {
      int i;

      /* define the timeout period (sec) */
      const time_t timeout=5;

      for (status=0,i=0; i<3; i++)
      {
         /* check if carrier-dectect enabled and CD line not asserted */
         if (port->cd && !port->cd())
         {
            /* create the message */
            static cc msg[]="No carrier detected.\n";

            /* make the logentry */
            LogEntry(FuncName,msg);

            /* indicate failure */
            status=0; break;
         }
         
         if (chat(port,"\003\003\003\003\003\r",CMD,timeout,"\n")<=0)
         {
            /* create the message */
            static cc msg[]="Can't get command prompt.\n";

            /* make the logentry */
            LogEntry(FuncName,msg);
         }
         
         if (chat(port,"exit\r","logout",timeout,"\n")<=0)
         {
            /* create the message */
            static cc msg[]="Attempt to log-out failed.\n";

            /* make the logentry */
            LogEntry(FuncName,msg);
         }
         else
         {
            /* create the message */
            static cc msg[]="Log-out successful.\n";

            /* make the logentry */
            LogEntry(FuncName,msg);

            /* indicate success */
            status=1; sleep(1); break;
         }
      }
   }
   
   return status;
}
