/*
 *
 *  Visual Voicemail Daemon
 *
 *  Copyright (C) 2021, Chris Talbot <chris@talbothome.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "vvm.h"
#include "vvmutil.h"

char
*
parse_email_address (const char *input)
{
  g_autofree gchar **input_parts = NULL;
  g_autofree gchar **full_email = NULL;
  g_autofree gchar **email = NULL;

  int full_email_length;
  int input_parts_length;

  //T-Mobile does "VOICE=$NUMBER@domain.com"
  input_parts = g_strsplit (input, "=", 2);
  input_parts_length = g_strv_length (input_parts);
  //The email can come in the form:
  //"$NUMBER" <$NUMBER@domain.com>
  full_email = g_strsplit (input_parts[input_parts_length - 1], " ", -1);
  full_email_length = g_strv_length (full_email);

  g_strdelimit (full_email[full_email_length - 1], "<>", ' ');
  g_strstrip (full_email[full_email_length - 1]);

  email = g_strsplit (full_email[full_email_length - 1], "@", 2);
  return g_strdup (email[0]);
}

static char
*
vvm_util_create_att_usa_status_sms (const char *carrier_prefix)
{
  g_warning ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  g_warning ("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!");
  g_warning ("!!!!!   AT&T USA uses a non-standard protocol for subscribing and   !!!!!!");
  g_warning ("!!!!!   unsubscribing. It may change at anytime and no longer work  !!!!!!");
  g_warning ("!!!!!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!WARNING!!!!!!");
  g_warning ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  return g_strdup_printf ("GET?c=ATTV:Pixel 5/11:4.4.0.10183&v=1.0&l=%s&AD", carrier_prefix);
  // Attributes for AT&T USA:
  // Pixel 5 => Model Number
  // 11 => Android 11
  // 4.4.0.10183 => AT&T Visual Voicemail Version
  // 1.0 => Version of VVM I guess?
  // carrier_prefix => put your number with no spaces
  //                 (i.e if your number is (999) 555-1123, use 9995551123
}

/*
 * 'STATE?' works for France Free Mobile However, 'STATE' reliably works
 * for several carriers. As of now, just changed all of them to 'STATE'
 * and we can work out other issues if they come up later
 */
static char
*
vvm_util_create_ios_status_sms (const char *carrier_prefix)
{
  return g_strdup_printf ("STATE");
}

char *
vvm_util_create_deactivate_sms (const char *carrier_prefix,
                                const char *vvm_type)
{
  if (g_strcmp0 (vvm_type, "cvvm") == 0)
    return g_strdup_printf ("%s%s", DEACTIVATE_SMS_PREFIX, CCVM_SUFFIX);
  else if (g_strcmp0 (vvm_type, "otmp") == 0)
    /*
     * Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.3
     * It should look similar to: "Deactivate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:"
     * message = g_strdup("Deactivate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:");
     * message = g_strdup("%s%s%s;%s<value>;%s<value>;<Clientprefix>:", DEACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * pv should be "13", I do not know the values for "ct" and "pt"
     * I think if <Clientprefix> is default (//VVM), it can be "Deactivate:pv=<value>;ct=<value>;pt=<value>"
     * message = g_strdup("Deactivate:pv=<value>;ct=<value>;pt=<value>");
     * or with vvmutil.h
     * message = g_strdup("%s%s%s;%s<value>;%s<value>", DEACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * In looking at Android: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/sms/OmtpMessageSender.java
     * They did not implement how to activate nor deactivate OMTP.
     * Therefore, I doubt any carrier actually implements this.
     */
    g_warning ("I do not know how to unsubscribe from an OMTP VVM service!");
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    g_critical ("I do not know how to unsubscribe from AT&T USA!");
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    g_critical ("I do not know how to unsubscribe from Free Mobile!");
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    g_critical ("I do not know how to unsubscribe from ios!");
  else if (g_strcmp0 (vvm_type, "vvm3") == 0)
    g_warning ("There is no way to unsubscribe from vvm3!");
  else
    g_critical ("Unknown type of VVM service.");
  return NULL;
}

char *
vvm_util_create_activate_sms (const char *carrier_prefix,
                              const char *vvm_type)
{
  if (g_strcmp0 (vvm_type, "cvvm") == 0)
    return g_strdup_printf ("%s%s", ACTIVATE_SMS_PREFIX, CCVM_SUFFIX);
  else if (g_strcmp0 (vvm_type, "otmp") == 0)
    /* Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.2
     * It should look similar to: "Activate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:"
     * message = g_strdup("Activate:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:");
     * or with vvmutil.h
     * message = g_strdup("%s%s%s;%s<value>;%s<value>;<Clientprefix>:", ACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * pv should be "13", I do not know the values for "ct" and "pt"
     * I think if <Clientprefix> is default (//VVM), it can be:"Activate:pv=<value>;ct=<value>;pt=<value>"
     * message = g_strdup("Activate:pv=<value>;ct=<value>;pt=<value>");
     * or with vvmutil.h
     * message = g_strdup("%s%s%s;%s<value>;%s<value>", ACTIVATE_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * In looking at Android: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/sms/OmtpMessageSender.java
     * They did not implement how to activate nor deactivate OMTP.
     * Therefore, I doubt any carrier actually implements this.
     */
    g_warning ("I do not know how to subscribe to an OMTP VVM service!");
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    return vvm_util_create_att_usa_status_sms (carrier_prefix);
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    return vvm_util_create_ios_status_sms (carrier_prefix);
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    return vvm_util_create_ios_status_sms (carrier_prefix);
  /* There is no specific activate request for vvm3, you just send a
   * status message. If it isn't activated, then there will be a place
   * later it activates
   */
  else if (g_strcmp0 (vvm_type, "vvm3") == 0)
    return g_strdup_printf ("STATUS");
  else
    g_critical ("Unknown type of VVM service.");
  return NULL;
}

char *
vvm_util_create_status_sms (const char *carrier_prefix,
                            const char *vvm_type)
{
  DBG ("VVM type: %s", vvm_type);
  if (g_strcmp0 (vvm_type, "cvvm") == 0)
    return g_strdup_printf ("%s%s", STATUS_SMS_PREFIX, CCVM_SUFFIX);
  else if (g_strcmp0 (vvm_type, "otmp") == 0)
    /* Refer to "OMTP_VVM_Specification v1.3, Section 2.9.2.1
     * The message should look similar to "STATUS:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:"
     * message = g_strdup("STATUS:pv=<value>;ct=<value>;pt=<value>;<Clientprefix>:");
     * or with vvmutil.h
     * message = g_strdup("%s%s%s;%s<value>;%s<value>;<Clientprefix>:", STATUS_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * pv should be "13", I do not know the values for "ct" and "pt"
     * I think if <Clientprefix> is default (//VVM), it can be: "STATUS:pv=<value>;ct=<value>;pt=<value>"
     * message = g_strdup("STATUS:pv=<value>;ct=<value>;pt=<value>");
     * or with vvmutil.h
     * message = g_strdup("%s%s%s;%s<value>;%s<value>;", STATUS_SMS_PREFIX, PROTOCOL_VERSION_PREFIX, PROTOCOL_VERSION_1_3, CLIENT_TYPE_PREFIX, TERMINAL_DESTINATION_PORT_NUMBER_PREFIX);
     * In looking at Android: https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/voicemail/impl/sms/OmtpMessageSender.java
     * They did not implement how to check the status of OMTP.
     * Therefore, I doubt any carrier actually implements this.
     */
    g_warning ("I do not know how check the status of an OMTP VVM service!");
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    return vvm_util_create_att_usa_status_sms (carrier_prefix);
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    return vvm_util_create_ios_status_sms (carrier_prefix);
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    return vvm_util_create_ios_status_sms (carrier_prefix);
  else if (g_strcmp0 (vvm_type, "vvm3") == 0)
    return g_strdup_printf ("STATUS");
  else
    g_critical ("Unknown type of VVM service.");
  return NULL;
}

static int
vvm_util_parse_otmp_sms_message_type (const char *message,
                                      const char *carrier_prefix)
{
  g_autofree char *status_prefix = NULL;
  g_autofree char *sync_prefix = NULL;

  status_prefix = g_strdup_printf ("%s:STATUS:", carrier_prefix);
  sync_prefix = g_strdup_printf ("%s:SYNC:", carrier_prefix);

  if (g_str_has_prefix (message, status_prefix))
    {
      DBG ("This is a status SMS message");
      return SMS_MESSAGE_STATUS;
    }
  else if (g_str_has_prefix (message, sync_prefix))
    {
      DBG ("This is a sync SMS message");
      return SMS_MESSAGE_SYNC;
    }
  else {
      DBG ("Not a status or sync SMS message");
      return SMS_MESSAGE_OTHER;
    }
}

static int
vvm_util_parse_att_usa_sms_message_type (const char *message,
                                         const char *carrier_prefix)
{
  if (g_str_match_string ("vvm.mobile.att.net", message, FALSE))
    // AT&T has pretty much identical STATUS and SYNC messages, and the SYNC
    // Messages aren't helpful, just process all as STATUS SMS
    return SMS_MESSAGE_STATUS;
  else if (g_str_has_prefix (message, "GET?AD="))
    // This is from AWESim
    return SMS_MESSAGE_STATUS;
  else if (g_str_has_prefix (message, "STATE?state="))
    // This is a new user message
    return SMS_MESSAGE_STATUS;
  else {
      DBG ("Not a status or sync SMS message");
      return SMS_MESSAGE_OTHER;
    }
}


static int
vvm_util_parse_ios_sms_message_type (const char *message,
                                     const char *carrier_prefix)
{
  if (g_str_has_prefix (message, "STATE?state="))
    return SMS_MESSAGE_STATUS;
  else if (g_str_has_prefix (message, "MBOXUPDATE?m="))
    // This is a new user message
    return SMS_MESSAGE_SYNC;
  else {
      DBG ("Not a status or sync SMS message");
      return SMS_MESSAGE_OTHER;
    }
}
int
vvm_util_parse_sms_message_type (const char *message,
                                 const char *carrier_prefix,
                                 const char *vvm_type)
{
  int sms_message_type;
  if ((g_strcmp0 (vvm_type, "cvvm") == 0) ||
      (g_strcmp0 (vvm_type, "otmp") == 0) ||
      (g_strcmp0 (vvm_type, "vvm3") == 0))
    sms_message_type = vvm_util_parse_otmp_sms_message_type (message, carrier_prefix);
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    sms_message_type = vvm_util_parse_att_usa_sms_message_type (message, carrier_prefix);
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    sms_message_type = vvm_util_parse_ios_sms_message_type (message, carrier_prefix);
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    sms_message_type = vvm_util_parse_ios_sms_message_type (message, carrier_prefix);
  else {
      g_critical ("Unknown VVM type!");
      sms_message_type = SMS_MESSAGE_OTHER;
    }
  return sms_message_type;
}

static void
vvm_util_parse_sync_otmp_sms_message (const char                 *message,
                                      struct sms_control_message *sms_msg)
{
  gchar **status_parts, **settings, **single_setting;
  unsigned int status_parts_length, settings_length, adjust_factor;
  /*
   * Example SYNC Message from OTMP VVM Specification:
   * //VVM:SYNC:ev=NM;id=3446456;c=1;t=v;s=01234567898;dt=02/08/200812:53 +0200;l=30
   */

  status_parts = g_strsplit (message, ":", 3);
  status_parts_length = g_strv_length (status_parts);

  if (strlen (status_parts[status_parts_length - 1]) == 0)
    adjust_factor = 2;
  else
    adjust_factor = 1;
  settings = g_strsplit (status_parts[status_parts_length - adjust_factor], ";", 0);
  settings_length = g_strv_length (settings);

  g_strfreev (status_parts);

  for (int i = 0; i < settings_length; i++)
    {
      if (settings[i] == NULL)
        continue;
      if (strlen (settings[i]) < 1)
        continue;
      single_setting = g_strsplit (settings[i], "=", 2);
      if (single_setting[1] == NULL)
        continue;
      if (strlen (single_setting[1]) < 1)
        continue;
      if (g_strcmp0 (single_setting[0], "ev") == 0)            //event that triggered the SYNC SMS.
        {
          if (g_strcmp0 (single_setting[1], "NM") == 0)
            sms_msg->sync_status_reason = SYNC_SMS_NEW_MESSAGE;
          else if (g_strcmp0 (single_setting[1], "MBU") == 0)
            sms_msg->sync_status_reason = SYNC_SMS_MAILBOX_UPDATE;
          else if (g_strcmp0 (single_setting[1], "GU") == 0)
            sms_msg->sync_status_reason = SYNC_SMS_GREETINGS_VOICE_SIGNATURE_UPDATE;
          else
            sms_msg->sync_status_reason = SYNC_SMS_UNKNOWN;
        }
      else if (g_strcmp0 (single_setting[0], "id") == 0)              //UID of the message in the Mailbox
        sms_msg->uid = g_strdup (single_setting[1]);
      else if (g_strcmp0 (single_setting[0], "c") == 0)              //Type of Message
        sms_msg->new_mailbox_messages = g_strdup (single_setting[1]);
      else if (g_strcmp0 (single_setting[0], "t") == 0)              //Type of Message
        {
          if (g_strcmp0 (single_setting[1], "v") == 0)
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_VOICE;
          else if (g_strcmp0 (single_setting[1], "o") == 0)
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_VIDEO;
          else if (g_strcmp0 (single_setting[1], "f") == 0)
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_FAX;
          else if (g_strcmp0 (single_setting[1], "i") == 0)
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_INFOTAINMENT;
          else if (g_strcmp0 (single_setting[1], "e") == 0)
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_ECC;
          else
            sms_msg->mailbox_message_type = MAILBOX_MESSAGE_UNKNOWN;
        }
      else if (g_strcmp0 (single_setting[0], "s") == 0)              //Message Sender
        sms_msg->message_sender = g_strdup (single_setting[1]);
      else if (g_strcmp0 (single_setting[0], "dt") == 0)              //Date of Message
        // format DD/MM/YYYY HH:MM TZ
        sms_msg->message_date = g_strdup (single_setting[1]);
      else if (g_strcmp0 (single_setting[0], "l") == 0)              //Date of Message
        sms_msg->message_length = g_strdup (single_setting[1]);
      else
        DBG ("Not procesing setting %s", settings[i]);
      g_strfreev (single_setting);
    }
  g_strfreev (settings);
}

void
vvm_util_parse_sync_att_usa_sms_message (const char                 *message,
                                         struct sms_control_message *sms_msg)
{
  g_critical ("AT&T has no sync message!!");
}

void
vvm_util_parse_sync_ios_sms_message (const char                 *message,
                                     struct sms_control_message *sms_msg)
{
  DBG ("iOS Sync message contain only the number of new messages.");
  sms_msg->sync_status_reason = SYNC_SMS_MAILBOX_UPDATE;
}

void
vvm_util_parse_sync_sms_message (const char                 *message,
                                 struct sms_control_message *sms_msg,
                                 const char                 *vvm_type)
{
  DBG ("Parsing VVM sync message.");
  sms_msg->type = g_strdup ("sync");

  if ((g_strcmp0 (vvm_type, "cvvm") == 0) ||
      (g_strcmp0 (vvm_type, "otmp") == 0) ||
      (g_strcmp0 (vvm_type, "vvm3") == 0))
    vvm_util_parse_sync_otmp_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    vvm_util_parse_sync_att_usa_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    vvm_util_parse_sync_ios_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    vvm_util_parse_sync_ios_sms_message (message, sms_msg);
  else
    g_critical ("Unknown VVM type!");
}

void
vvm_util_parse_status_otmp_sms_message (const char                 *message,
                                        struct sms_control_message *sms_msg)
{
  gchar **status_parts, **settings, **single_setting;
  unsigned int settings_length;
  /*
   * Example STATUS Message from OTMP VVM Specification:
   * //VVM :STATUS:st=N;rc=0;srv=1:10.115.67.251;tui=123;dn=999;ipt=143;spt=25; u=78236487@wirelesscarrier.com;pw=32u4yguetrr34;lang=eng|fre;g_len=25;vs_len=15;pw_len=4-6;smtp_u=super_user@wirelesscarrier.com;smtp_pw=48769463wer;pm=Y;gm=N;vtc=D;vt=1
   */

  status_parts = g_strsplit (message, ":", 3);

  settings = g_strsplit (status_parts[2], ";", 0);
  settings_length = g_strv_length (settings);

  g_strfreev (status_parts);

  for (int i = 0; i < settings_length; i++)
    {
      if (settings[i] == NULL)
        continue;
      if (strlen (settings[i]) < 1)
        continue;
      single_setting = g_strsplit (settings[i], "=", 2);
      if (single_setting[1] == NULL)
        continue;
      if (strlen (single_setting[1]) < 1)
        continue;
      if (g_strcmp0 (single_setting[0], "st") == 0)            //Provisioning Status
        {
          if (g_strcmp0 (single_setting[1], "N") == 0)
            {
              // N = Subscriber New
              sms_msg->provision_status = VVM_PROVISION_STATUS_NEW;
              DBG ("Provisioning Status: New.");
            }
          else if (g_strcmp0 (single_setting[1], "R") == 0)
            {
              // R = Subscriber Ready
              sms_msg->provision_status = VVM_PROVISION_STATUS_READY;
              DBG ("Provisioning Status: Ready.");
            }
          else if (g_strcmp0 (single_setting[1], "P") == 0)
            {
              // P = Subscriber Provisioned
              sms_msg->provision_status = VVM_PROVISION_STATUS_PROVISIONED;
              DBG ("Provisioning Status: Provisioned.");
            }
          else if (g_strcmp0 (single_setting[1], "U") == 0)
            {
              // U = Subscriber Unknown
              sms_msg->provision_status = VVM_PROVISION_STATUS_UNKNOWN;
              DBG ("Provisioning Status: Unknown.");
            }
          else if (g_strcmp0 (single_setting[1], "B") == 0)
            {
              // B = Subscriber Blocked
              sms_msg->provision_status = VVM_PROVISION_STATUS_BLOCKED;
              DBG ("Provisioning Status: Blocked.");
            }
          else
            sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET;
        }
      else if (g_strcmp0 (single_setting[0], "rc") == 0)              //Return Code
        {
          if (g_strcmp0 (single_setting[1], "0") == 0)
            //0 = Success
            DBG ("Return Code: Success.");
          else if (g_strcmp0 (single_setting[1], "1") == 0)
            //1 = System error
            DBG ("Return Code: System error.");
          else if (g_strcmp0 (single_setting[1], "2") == 0)
            //2 = Subscriber error
            DBG ("Return Code: Subscriber error.");
          else if (g_strcmp0 (single_setting[1], "3") == 0)
            //3 = Mailbox unknown
            DBG ("Return Code: Mailbox unknown.");
          else if (g_strcmp0 (single_setting[1], "4") == 0)
            //4 = VVM not activated
            DBG ("Return Code: VVM not activated.");
          else if (g_strcmp0 (single_setting[1], "5") == 0)
            //5 = VVM not provisioned
            DBG ("Return Code: System error.");
          else if (g_strcmp0 (single_setting[1], "6") == 0)
            //6 = VVM client unknown
            DBG ("Return Code: VVM not provisioned.");
          else if (g_strcmp0 (single_setting[1], "7") == 0)
            //7 = VVM mailbox not initialised
            DBG ("Return Code: VVM mailbox not initialised.");
          else
            g_critical ("Unknown Return Code.");
        }
      else if (g_strcmp0 (single_setting[0], "srv") == 0)              //Mailbox Hostname
        sms_msg->mailbox_hostname = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "ipt") == 0)              //Mailbox Port
        sms_msg->mailbox_port = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "u") == 0)              //Mailbox Username
        sms_msg->mailbox_username = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "pw") == 0)              //Mailbox Password
        sms_msg->mailbox_password = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "lang") == 0)              //Language
        sms_msg->language = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "g_len") == 0)              //Greeting Length
        sms_msg->greeting_length = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "vs_len") == 0)              //Voice Signature Length
        sms_msg->voice_signature_length = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "pw_len") == 0)              //TUI Password Length
        sms_msg->TUI_password_length = g_strdup (single_setting[1]);

      else if (g_strcmp0 (single_setting[0], "vmg_url") == 0)              //vvm3: URL for activation
        sms_msg->activate_url = g_strdup (single_setting[1]);

      else
        DBG ("Not procesing setting %s", single_setting[0]);
      g_strfreev (single_setting);
    }
  g_strfreev (settings);
}

char
*
decode (const char *input)
{
  gsize base64_out_len;
  g_autofree unsigned char *base_64_decoded = NULL;
  g_autofree char *decoded_string = NULL;
  GString *decoded = g_string_new (NULL);
  char *to_return;

  base_64_decoded = g_base64_decode (input, &base64_out_len);
  decoded_string = g_strndup ((char *)base_64_decoded, base64_out_len);
  for (int i = 0; i < strlen (decoded_string); i++)
    {
      const char *decode[] = {
        "XTQ^ZSUU_Y00000",
        "YUP_[RTT^X11111",
        "ZVS\\XQWW][22222",
        "[WR]YPVV\\Z33333",
        "\\PUZ^WQQ[]44444",
        "]QT[_VPPZ\\55555",
        "^RWX\\USSY_66666",
        "_SVY]TRRX^77777",
        "P\\YVR[]]WQ88888",
        "Q]XWSZ\\\\VP99999"
      };
      for (int j = 0; j < 10; j++)
        {
          if (i > 14)
            {
              g_warning ("This string should not be longer than 15!");
              decoded = g_string_append_unichar (decoded, decoded_string[i]);
            }
          else if (decode[j][i] == decoded_string[i])
            decoded = g_string_append_c (decoded, j + '0');
        }
    }
  to_return = g_string_free (decoded, FALSE);
  return to_return;
}

void
vvm_util_parse_status_ios_sms_message (const char                 *message,
                                       struct sms_control_message *sms_msg)
{
  g_autofree gchar **status_parts = NULL;
  g_autofree gchar **hostname_parts = NULL;
  g_autofree gchar **other_parts = NULL;
  int other_parts_length;

  if (g_str_has_prefix (message, "STATE?state="))
    {
      message = message + 6;
      other_parts = g_strsplit (message, ";", -1);
      other_parts_length = g_strv_length (other_parts);
      for (int i = 0; i < other_parts_length; i++)
        {
          g_autofree gchar **single_setting = NULL;
          if (strlen (other_parts[i]) < 1)
            continue;
          if (other_parts[i] == NULL)
            continue;
          single_setting = g_strsplit (other_parts[i], "=", 2);
          if (single_setting[1] == NULL)
            continue;
          if (strlen (single_setting[1]) < 1)
            continue;
          if (g_strcmp0 (single_setting[0], "state") == 0)
            {
              if (g_strcmp0 (single_setting[1], "NewAccount") == 0)
                {
                  sms_msg->provision_status = VVM_PROVISION_STATUS_NEW;
                  DBG ("Provisioning Status: New.");
                }
              else if (g_strcmp0 (single_setting[1], "Active") == 0)
                {
                  sms_msg->provision_status = VVM_PROVISION_STATUS_READY;
                  DBG ("Provisioning Status: Active.");
                }
              else if (g_strcmp0 (single_setting[1], "NotAvailable") == 0)
                {
                  sms_msg->provision_status = VVM_PROVISION_STATUS_BLOCKED;
                  DBG ("Provisioning Status: Not Available.");
                }
              else
                sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET;
            }
          else if (g_strcmp0 (single_setting[0], "server") == 0)
            sms_msg->mailbox_hostname = g_strdup (single_setting[1]);
          else if (g_strcmp0 (single_setting[0], "pw") == 0)
            sms_msg->mailbox_password = g_strdup (single_setting[1]);
          else if (g_strcmp0 (single_setting[0], "port") == 0)                  //Mailbox Port
            {
              if (g_str_match_string ("143", single_setting[1], FALSE))
                sms_msg->mailbox_port = g_strdup ("143");
              else
                sms_msg->mailbox_port = g_strdup (single_setting[1]);
            }
          else if (g_strcmp0 (single_setting[0], "name") == 0)
            {
              sms_msg->mailbox_username = g_strdup (single_setting[1]);
              if (strstr (sms_msg->mailbox_username, "CMSGROUP"))
                {
                  char **name_parts = NULL;
                  name_parts = g_strsplit (sms_msg->mailbox_username, ":", -1);

                  g_clear_pointer (&sms_msg->mailbox_username, g_free);
                  sms_msg->mailbox_username = g_strdup (name_parts[1]);
                  g_strfreev (name_parts);
                }
            }
          else
            //I have no idea what these settings are
            DBG ("Not procesing setting %s", single_setting[0]);
        }
    }
}

void
vvm_util_parse_status_att_usa_sms_message (const char                 *message,
                                           struct sms_control_message *sms_msg)
{
  g_autofree gchar **status_parts = NULL;
  g_autofree gchar **hostname_parts = NULL;
  g_autofree gchar **other_parts = NULL;
  int other_parts_length;

  if (g_str_has_prefix (message, "STATE?state="))
    {
      vvm_util_parse_status_ios_sms_message (message, sms_msg);
    }
  else {
      if (g_str_has_prefix (message, "GET?AD="))
        {
          g_autofree gchar *stripped_message = NULL;

          message = message + 7;
          stripped_message = g_strdup (message);
          g_strdelimit (stripped_message, "\"", ' ');
          g_strstrip (stripped_message);

          status_parts = g_strsplit (stripped_message, "?", 2);
        }
      else
        status_parts = g_strsplit (message, "?", 2);

      hostname_parts = g_strsplit (status_parts[0], ":", 2);
      sms_msg->mailbox_hostname = g_strdup (hostname_parts[0]);
      DBG ("Hostname: %s", sms_msg->mailbox_hostname);

      other_parts = g_strsplit (status_parts[1], "&", -1);
      other_parts_length = g_strv_length (other_parts);
      for (int i = 0; i < other_parts_length; i++)
        {
          g_autofree gchar **single_setting = NULL;
          if (strlen (other_parts[i]) < 1)
            continue;
          if (other_parts[i] == NULL)
            continue;
          single_setting = g_strsplit (other_parts[i], "=", 2);
          if (single_setting[1] == NULL)
            continue;
          if (strlen (single_setting[1]) < 1)
            continue;
          if (g_strcmp0 (single_setting[0], "m") == 0)                //Mailbox Username
            sms_msg->mailbox_username = g_strdup (single_setting[1]);

          else if (g_strcmp0 (single_setting[0], "p") == 0)                  //Mailbox Password
            sms_msg->mailbox_password = decode (single_setting[1]);

          else if (g_strcmp0 (single_setting[0], "i") == 0)                  //Mailbox Port
            {
              if (g_str_match_string ("143", single_setting[1], FALSE))
                sms_msg->mailbox_port = g_strdup ("143");
              else
                sms_msg->mailbox_port = g_strdup (single_setting[1]);
            }
          else if (g_strcmp0 (single_setting[0], "S") == 0)                  //Provisioning Status
            {
              if (g_strcmp0 (single_setting[1], "I") == 0)
                {
                  // R = Subscriber Ready
                  sms_msg->provision_status = VVM_PROVISION_STATUS_READY;
                  DBG ("Provisioning Status: Ready.");
                }
              else if (g_strcmp0 (single_setting[1], "U") == 0)
                {
                  // U = Subscriber Unknown
                  sms_msg->provision_status = VVM_PROVISION_STATUS_UNKNOWN;
                  DBG ("Provisioning Status: Unknown.");
                }
              else
                sms_msg->provision_status = VVM_PROVISION_STATUS_NOT_SET;
            }
          else
            //I have no idea what these settings are
            DBG ("Not procesing setting %s", single_setting[0]);
        }
    }
  //This is from the AT&T VVM app
  sms_msg->TUI_password_length = g_strdup ("7-15");
}

void
vvm_util_parse_status_sms_message (const char                 *message,
                                   struct sms_control_message *sms_msg,
                                   const char                 *vvm_type)
{
  DBG ("Parsing VVM status message.");
  sms_msg->type = g_strdup ("status");

  if ((g_strcmp0 (vvm_type, "cvvm") == 0) ||
      (g_strcmp0 (vvm_type, "otmp") == 0) ||
      (g_strcmp0 (vvm_type, "vvm3") == 0))
    vvm_util_parse_status_otmp_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "AT&TUSAProprietary") == 0)
    vvm_util_parse_status_att_usa_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "FreeMobileProprietary") == 0)
    vvm_util_parse_status_ios_sms_message (message, sms_msg);
  else if (g_strcmp0 (vvm_type, "ios") == 0)
    vvm_util_parse_status_ios_sms_message (message, sms_msg);
  else
    g_critical ("Unknown VVM type!");
}

void
vvm_util_decode_vvm_headers (struct voicemail *vvm_msg,
                             char            **tokens)
{
  for (int i = 0; tokens[i] != NULL; i++)
    {
      g_autofree char *token_lower = NULL;
      char **single_token = NULL;
      if (tokens[i] == NULL)
        continue;
      if (strlen (tokens[i]) < 1)
        continue;
      g_strstrip (tokens[i]);
      single_token = g_strsplit_set (tokens[i], ":", 2);
      if (!single_token[1])
        {
          g_autofree char *new_token = NULL;
          g_strfreev (single_token);
          if (i > 1)
            {
              if (!*tokens[i - 1])
                new_token = g_strdup_printf ("%s%s", tokens[i - 2], tokens[i]);
              else
                new_token = g_strdup_printf ("%s%s", tokens[i - 1], tokens[i]);
            }
          DBG ("New string to parse %s", new_token);
          single_token = g_strsplit_set (new_token, ":", 2);
          if (!single_token[1])
            continue;
        }
      g_strstrip (single_token[1]);
      token_lower = g_utf8_strdown (single_token[0], -1);
      if (g_str_match_string ("date", token_lower, TRUE))
        {
          //DBG("Date %s", single_token[1]);
          if (!vvm_msg->message_date)
            vvm_msg->message_date = g_strdup (single_token[1]);
        }
      else if (g_strcmp0 ("from", token_lower) == 0)
        {
          if (!vvm_msg->message_sender)
            vvm_msg->message_sender = g_strdup (single_token[1]);
          //DBG("From: %s", vvm_msg->message_sender);
        }
      else if (g_strcmp0 ("to", token_lower) == 0)
        {
          g_clear_pointer (&vvm_msg->to, g_free);
          vvm_msg->to = g_strdup (single_token[1]);
          //DBG("To: %s", vvm_msg->to);
        }
      else if (g_str_match_string ("message-context", token_lower, TRUE))
        {
          g_clear_pointer (&vvm_msg->message_context, g_free);
          vvm_msg->message_context = g_strdup (single_token[1]);
          //DBG("Message-Context: %s", single_token[1]);
        }
      else if (g_str_match_string ("mime-version", token_lower, TRUE))
        {
          g_clear_pointer (&vvm_msg->mime_version, g_free);
          vvm_msg->mime_version = g_strdup (single_token[1]);
          //DBG("MIME-Version: %s", single_token[1]);
        }
      else if (g_str_match_string ("content-type", token_lower, TRUE))
        {
          g_clear_pointer (&vvm_msg->content_type, g_free);
          vvm_msg->content_type = g_strdup (single_token[1]);
          //DBG("Content-type: %s", single_token[1]);
        }
      else if (strlen (token_lower) > 0)
        g_debug ("Cannot process Header:%s:containing:%s", token_lower, single_token[1]);
      else
        g_debug ("Cannot process line: %s", tokens[i]);
      g_strfreev (single_token);
    }
}

static char
*
vvm_util_decode_vvm_single_email_attachment (const char *attachment,
                                             const char *folderpath)
{
  g_auto(GStrv) lines = NULL;
  g_autofree char *contentencoding = NULL;
  g_autofree char *contenttype = NULL;
  g_autofree char *duration = NULL;
  g_autofree char *attributes = NULL;
  g_autofree char *string_to_decode = NULL;
  int attachment_line = 1;
  int found_attachment = FALSE;
  int base64_encoded;

  g_auto(GStrv) attribute_parse = NULL;
  g_autofree char *filename = NULL;
  GString *decoded = g_string_new (NULL);

  DBG ("Decoding Attachment");
  lines = g_strsplit_set (attachment, "\r\n", -1);
  //Decode Attachment headers first
  for (int i = 0; lines[i] != NULL; i++)
    {
      g_auto(GStrv) header = NULL;
      if (strlen (lines[i]) < 1)
        continue;
      header = g_strsplit_set (lines[i], ":", 2);
      if (g_str_match_string ("Content-Transfer-Encoding", header[0], TRUE))
        {
          contentencoding = g_strdup (header[1]);
          DBG ("Content-Transfer-Encoding: %s", contentencoding);
        }
      else if (g_str_match_string ("Content-Type", header[0], TRUE))
        {
          contenttype = g_strdup (header[1]);
          DBG ("Content-Type: %s", contenttype);
        }
      else if (g_str_match_string ("X-AppleVM-Duration", header[0], TRUE))
        {
          duration = g_strdup (header[1]);
          DBG ("X-AppleVM-Duration: %s", duration);
        }
      else if (g_str_match_string ("Content-Disposition", header[0], TRUE))
        {
          attributes = g_strdup (header[1]);
          DBG ("Content-Disposition: %s", attributes);
        }
      else {
          if (g_strv_length (header) == 2)
            DBG ("Do not know how to debug header: %s", lines[i]);
          else if (found_attachment == FALSE)
            {
              attachment_line = i;
              DBG ("Attachment contents on line: %d, length %" G_GSIZE_FORMAT,
                   attachment_line, strlen (lines[i]));
              found_attachment = TRUE;
            }
        }
    }
  if (contenttype == NULL)
    {
      g_critical ("Unknown content type.");
      return NULL;
    }
  if (attributes == NULL)
    {
      g_debug ("No attributes. This is an attachment, but not one to save");
      return g_strdup ("NONE");
    }
  attribute_parse = g_strsplit_set (attributes, ";", -1);
  for (int i = 0; attribute_parse[i] != NULL; i++)
    if (g_str_match_string ("filename", attribute_parse[i], TRUE))
      {
        g_auto(GStrv) filename_parse = NULL;
        filename_parse = g_strsplit_set (attribute_parse[i], "=", 2);
        filename = g_strdup (filename_parse[1]);
        g_strdelimit (filename, "\"", ' ');
        g_strstrip (filename);
        DBG ("filename: %s", filename);
      }
  if (filename == NULL)
    {
      if (strstr (contenttype, "audio/amr"))
        {
           filename = g_strdup ("voicemail.amr");
           DBG ("filename: %s", filename);
        }
      else if (strstr (contenttype, "audio/wav"))
        {
           filename = g_strdup ("voicemail.wav");
           DBG ("filename: %s", filename);
        }
      else {
           g_critical ("Could not find a file name.");
           return g_strdup ("NONE");
        }
    }

  DBG ("Folder Path: %s", folderpath);

  if (g_str_match_string ("base64", contentencoding, TRUE))
    base64_encoded = TRUE;
  else
    base64_encoded = FALSE;

  DBG ("Reconstructing the message!!");
  //AT&T USA linebreaks the base64 attachment every 60 characters
  //Why you ask? I have no idea.
  for (int i = attachment_line; lines[i] != NULL; i++)
    {
      if (lines[i + 1] != NULL)
        {
          decoded = g_string_append (decoded, lines[i]);
          if (base64_encoded == FALSE)
            //The encoding is probably "text/plain", so
            //add /r/n back in
            decoded = g_string_append (decoded, "/r/n");
        }
      else
      //This tends to be at the end of the email
      if (g_strcmp0 ("--", lines[i]) != 0)
        decoded = g_string_append (decoded, lines[i]);
    }
  string_to_decode = g_string_free (decoded, FALSE);
  //DBG("decoded message: %s", string_to_decode);
  if (string_to_decode == NULL)
    {
      g_critical ("Error reconstructing attachment!");
      return NULL;
    }
  else {
      //DBG("decoded message: %s", string_to_decode);
    }

  if (base64_encoded)
    {
      gsize base64_out_len;
      g_autofree unsigned char *base_64_decoded = NULL;
      base_64_decoded = g_base64_decode (string_to_decode, &base64_out_len);
      vvm_store (NULL, NULL, base_64_decoded, base64_out_len, filename, folderpath);
    }
  else if (g_strrstr ("text/plain", contentencoding) != NULL)
    vvm_store (NULL, string_to_decode, NULL, strlen (string_to_decode), filename, folderpath);
  else {
      g_critical ("Do not know how to decode this attachment!");
      return NULL;
    }

  //Content-Transfer-Encoding: base64
  //Content-Type: audio/amr
  //X-AppleVM-Duration: 36
  //Content-Disposition: attachment; size=58826;filename="voicemail-20210624165921.amr"
  return g_strdup_printf ("%s%s", folderpath, filename);
}


static int
vvm_util_decode_vvm_email_multipart_mixed (struct voicemail *vvm_msg,
                                           const char       *folderpath)
{
  g_auto(GStrv) tokens = NULL;
  g_auto(GStrv) boundary = NULL;
  g_auto(GStrv) email_attachments = NULL;

  DBG ("Decoding message of content type: %s", vvm_msg->content_type);

  //Content type looks like: multipart/mixed; boundary="_Part_287_1624553961"
  // "_Part_287_1624553961" divides headers and each attachment
  tokens = g_strsplit_set (vvm_msg->content_type, ";", 2);
  if (g_str_match_string ("boundary=", tokens[1], FALSE) == FALSE)
    {
      g_critical ("could not find boundary!");
      return FALSE;
    }
  boundary = g_strsplit_set (tokens[1], "=", 2);
  g_strdelimit (boundary[1], "\"", ' ');
  g_strstrip (boundary[1]);
  DBG ("Boundary %s", boundary[1]);
  email_attachments = g_strsplit (vvm_msg->contents, boundary[1], -1);
  //The first part are the headers, skip
  for (int i = 2; email_attachments[i] != NULL; i++)
    {
      if (g_str_match_string ("Content-Type", email_attachments[i], FALSE))
        {
          g_autofree char *save_file_path = NULL;
          save_file_path = vvm_util_decode_vvm_single_email_attachment (email_attachments[i], folderpath);
          if (save_file_path == NULL)
            {
              g_critical ("Failed to decode attachment");
              return FALSE;
            }
          else if (g_strcmp0 (save_file_path, "NONE") == 0)
            g_debug ("Not an attachment to save");
          else {
              DBG ("Putting attachment in settings");
              if (vvm_msg->attachments == NULL)
                vvm_msg->attachments = g_strdup (save_file_path);
              else {
                  g_autofree char *tmp = NULL;
                  tmp = g_strdup (vvm_msg->attachments);
                  g_free (vvm_msg->attachments);
                  vvm_msg->attachments = g_strdup_printf ("%s;%s", tmp, save_file_path);
                }
              DBG ("Attachments saved: %s", vvm_msg->attachments);
            }
        }
      else
        DBG ("There is no Content-Type, not an attachment: %s", email_attachments[i]);
    }


  return TRUE;
}

int
vvm_util_decode_vvm_all_email_attachments (struct voicemail *vvm_msg,
                                           const char       *folderpath)
{
  g_autofree char *new_dir = NULL;

  new_dir = g_strdup_printf ("%s%s-", folderpath, vvm_msg->file_uuid);
  DBG ("Attachment Folder Path: %s", new_dir);

  if (vvm_util_decode_vvm_email_multipart_mixed (vvm_msg, new_dir) == FALSE)
    {
      g_critical ("Failed to decode message of content type: %s", vvm_msg->content_type);
      return FALSE;
    }

  return TRUE;
}

void
vvm_util_delete_vvm_message (struct voicemail *vvm_msg)
{
  g_free (vvm_msg->uid);
  g_free (vvm_msg->mailindex);
  g_free (vvm_msg->message_sender);
  g_free (vvm_msg->message_date);
  g_free (vvm_msg->to);
  g_free (vvm_msg->mime_version);
  g_free (vvm_msg->message_context);
  g_free (vvm_msg->file_uuid);
  g_free (vvm_msg->contents);
  g_free (vvm_msg->attachments);
  g_free (vvm_msg->dbus_path);
  g_free (vvm_msg->email_filepath);

  g_free (vvm_msg);
}

void
vvm_util_delete_status_message (struct sms_control_message *sms_msg)
{
  g_free (sms_msg->type);
  g_free (sms_msg->mailbox_hostname);
  g_free (sms_msg->mailbox_port);
  g_free (sms_msg->vvm_destination_number);
  g_free (sms_msg->mailbox_username);
  g_free (sms_msg->mailbox_password);
  g_free (sms_msg->vvm_type);
  g_free (sms_msg->default_number);
  g_free (sms_msg->carrier_prefix);
  g_free (sms_msg->uid);
  g_free (sms_msg->new_mailbox_messages);
  g_free (sms_msg->message_sender);
  g_free (sms_msg->message_date);
  g_free (sms_msg->message_length);
  g_free (sms_msg->language);
  g_free (sms_msg->greeting_length);
  g_free (sms_msg->voice_signature_length);
  g_free (sms_msg->TUI_password_length);
  g_free (sms_msg->mailindex);
  g_free (sms_msg->activate_url);

  g_free (sms_msg);
}
