/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include <stdlib.h>
#include <string.h>

#include "JonahIni.h"

#include "smtp.h"
#include "tcp.h"
#include "commsg.h"



#define CR 13
#define LF 10
#define CRLF "\015\012"
#define SMTP_PORT 25

#define ST_NOT_READY 0
#define ST_READY 1
#define ST_OPEN 2

SMTP::SMTP(void)  {
  struct hostent * he;

  error = 0;
  theRelay[0] = 0;
  state = ST_NOT_READY;

// Check TCP initialized OK...
  error = tcp.error;
  if (error) return;

// Get details of local address...
  if (gethostname(theHost, sizeof(theHost))) {
    error = WSAGetLastError();
    return;
  };

  if ((he = gethostbyname (theHost)) == NULL) {
    error = WSAGetLastError();
    return;
  };
  strncpy(theHost, he->h_name, sizeof(theHost));
  theHost[sizeof(theHost)-1] = 0;

  
// If the inifile has an entry for a relay, read it...  
  if (!IniReadString("Mail", "Relay", theRelay, sizeof(theRelay))) {
    if ((error = addr.setAddress(theRelay)) != 0) return;
  };

  state = ST_READY;

}

SMTP::SMTP(const char * relayName)  {
  struct hostent * he;

  error = 0;
  theRelay[0] = 0;
  state = ST_NOT_READY;

  error = tcp.error;
  if (error) return;


  if (gethostname(theHost, sizeof(theHost)) != 0) {
    error = WSAGetLastError();
    return;
  };

  if ((he = gethostbyname (theHost)) == NULL) {
    error = WSAGetLastError();
    return;
  };
  strncpy(theHost, he->h_name, sizeof(theHost));
  theHost[sizeof(theHost)-1] = 0;


  strncpy(theRelay, relayName, sizeof(theRelay));
 
  if ((error = addr.setAddress(theRelay)) != 0) return;

  state = ST_READY;

}

uint32 SMTP::setRelay(const char * relayName) {
  if (state != ST_READY) return COM_SMTP_STATE_ERROR;
  
  strncpy(theRelay, relayName, sizeof(theRelay));
  
  if ((error = addr.setAddress(theRelay)) != 0) return error;

  if (gethostname(theHost, sizeof(theHost))) {
    error = WSAGetLastError();
  };
  return error;
}


SMTP::~SMTP() {
  free(theRelay);
}

bool SMTP::awaitReply(int & code) {
  char buf[513];
  size_t len;
  unsigned i;
  bool done;
  size_t line_len;
  size_t search_start;

  
  done = false; 
  i = 0;
  line_len = 0;
  search_start = 0;
// read a line terminated by CRLF (this mechanism discards anything after a CRLF,
// but that shouldn't matter in SMTP

  while (!done) {
// Append a segment to the end of the line buffer.
    if ((error = tcp.rcv((unsigned char *)&buf[i], sizeof(buf)-i-1, len, 30000)) != 0) return false;

// Starting at one less than where we left off last time...
    search_start = 0;
    if (line_len > 0) search_start = line_len-1;
    line_len+= len;

// Scan the line, looking for a terminating CRLF
    i = search_start;
    while (i<line_len-1)
      if ((buf[i] == CR) &&(buf[i+1] == LF)) {
        done = 1;
        break;
      } else i++;
  };
  

  code = 0;
  i = 0;
  while (isdigit(buf[i])) {
    code *= 10;
    code += buf[i++] - '0'; 
  };

  return true;
}

uint32 SMTP::sendMessage(const char * from, const char * to, const char * message) {
  if (openChannel(from, to) != 0) return error;
  if (sendData(message) != 0) return error;
  if (closeChannel() != 0) return error;
  return error;
};

  
uint32 SMTP::openChannel(const char * from,
                         const char * to) {
  
  int code;

  if (state != ST_READY) {
    error = COM_SMTP_STATE_ERROR;
    return error;
  };

  if ((error = tcp.open(addr, SMTP_PORT)) != 0) goto errorRet;

// Wait for a 220 greeting...
  if (!awaitReply(code)) goto errorRet;
  if (code != 220) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };

// Send HELO command
  if ((error = tcp.send((unsigned char *)"HELO ", 5)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)theHost, strlen(theHost))) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if (code != 250) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };

// Send MAIL command
  if ((error = tcp.send((unsigned char *)"MAIL FROM:", 10)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)from, strlen(from))) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if (code != 250) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };


// Send RCPT command
  if ((error = tcp.send((unsigned char *)"RCPT TO:", 8)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)to, strlen(to))) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if ((code != 250) && (code != 251)) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };

// Send DATA command
  if ((error = tcp.send((unsigned char *)"DATA", 4)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if (code != 354) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };



  state = ST_OPEN;
  return 0;

errorRet:
  tcp.close();
  state = ST_READY;
  return error;
}


uint32 SMTP::sendData(const char * message) {
  bool firstChar;
  int start;
  int end;

  if (state != ST_OPEN) {
    error = COM_SMTP_STATE_ERROR;
    return error;
  };


  start = 0; end=0; firstChar = true;
  while (message[end] != 0) {
    if (message[end] == '\n') {
      if ((error = tcp.send((unsigned char *)&message[start], end-start)) != 0) goto errorRet;
      if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;
      start = end+1; end = start;
      firstChar = true; continue;
    } else if (firstChar && (message[end] == '.')) {
      if ((error = tcp.send((unsigned char *)".", 1)) != 0) goto errorRet;
    } else {
      end++;
    };
    firstChar = false;
  };
  if (end > start) {
    if ((error = tcp.send((unsigned char *)&message[start], end-start)) != 0) goto errorRet;
    if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;
  };

  return error;

errorRet:
  tcp.close();
  return error;
}




uint32 SMTP::closeChannel(void) {
  int code;

  if (state != ST_OPEN) {
    error = COM_SMTP_STATE_ERROR;
    return error;
  };
  
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)".", 1)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if (code != 250) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };

// Send QUIT command  
  if ((error = tcp.send((unsigned char *)"QUIT", 4)) != 0) goto errorRet;
  if ((error = tcp.send((unsigned char *)CRLF, 2)) != 0) goto errorRet;

  if (!awaitReply(code)) goto errorRet;
  if (code != 221) {
    error = COM_SMTP_STATE_ERROR;
    goto errorRet;
  };

  error = 0;

errorRet:
  tcp.close();
  return error;

}


