/* STARTSSL example

   Inline upgrading from a clear-text connection to an SSL/TLS connection.

   Some protocols such as SMTP, XMPP, Mysql, Postgresql and others allow, or require,
   that you start the connection without encryption; and then send a command to switch
   over to encryption.

   E.g. a typical SMTP submission would entail a dialog such as this:

   1. client connects to server in the clear
   2. server says hello
   3. client sents a EHLO
   4. server tells the client that it supports SSL/TLS
   5. client sends a 'STARTTLS' to make use of this faciltiy
   6. client/server negiotiate a SSL or TLS connection.
   7. client sends another EHLO
   8. server now tells the client what (else) is supported; such as additional authentication options.
   ... conversation continues encrypted.

   This can be enabled in NetworkClientSecure by telling it to start in plaintext:

          client.setPlainStart();

   and client is than a plain, TCP, connection (just as NetworkClient would be); until the client calls
   the method:

          client.startTLS(); // returns zero on error; non zero on success.

    After which things switch to TLS/SSL.
*/

#include <Arduino.h>
#include <WiFi.h>
#include <NetworkClientSecure.h>

#ifndef WIFI_NETWORK
#define WIFI_NETWORK "YOUR Wifi SSID"
#endif

#ifndef WIFI_PASSWD
#define WIFI_PASSWD "your-secret-password"
#endif

#ifndef SMTP_HOST
#define SMTP_HOST "smtp.gmail.com"
#endif

#ifndef SMTP_PORT
#define SMTP_PORT (587)  // Standard (plaintext) submission port
#endif

const char *ssid = WIFI_NETWORK;        // your network SSID (name of wifi network)
const char *password = WIFI_PASSWD;     // your network password
const char *server = SMTP_HOST;         // Server URL
const int submission_port = SMTP_PORT;  // submission port.

NetworkClientSecure client;

static bool readAllSMTPLines();

void setup() {
  int ret;
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  delay(100);

  Serial.print("Attempting to connect to SSID: ");
  Serial.print(ssid);
  WiFi.begin(ssid, password);

  // attempt to connect to Wifi network:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    // wait 1 second for re-trying
    delay(1000);
  }

  Serial.print("Connected to ");
  Serial.println(ssid);

  Serial.printf("\nStarting connection to server: %s:%d\n", server, submission_port);

  // skip verification for this demo. In production one should at the very least
  // enable TOFU; or ideally hardcode a (CA) certificate that is trusted.
  client.setInsecure();

  // Enable a plain-test start.
  client.setPlainStart();

  if (!client.connect(server, SMTP_PORT)) {
    Serial.println("Connection failed!");
    return;
  };

  Serial.println("Connected to server (in the clear, in plaintest)");

  if (!readAllSMTPLines()) {
    goto err;
  }

  Serial.println("Sending  : EHLO\t\tin the clear");
  client.print("EHLO there\r\n");

  if (!readAllSMTPLines()) {
    goto err;
  }

  Serial.println("Sending  : STARTTLS\t\tin the clear");
  client.print("STARTTLS\r\n");

  if (!readAllSMTPLines()) {
    goto err;
  }

  Serial.println("Upgrading connection to TLS");
  if ((ret = client.startTLS()) <= 0) {
    Serial.printf("Upgrade connection failed: err %d\n", ret);
    goto err;
  }

  Serial.println("Sending  : EHLO again\t\tover the now encrypted connection");
  client.print("EHLO again\r\n");

  if (!readAllSMTPLines()) {
    goto err;
  }

  // normally, as this point - we'd be authenticating and then be submitting
  // an email. This has been left out of this example.

  Serial.println("Sending  : QUIT\t\t\tover the now encrypted connection");
  client.print("QUIT\r\n");

  if (!readAllSMTPLines()) {
    goto err;
  }

  Serial.println("Completed OK\n");
err:
  Serial.println("Closing connection");
  client.stop();
}

// SMTP command repsponse start with three digits and a space;
// or, for continuation, with three digits and a '-'.
static bool readAllSMTPLines() {
  String s = "";
  int i;

  // blocking read; we cannot rely on a timeout
  // of a NetworkClientSecure read; as it is non
  // blocking.
  const unsigned long timeout = 15 * 1000;
  unsigned long start = millis();  // the timeout is for the entire CMD block response; not per character/line.
  while (1) {
    while ((i = client.available()) == 0 && millis() - start < timeout) {
      /* .. wait */
    };
    if (i == 0) {
      Serial.println("Timeout reading SMTP response");
      return false;
    };
    if (i < 0) {
      break;
    }

    i = client.read();
    if (i < 0) {
      break;
    }

    if (i > 31 && i < 128) {
      s += (char)i;
    }
    if (i == 0x0A) {
      Serial.print("Receiving: ");
      Serial.println(s);
      if (s.charAt(3) == ' ') {
        return true;
      }
      s = "";
    }
  }
  Serial.printf("Error reading SMTP command response line: %d\n", i);
  return false;
}

void loop() {
  // do nothing
}
