/*
 *
 *  Visual Voicemail Daemon
 *
 *  Copyright (C) 2010-2011, Intel Corporation
 *                2021-2022, 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
 *
 */

#define _XOPEN_SOURCE 700

/* See https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/332#note_1107764 */
#define EDS_DISABLE_DEPRECATED

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

#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <glib.h>
#include <glib/gstdio.h>
#include <time.h>
#include <stdio.h>
#include <gio/gio.h>
#include <stdlib.h>

#include <sys/select.h>
#include <netdb.h>
#include <stdio.h>
#include <curl/curl.h>

#include "vvm.h"
#include "dbus.h"
#include "vvmutil.h"
#include "phone-utils.h"

#define BEARER_SETUP_TIMEOUT    20      /* 20 seconds */
#define BEARER_IDLE_TIMEOUT     10      /* 10 seconds */
#define CHUNK_SIZE 2048                  /* 2 Kib */

#define DEFAULT_MAX_ATTACHMENTS_NUMBER 25
#define MAX_ATTEMPTS 3
#define DEFAULT_MAX_ATTACHMENT_TOTAL_SIZE 1100000

struct vvm_service
{
  char *identity;
  char *path;
  char *interface;
  char *country_code;
  char *own_number;
  char *mailbox_interface;
  char *mailbox_hostname;
  char *mailbox_port;
  char *mailbox_username;
  char *mailbox_password;
  char *mailbox_auth;
  char *mailbox_URI;
  char *mailbox_language;
  char *mailbox_greeting_length;
  char *mailbox_voice_signature_length;
  char *mailbox_TUI_password_length;
  char *mailbox_vvm_type;
  int mailbox_starttls;
  int mailbox_legacytls;
  vvm_service_bearer_handler_func_t bearer_handler;
  void *bearer_data;
  guint bearer_timeout;
  int mailbox_active;
  int allow_plaintext_connection;
  gboolean bearer_setup;
  gboolean bearer_active;
  gboolean use_mailbox_interface;
  GHashTable *messages;
  GKeyFile *settings;
  CURL *curl;
};

static GList *service_list;

gboolean global_debug = FALSE;

guint service_registration_id;
guint manager_registration_id;

static void append_properties (GVariantBuilder    *service_builder,
                               struct vvm_service *service);
static void emit_msg_status_changed (const char *path,
                                     const char *new_status);
static void append_message_entry (char                     *path,
                                  const struct vvm_service *service,
                                  struct voicemail         *vvm_msg,
                                  GVariantBuilder          *message_builder);
static int vvm_message_unregister (const struct vvm_service *service,
                                   struct voicemail         *vvm_msg);

static void
handle_method_call_service (GDBusConnection       *connection,
                            const gchar           *sender,
                            const gchar           *object_path,
                            const gchar           *interface_name,
                            const gchar           *method_name,
                            GVariant              *parameters,
                            GDBusMethodInvocation *invocation,
                            gpointer               user_data)
{
  if (g_strcmp0 (method_name, "GetMessages") == 0)
    {
      struct vvm_service      *service = user_data;
      GVariantBuilder          messages_builder;
      GVariant                *messages, *all_messages;
      GHashTableIter           table_iter;
      gpointer                 key, value;
      guint                    i = 0;

      DBG ("Retrieving all Messages...");

      if (g_hash_table_size (service->messages) == 0)
        {
          all_messages = g_variant_new ("(a(oa{sv}))", NULL);
          DBG ("No Messages!");
        }
      else {
          g_variant_builder_init (&messages_builder, G_VARIANT_TYPE ("a(oa{sv})"));

          g_hash_table_iter_init (&table_iter, service->messages);
          while (g_hash_table_iter_next (&table_iter, &key, &value))
            {
              i = i + 1;
              DBG ("On message %d!", i);
              g_variant_builder_open (&messages_builder, G_VARIANT_TYPE ("(oa{sv})"));
              append_message_entry (key, service, value, &messages_builder);
              g_variant_builder_close (&messages_builder);
            }
          DBG ("Messages total: %d", i);
          messages = g_variant_builder_end (&messages_builder);

          all_messages = g_variant_new ("(*)", messages);
        }

      g_dbus_method_invocation_return_value (invocation, all_messages);
    }
  else if (g_strcmp0 (method_name, "GetProperties") == 0)
    {
      struct vvm_service      *service = user_data;
      GVariantBuilder          properties_builder;
      GVariant                *properties, *all_properties;

      g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}"));

      g_variant_builder_add_parsed (&properties_builder,
                                    "{'ModemCountryCode', <%s>}",
                                    service->country_code);

      g_variant_builder_add_parsed (&properties_builder,
                                    "{'MailBoxActive', <%b>}",
                                    service->mailbox_active);

      properties = g_variant_builder_end (&properties_builder);

      all_properties = g_variant_new ("(*)", properties);

      DBG ("vvmd properties: %s", g_variant_print (properties, TRUE));

      g_dbus_method_invocation_return_value (invocation, all_properties);

      return;
    }
  else {
      g_dbus_method_invocation_return_error (invocation,
                                             G_DBUS_ERROR,
                                             G_DBUS_ERROR_INVALID_ARGS,
                                             "Cannot find the method requested!");
      return;
    }
}

static const GDBusInterfaceVTable interface_vtable_service = {
  handle_method_call_service
};

static void
handle_method_call_manager (GDBusConnection       *connection,
                            const gchar           *sender,
                            const gchar           *object_path,
                            const gchar           *interface_name,
                            const gchar           *method_name,
                            GVariant              *parameters,
                            GDBusMethodInvocation *invocation,
                            gpointer               user_data)
{
  if (g_strcmp0 (method_name, "GetServices") == 0)
    {
      struct vvm_service      *service;
      GVariant                *all_services;
      GList                   *l;

      DBG ("At Get Services Method Call");

      if (service_list)
        {
          GVariantBuilder  service_builder;
          GVariant        *get_services;

          g_variant_builder_init (&service_builder, G_VARIANT_TYPE ("a(oa{sv})"));
          for (l = service_list; l != NULL; l = l->next)
            {
              service = l->data;
              g_variant_builder_open (&service_builder, G_VARIANT_TYPE ("(oa{sv})"));
              append_properties (&service_builder, service);
              g_variant_builder_close (&service_builder);
            }
          get_services = g_variant_builder_end (&service_builder);
          all_services = g_variant_new ("(*)", get_services);
        }
      else
        all_services = g_variant_new ("(a(oa{sv}))", NULL);
      g_dbus_method_invocation_return_value (invocation, all_services);
    }
  else {
      g_dbus_method_invocation_return_error (invocation,
                                             G_DBUS_ERROR,
                                             G_DBUS_ERROR_INVALID_ARGS,
                                             "Cannot find the method requested!");
      return;
    }
}

static const
GDBusInterfaceVTable interface_vtable_manager = {
  handle_method_call_manager
};

static void
handle_method_call_message (GDBusConnection       *connection,
                            const gchar           *sender,
                            const gchar           *object_path,
                            const gchar           *interface_name,
                            const gchar           *method_name,
                            GVariant              *parameters,
                            GDBusMethodInvocation *invocation,
                            gpointer               user_data)
{
  if (g_strcmp0 (method_name, "Delete") == 0)
    {
      struct vvm_service *service = user_data;
      struct voicemail *vvm_msg;
      const char *path = object_path;
      g_autofree char *uuid = NULL;
      g_autofree char *attachments = NULL;
      DBG ("message path %s", path);

      vvm_msg = g_hash_table_lookup (service->messages, path);
      if (vvm_msg == NULL)
        g_dbus_method_invocation_return_dbus_error (invocation,
                                                    VVM_MESSAGE_INTERFACE,
                                                    "Cannot find this VVM!");

      uuid = g_strdup (vvm_msg->file_uuid);
      attachments = g_strdup (vvm_msg->attachments);
      if (vvm_message_unregister (service, vvm_msg) < 0)
        g_dbus_method_invocation_return_dbus_error (invocation,
                                                    VVM_MESSAGE_INTERFACE,
                                                    "There was an error deleting the VVM!");
      vvm_store_remove_attachments (service->identity, uuid, attachments);
      vvm_store_remove (service->identity, uuid);
      g_dbus_method_invocation_return_value (invocation, NULL);
      DBG ("Successfully Deleted Message!");
      return;
    }
  else if (g_strcmp0 (method_name, "MarkRead") == 0)
    {
      struct vvm_service *service = user_data;
      struct voicemail *vvm_msg;
      const char *path = object_path;
      GKeyFile *meta;

      DBG ("message path %s", path);

      vvm_msg = g_hash_table_lookup (service->messages, path);
      if (vvm_msg == NULL)
        g_dbus_method_invocation_return_dbus_error (invocation,
                                                    VVM_MESSAGE_INTERFACE,
                                                    "Cannot find this VVM!");

      meta = vvm_store_meta_open (service->identity, vvm_msg->file_uuid);

      vvm_msg->lifetime_status = VVM_LIFETIME_STATUS_READ;

      g_key_file_set_integer (meta, "info", "LifetimeStatus", vvm_msg->lifetime_status);

      vvm_store_meta_close (service->identity, vvm_msg->file_uuid, meta, TRUE);

      emit_msg_status_changed (path, "read");

      g_dbus_method_invocation_return_value (invocation, NULL);
      return;
    }
  else {
      g_dbus_method_invocation_return_error (invocation,
                                             G_DBUS_ERROR,
                                             G_DBUS_ERROR_INVALID_ARGS,
                                             "Cannot find the method requested!");
      return;
    }
}

static const GDBusInterfaceVTable interface_vtable_message = {
  handle_method_call_message
};

void
vvm_service_set_hostname (struct vvm_service *service,
                          const char         *mailbox_hostname)
{
  g_free (service->mailbox_hostname);
  service->mailbox_hostname = g_strdup (mailbox_hostname);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxHostname", service->mailbox_hostname);
}

void
vvm_service_set_interface (struct vvm_service *service,
                           const char         *mailbox_interface)
{
  g_free (service->mailbox_interface);
  service->mailbox_interface = g_strdup (mailbox_interface);
  DBG ("Modem interface set to %s", service->mailbox_interface);
}

void
vvm_service_set_port (struct vvm_service *service,
                      const char         *mailbox_port)
{
  g_free (service->mailbox_port);
  service->mailbox_port = g_strdup (mailbox_port);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxPort", service->mailbox_port);
}

void
vvm_service_set_username (struct vvm_service *service,
                          const char         *mailbox_username)
{
  if (mailbox_username == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_username);
  service->mailbox_username = g_strdup (mailbox_username);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxUsername", service->mailbox_username);
}

void
vvm_service_set_password (struct vvm_service *service,
                          const char         *mailbox_password)
{
  if (mailbox_password == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_password);
  service->mailbox_password = g_strdup (mailbox_password);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxPassword", service->mailbox_password);
}

void
vvm_service_set_language (struct vvm_service *service,
                          const char         *language)
{
  if (language == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_language);
  service->mailbox_language = g_strdup (language);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxLanguage", service->mailbox_language);
}

void
vvm_service_set_greeting_length (struct vvm_service *service,
                                 const char         *greeting_length)
{
  if (greeting_length == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_greeting_length);
  service->mailbox_greeting_length = g_strdup (greeting_length);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "GreetingLength", service->mailbox_greeting_length);
}

void
vvm_service_set_voice_signature_length (struct vvm_service *service,
                                        const char         *voice_signature_length)
{
  if (voice_signature_length == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_voice_signature_length);
  service->mailbox_voice_signature_length = g_strdup (voice_signature_length);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "VSLength", service->mailbox_voice_signature_length);
}

void
vvm_service_set_tui_password_length (struct vvm_service *service,
                                     const char         *TUI_password_length)
{
  if (TUI_password_length == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_TUI_password_length);
  service->mailbox_TUI_password_length  = g_strdup (TUI_password_length);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "TUIPasswordLength", service->mailbox_TUI_password_length);
}

void
vvm_service_set_subscription_configuration (struct vvm_service *service,
                                            const char         *configuration)
{
  if (configuration == NULL || service == NULL)
    {
      g_critical ("There is a NULL argument! Not setting.");
      return;
    }
  g_free (service->mailbox_vvm_type);
  service->mailbox_vvm_type  = g_strdup (configuration);
  g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                         "MailboxVVMType", service->mailbox_vvm_type);
}

static void
activate_curl_structure (struct vvm_service *service)
{
  DBG ("Setting up CURL...");
  service->curl = curl_easy_init ();
  if (global_debug)
    curl_easy_setopt (service->curl, CURLOPT_VERBOSE, 1L);
  curl_easy_setopt (service->curl, CURLOPT_USERNAME, service->mailbox_username);
  curl_easy_setopt (service->curl, CURLOPT_PASSWORD, service->mailbox_password);
  if (service->mailbox_starttls)
    curl_easy_setopt (service->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
  if (service->mailbox_legacytls)
    curl_easy_setopt (service->curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
  if (service->use_mailbox_interface)
    curl_easy_setopt (service->curl, CURLOPT_INTERFACE, service->mailbox_interface);

  if (*service->mailbox_auth)
    curl_easy_setopt (service->curl, CURLOPT_LOGIN_OPTIONS, service->mailbox_auth);
}

static void
deactivate_curl_structure (struct vvm_service *service)
{
  DBG ("Tearing down CURL...");
  curl_easy_cleanup (service->curl);
}

void
vvm_service_deactivate_vvm_imap_server (struct vvm_service *service)
{
  DBG ("Deactivating IMAP Mailbox");
  if (service->mailbox_active == FALSE)
    {
      DBG ("IMAP Mailbox already deactivated!");
      return;
    }
  service->mailbox_active = FALSE;
  g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                          "MailboxActive", service->mailbox_active);
  deactivate_curl_structure (service);

  vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings);
}

static int
vvm_service_test_vvm_imap_server (struct vvm_service *service,
                                  gboolean            modem_iface)
{
  CURL *curl;
  CURLcode res = CURLE_OK;
  char *URI, *mailbox_auth;

  DBG ("Activating IMAP Mailbox");
  if (service->mailbox_active == TRUE)
    {
      DBG ("IMAP Mailbox already active!");
      return TRUE;
    }

  curl = curl_easy_init ();
  if (global_debug)
    curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);

  // Set Username and Password
  curl_easy_setopt (curl, CURLOPT_USERNAME, service->mailbox_username);
  curl_easy_setopt (curl, CURLOPT_PASSWORD, service->mailbox_password);
  //Do five second timeout
  curl_easy_setopt (curl, CURLOPT_TIMEOUT, 5L);

  if (modem_iface)
    curl_easy_setopt (curl, CURLOPT_INTERFACE, service->mailbox_interface);

  /*
   * This is testing various IMAP settings to see that works in this order:
   * IMAP over SSL:
   * curl -v imaps://$USERNAME:$PASSWORD@$HOSTNAME/
   * IMAP over SSL (AUTH=PLAIN):
   * curl -v --login-options AUTH=PLAIN imaps://$USERNAME:$PASSWORD@$HOSTNAME/
   * IMAP over SSL (AUTH=DIGEST-MD5):
   * curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/
   * IMAP on the port provided by the STATUS SMS:
   * curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/
   * IMAP over the standard port (If the provided port is different)
   * curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME/
   *
   */

  //curl -v imaps://$USERNAME:$PASSWORD@$HOSTNAME/
  DBG ("Trying IMAP with SSL");

  mailbox_auth = g_strdup ("");
  URI = g_strdup_printf ("imaps://%s/INBOX", service->mailbox_hostname);
  curl_easy_setopt (curl, CURLOPT_URL, URI);

  res = curl_easy_perform (curl);
  // Try to guess if the server is using legacy ciphers
  if (res == CURLE_SSL_CONNECT_ERROR)
    {
      DBG ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
      DBG ("Trying with legacy ciphers");
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
      res = curl_easy_perform (curl);
      if (res == CURLE_OK)
        {
          service->mailbox_legacytls = TRUE;
          g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                  "MailboxLegacyTLS", service->mailbox_legacytls);
        }
    }
  if (res == CURLE_OK)
    {
      DBG ("Mailbox Activated!");
      service->mailbox_active = TRUE;
      service->mailbox_URI = URI;
      service->mailbox_auth = mailbox_auth;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxActive", service->mailbox_active);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxURI", service->mailbox_URI);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxAuth", service->mailbox_auth);
      curl_easy_cleanup (curl);
      return TRUE;
    }
  else {
      // Reset authorized ciphers to the default value
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL");
      g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
    }

  DBG ("Trying IMAP with SSL: Explicit PLAIN Authentication");
  g_free (mailbox_auth);
  mailbox_auth = g_strdup ("AUTH=PLAIN");
  curl_easy_setopt (curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth);

  res = curl_easy_perform (curl);
  // Try to guess if the server is using legacy ciphers
  if (res == CURLE_SSL_CONNECT_ERROR)
    {
      DBG ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
      DBG ("Trying with legacy ciphers");
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
      res = curl_easy_perform (curl);
      if (res == CURLE_OK)
        {
          service->mailbox_legacytls = TRUE;
          g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                  "MailboxLegacyTLS", service->mailbox_legacytls);
        }
    }
  if (res == CURLE_OK)
    {
      DBG ("Mailbox Activated!");
      service->mailbox_active = TRUE;
      service->mailbox_URI = URI;
      service->mailbox_auth = mailbox_auth;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxActive", service->mailbox_active);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxURI", service->mailbox_URI);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxAuth", service->mailbox_auth);
      curl_easy_cleanup (curl);
      return TRUE;
    }
  else {
      // Reset authorized ciphers to the default value
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL");
      g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
    }

  //curl -v --login-options AUTH=DIGEST-MD5 imaps://$USERNAME:$PASSWORD@$HOSTNAME/
  DBG ("Trying IMAP with SSL: Explicit DIGEST-MD5 Authentication");
  g_free (mailbox_auth);
  mailbox_auth = g_strdup ("AUTH=DIGEST-MD5");
  curl_easy_setopt (curl, CURLOPT_LOGIN_OPTIONS, mailbox_auth);
  res = curl_easy_perform (curl);
  // Try to guess if the server is using legacy ciphers
  if (res == CURLE_SSL_CONNECT_ERROR)
    {
      DBG ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
      DBG ("Trying with legacy ciphers");
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
      res = curl_easy_perform (curl);
      if (res == CURLE_OK)
        {
          service->mailbox_legacytls = TRUE;
          g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                  "MailboxLegacyTLS", service->mailbox_legacytls);
        }
    }
  if (res == CURLE_OK)
    {
      DBG ("Mailbox Activated!");
      service->mailbox_active = TRUE;
      service->mailbox_URI = URI;
      service->mailbox_auth = mailbox_auth;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxActive", service->mailbox_active);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxURI", service->mailbox_URI);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxAuth", service->mailbox_auth);
      curl_easy_cleanup (curl);
      return TRUE;
    }
  else {
      // Reset authorized ciphers to the default value
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL");
      g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
    }

  // I need to clean out the curl_easy_setopt(), so tear down and re-setup
  curl_easy_cleanup (curl);
  curl = curl_easy_init ();
  if (global_debug)
    curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);

  // Set Username and Password
  curl_easy_setopt (curl, CURLOPT_USERNAME, service->mailbox_username);
  curl_easy_setopt (curl, CURLOPT_PASSWORD, service->mailbox_password);
  //Do five second timeout
  curl_easy_setopt (curl, CURLOPT_TIMEOUT, 5L);

  if (modem_iface)
    curl_easy_setopt (curl, CURLOPT_INTERFACE, service->mailbox_interface);

  /* Do not allow any non-TLS connection */
  if (!service->allow_plaintext_connection)
    {
      DBG ("Disallowing plaintext connection over IMAP");
      curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    }
  else
    DBG ("Allowing IMAP to choose if it wants a plaintext connection");

  g_free (mailbox_auth);
  mailbox_auth = g_strdup ("");

  DBG ("Trying IMAP with Specified port");
  //curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME:$PORT/
  g_free (URI);
  URI = g_strdup_printf ("imap://%s:%s/INBOX", service->mailbox_hostname, service->mailbox_port);
  curl_easy_setopt (curl, CURLOPT_URL, URI);
  res = curl_easy_perform (curl);
  // Try to guess if the server is using legacy ciphers
  if (res == CURLE_SSL_CONNECT_ERROR)
    {
      DBG ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
      DBG ("Trying with legacy ciphers");
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
      res = curl_easy_perform (curl);
      if (res == CURLE_OK)
        {
          service->mailbox_legacytls = TRUE;
          g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                  "MailboxLegacyTLS", service->mailbox_legacytls);
        }
    }
  if (res == CURLE_OK)
    {
      DBG ("Mailbox Activated!");
      service->mailbox_active = TRUE;
      service->mailbox_URI = URI;
      service->mailbox_auth = mailbox_auth;
      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxActive", service->mailbox_active);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxURI", service->mailbox_URI);
      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxAuth", service->mailbox_auth);
      curl_easy_cleanup (curl);
      return TRUE;
    }
  else {
      // Reset authorized ciphers to the default value
      curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL");
      g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
    }

  //Next, IMAP with default port
  if (g_strcmp0 (service->mailbox_port, "143") != 0)        //service->mailbox_port isn't 143 (default IMAP port)
    {
      DBG ("Trying IMAP on default port");
      //curl -v imap://$USERNAME:$PASSWORD@$HOSTNAME/
      g_free (URI);
      URI = g_strdup_printf ("imap://%s/INBOX", service->mailbox_hostname);
      curl_easy_setopt (curl, CURLOPT_URL, URI);
      res = curl_easy_perform (curl);
      // Try to guess if the server is using legacy ciphers
      if (res == CURLE_SSL_CONNECT_ERROR)
        {
          DBG ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
          DBG ("Trying with legacy ciphers");
          curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL:@SECLEVEL=1");
          res = curl_easy_perform (curl);
          if (res == CURLE_OK)
            {
              service->mailbox_legacytls = TRUE;
              g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                      "MailboxLegacyTLS", service->mailbox_legacytls);
            }
        }
      if (res == CURLE_OK)
        {
          DBG ("Mailbox Activated!");
          service->mailbox_active = TRUE;
          service->mailbox_URI = URI;
          service->mailbox_auth = mailbox_auth;
          g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                                  "MailboxActive", service->mailbox_active);
          g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                                 "MailboxURI", service->mailbox_URI);
          g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                                 "MailboxAuth", service->mailbox_auth);
          curl_easy_cleanup (curl);
          return TRUE;
        }
      else {
          // Reset authorized ciphers to the default value
          curl_easy_setopt (curl, CURLOPT_SSL_CIPHER_LIST, "ALL");
          g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
        }
    }

  g_free (URI);
  g_free (mailbox_auth);
  curl_easy_cleanup (curl);
  return FALSE;
}

static int
vvm_service_activate_vvm_imap_server (struct vvm_service *service)
{
  service->use_mailbox_interface = FALSE;
  DBG ("Trying to activate without using mailbox interface.");
  if (vvm_service_test_vvm_imap_server (service, service->use_mailbox_interface))
    {
      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "UseMailboxInterface", service->use_mailbox_interface);
      vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings);
      return TRUE;
    }
  DBG ("Retrying to activate without using mailbox interface.");
  service->use_mailbox_interface = TRUE;
  if (vvm_service_test_vvm_imap_server (service, service->use_mailbox_interface))
    {
      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "UseMailboxInterface", service->use_mailbox_interface);
      vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings);
      return TRUE;
    }
  g_critical ("Unable to activate mailbox!");
  g_critical ("You may not get any SMS SYNC Messages until the mailbox is active.");
  vvm_settings_sync (service->identity, SETTINGS_STORE, service->settings);
  return FALSE;
}

struct struct_string
{
  GString *response;
};

//CURL really wants a struct, I don't know why.....
static size_t
curl_string_cb (void  *data,
                size_t size,
                size_t nmemb,
                void  *userdata)
{
  size_t realsize = size * nmemb;
  struct struct_string *mem = (struct struct_string *)userdata;
  DBG ("received %lu", realsize);

  mem->response = g_string_append_len (mem->response, (char *)data, realsize);
  //DBG("String so far: %s", mem->response->str);
  return realsize;
}

static char
*
format_email (struct vvm_service *service,
              const char         *input)
{
  g_autofree char *parsed_number = NULL;

  parsed_number = parse_email_address (input);

  return phone_utils_format_number_e164 (parsed_number,
                                         service->country_code,
                                         TRUE);
}

static int
vvmd_service_retrieve_headers (struct vvm_service         *service,
                               const char                 *index,
                               struct sms_control_message *sms_msg,
                               struct voicemail           *vvm_msg)
{
  g_autofree char *URI = NULL;
  g_autofree char *mailbox = NULL;
  g_autofree char *mailbox_index = NULL;
  struct struct_string chunk = {0};
  CURLcode res = CURLE_OK;

  chunk.response = g_string_new (NULL);
  if (sms_msg)
    {
      //Sync Message exists, so copy over data from here
      vvm_msg->uid = g_strdup (sms_msg->uid);
      vvm_msg->mailbox_message_type = sms_msg->mailbox_message_type;
      vvm_msg->message_sender = phone_utils_format_number_e164 (sms_msg->message_sender,
                                                                service->country_code,
                                                                TRUE);

      //I am using the UID to find the voicemail
      mailbox = g_strdup ("UID");
      mailbox_index = g_strdup (sms_msg->uid);
    }
  else {
      // I am using the mailindex to find the voicemail
      // (since I am looking at all of them)
      mailbox = g_strdup ("MAILINDEX");
      mailbox_index = g_strdup (index);
      vvm_msg->mailindex = g_strdup (index);
    }

  /*
   * Trying to find these headers:
   * - From (Mandatory)
   * - To (Mandatory)
   * - Date (Mandatory)
   * - Subject (Optional)
   * - Message-Context (Mandatory)
   * - Content-Duration (Mandatory for Voice/Video Messages)
   * - Content-Type (Mandatory)
   * - MIME-Version (Maondatory)
   * - Importance (Optional)
   * - Sensitivity (Optional)
   * - X-Content-Pages (Mandatory in Faxes)
   * - X-Original-Msg-UID (Optional)
   */

  URI = g_strdup_printf ("%s;%s=%s;SECTION=HEADER.FIELDS%%20(FROM%%20TO%%20DATE%%20SUBJECT%%20MESSAGE-CONTEXT%%20CONTENT-DURATION%%20CONTENT-TYPE%%20MIME-VERSION%%20IMPORTANCE%%20X-CONTENT-PAGES%%20X-ORIGINAL-MSG-UID)", service->mailbox_URI, mailbox, mailbox_index);
  DBG ("URL: %s", URI);
  curl_easy_setopt (service->curl, CURLOPT_URL, URI);
  curl_easy_setopt (service->curl, CURLOPT_WRITEFUNCTION, curl_string_cb);
  curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk);
  res = curl_easy_perform (service->curl);
  if (res == CURLE_OK)
    {
      g_autofree char *sender;
      g_autofree char *to;
      char **tokens = NULL;
      g_autofree char *headers_response;

      headers_response = g_string_free (chunk.response, FALSE);
      DBG ("Returned: %s", headers_response);
      tokens = g_strsplit_set (headers_response, "\r\n", -1);
      vvm_util_decode_vvm_headers (vvm_msg, tokens);
      g_strfreev (tokens);
      sender = format_email (service, vvm_msg->message_sender);
      g_free (vvm_msg->message_sender);
      vvm_msg->message_sender = g_strdup (sender);
      to = format_email (service, vvm_msg->to);
      g_free (vvm_msg->to);
      vvm_msg->to = g_strdup (to);
    }
  else {
      g_warning ("Error Downloading headers: %s\n", curl_easy_strerror (res));
      return FALSE;
    }

  return TRUE;
}

static int
vvm_service_store_headers (struct vvm_service *service,
                           struct voicemail   *vvm_msg)
{
  GKeyFile *meta;
  DBG ("Storing headers");
  meta = vvm_store_meta_open (service->identity, vvm_msg->file_uuid);

  if (vvm_msg->message_date)
    g_key_file_set_string (meta, "info", "Date", vvm_msg->message_date);
  else {
      g_critical ("There is no date!");
      return FALSE;
    }

  if (vvm_msg->message_sender)
    g_key_file_set_string (meta, "info", "Sender", vvm_msg->message_sender);
  else {
      g_critical ("There is no sender!");
      return FALSE;
    }

  if (vvm_msg->to)
    g_key_file_set_string (meta, "info", "To", vvm_msg->to);
  else {
      g_critical ("There is no To!");
      return FALSE;
    }

  if (vvm_msg->message_context)
    g_key_file_set_string (meta, "info", "MessageContext", vvm_msg->message_context);

  if (vvm_msg->mime_version)
    g_key_file_set_string (meta, "info", "MIMEVersion", vvm_msg->mime_version);

  if (vvm_msg->content_type)
    g_key_file_set_string (meta, "info", "ContentType", vvm_msg->content_type);
  else {
      g_critical ("There is no Content Type!");
      return FALSE;
    }

  //Mailindex is relative, so do not store that
  if (vvm_msg->uid)
    g_key_file_set_string (meta, "info", "UID", vvm_msg->uid);

  if (vvm_msg->attachments)
    g_key_file_set_string (meta, "info", "Attachments", vvm_msg->attachments);
  else
    g_warning ("There are no attachments!");
  if (vvm_msg->email_filepath)
    g_key_file_set_string (meta, "info", "EmailFilepath", vvm_msg->email_filepath);

  g_key_file_set_integer (meta, "info", "LifetimeStatus", vvm_msg->lifetime_status);

  //DEBUG
  /*
   * if(vvm_msg->contents)lifetime_status
   *      g_key_file_set_string(meta, "contents", "contents", vvm_msg->contents);
   */

  vvm_store_meta_close (service->identity, vvm_msg->file_uuid, meta, TRUE);
  return TRUE;
}


static int
vvm_service_retrieve_vvm_email (struct vvm_service *service,
                                struct voicemail   *vvm_msg)
{
  g_autofree char *URI = NULL;
  g_autofree char *mailbox = NULL;
  g_autofree char *mailbox_index = NULL;
  struct struct_string chunk = {0};
  CURLcode res = CURLE_OK;

  chunk.response = g_string_new (NULL);

  DBG ("Retrieving VVM Email");
  if (vvm_msg->uid)
    {
      //We have a uid, so use that
      mailbox = g_strdup ("UID");
      mailbox_index = g_strdup (vvm_msg->uid);
    }
  else {
      // I am using the mailindex to find the voicemail
      // (since I am looking at all of them)
      mailbox = g_strdup ("MAILINDEX");
      mailbox_index = g_strdup (vvm_msg->mailindex);
    }

  URI = g_strdup_printf ("%s;%s=%s", service->mailbox_URI, mailbox, mailbox_index);
  DBG ("URL: %s", URI);

  curl_easy_setopt (service->curl, CURLOPT_URL, URI);
  curl_easy_setopt (service->curl, CURLOPT_WRITEFUNCTION, curl_string_cb);
  curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk);
  res = curl_easy_perform (service->curl);
  if (res == CURLE_OK)
    {
      g_critical ("Downloaded email Successfully");
      vvm_msg->contents = g_string_free (chunk.response, FALSE);
      //DBG("Email Contents: %s", vvm_msg->contents);
    }
  else {
      g_warning ("Error Downloading email: %s\n", curl_easy_strerror (res));
      return FALSE;
    }

  return TRUE;
}

static int
vvm_message_register (struct vvm_service *service,
                      struct voicemail   *vvm_msg)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GDBusNodeInfo   *introspection_data = vvm_dbus_get_introspection_data ();
  g_autoptr(GError)  error = NULL;

  //This builds the path for the message, do not disturb!
  vvm_msg->dbus_path = g_strdup_printf ("%s/%s", service->path, vvm_msg->file_uuid);
  if (vvm_msg->dbus_path == NULL)
    return -ENOMEM;

  vvm_msg->message_registration_id = g_dbus_connection_register_object (connection,
                                                                        vvm_msg->dbus_path,
                                                                        introspection_data->interfaces[2],
                                                                        &interface_vtable_message,
                                                                        service, // user_data
                                                                        NULL, // user_data_free_func
                                                                        &error); // GError**
  if (error)
    {
      g_critical ("Error Registering Message %s: %s", vvm_msg->dbus_path, error->message);
      return FALSE;
    }


  g_assert (vvm_msg->message_registration_id > 0);

  g_hash_table_replace (service->messages, vvm_msg->dbus_path, vvm_msg);

  DBG ("message registered %s", vvm_msg->dbus_path);

  return TRUE;
}

static char *
iso8601_date (const char *date)
{
  time_t time_tmp;
  GDateTime *time_utc;
  GTimeZone *here = g_time_zone_new_local ();
  char *converted_time;

  time_tmp = curl_getdate (date, NULL);
  time_utc = g_date_time_new_from_unix_utc (time_tmp);
  converted_time = g_date_time_format_iso8601 (time_utc);
  g_date_time_unref (time_utc);
  g_time_zone_unref (here);

  return converted_time;
}

static void
append_message (const char               *path,
                const struct vvm_service *service,
                struct voicemail         *vvm_msg,
                GVariantBuilder          *message_builder)
{
  g_variant_builder_add (message_builder, "o", vvm_msg->dbus_path);

  g_variant_builder_open (message_builder, G_VARIANT_TYPE ("a{sv}"));

  if (vvm_msg->message_date)
    {
      g_autofree char *normalized_date = NULL;
      /* Emails dont gives dates in iso8601 */
      normalized_date = iso8601_date (vvm_msg->message_date);
      g_variant_builder_add_parsed (message_builder,
                                    "{'Date', <%s>}",
                                    normalized_date);
    }
  else
    g_critical ("There is no date!");

  if (vvm_msg->message_sender)
    g_variant_builder_add_parsed (message_builder,
                                  "{'Sender', <%s>}",
                                  vvm_msg->message_sender);
  else
    g_critical ("There is no sender!");

  if (vvm_msg->to)
    g_variant_builder_add_parsed (message_builder,
                                  "{'To', <%s>}",
                                  vvm_msg->to);
  else
    g_critical ("There is no To!");

  if (vvm_msg->message_context)
    g_variant_builder_add_parsed (message_builder,
                                  "{'MessageContext', <%s>}",
                                  vvm_msg->message_context);

  if (vvm_msg->mime_version)
    g_variant_builder_add_parsed (message_builder,
                                  "{'MIMEVersion', <%s>}",
                                  vvm_msg->mime_version);

  if (vvm_msg->content_type)
    g_variant_builder_add_parsed (message_builder,
                                  "{'ContentType', <%s>}",
                                  vvm_msg->content_type);
  else
    g_critical ("There is no Content Type!");

  if (vvm_msg->attachments)
    g_variant_builder_add_parsed (message_builder,
                                  "{'Attachments', <%s>}",
                                  vvm_msg->attachments);
  else
    g_warning ("There are no attachments!");

  if (vvm_msg->email_filepath)
    g_variant_builder_add_parsed (message_builder,
                                  "{'EmailFilepath', <%s>}",
                                  vvm_msg->email_filepath);
  else
    g_warning ("There are no attachments!");

  g_variant_builder_add_parsed (message_builder,
                                "{'LifetimeStatus', <%i>}",
                                vvm_msg->lifetime_status);

  g_variant_builder_close (message_builder);
}

static void
append_message_entry (char                     *path,
                      const struct vvm_service *service,
                      struct voicemail         *vvm_msg,
                      GVariantBuilder          *message_builder)
{
  g_autoptr(GError) error = NULL;

  DBG ("Message Added %p", vvm_msg);

  append_message (vvm_msg->dbus_path, service, vvm_msg, message_builder);
}

static void
emit_message_added (const struct vvm_service *service,
                    struct voicemail         *vvm_msg)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GVariantBuilder message_builder;
  GVariant        *message;
  g_autoptr(GError) error = NULL;

  g_variant_builder_init (&message_builder, G_VARIANT_TYPE ("(oa{sv})"));

  append_message (vvm_msg->dbus_path, service, vvm_msg, &message_builder);

  message = g_variant_builder_end (&message_builder);

  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 service->path,
                                 VVM_SERVICE_INTERFACE,
                                 "MessageAdded",
                                 message,
                                 &error);

  if (error != NULL)
    g_warning ("Error in Proxy call: %s\n", error->message);
}

static int
vvm_service_delete_vvm_email_from_server (struct vvm_service *service,
                                          struct voicemail   *vvm_msg)
{
  g_autofree char *URI = NULL;
  g_autofree char *mailbox = NULL;
  g_autofree char *mailbox_index = NULL;
  g_autofree char *delete_command = NULL;
  CURLcode res = CURLE_OK;

  deactivate_curl_structure (service);
  activate_curl_structure (service);

  DBG ("Deleting VVM Email from server");
  if (vvm_msg->uid)
    {
      //We have a uid, so use that
      mailbox = g_strdup ("UID");
      mailbox_index = g_strdup (vvm_msg->uid);
      delete_command = g_strdup_printf ("UID STORE %s +Flags \\Deleted", mailbox_index);
    }
  else {
      // I am using the mailindex to find the voicemail
      // (since I am looking at all of them)
      mailbox = g_strdup ("MAILINDEX");
      mailbox_index = g_strdup (vvm_msg->mailindex);
      delete_command = g_strdup_printf ("STORE %s +Flags \\Deleted", mailbox_index);
    }

  URI = g_strdup_printf ("%s;%s=%s", service->mailbox_URI, mailbox, mailbox_index);
  DBG ("URL: %s", URI);

  curl_easy_setopt (service->curl, CURLOPT_URL, URI);
  DBG ("Delete Command: %s", delete_command);
  curl_easy_setopt (service->curl, CURLOPT_CUSTOMREQUEST, delete_command);

  res = curl_easy_perform (service->curl);
  if (res == CURLE_OK)
    {
      DBG ("Deleted email Successfully, now expunging everything");
      curl_easy_setopt (service->curl, CURLOPT_CUSTOMREQUEST, "EXPUNGE");
      res = curl_easy_perform (service->curl);
      if (res == CURLE_OK)
        DBG ("Deleted email Successfully");
      else {
          g_warning ("Error expunging email: %s\n", curl_easy_strerror (res));
          return FALSE;
        }
    }
  else {
      g_warning ("Error deleting email: %s\n", curl_easy_strerror (res));
      return FALSE;
    }

  deactivate_curl_structure (service);
  activate_curl_structure (service);

  return TRUE;
}

int
vvm_service_new_vvm (struct vvm_service *service,
                     char               *sync_message,
                     char               *index,
                     const char         *mailbox_vvm_type)
{
  struct sms_control_message *sms_msg = NULL;
  struct voicemail *vvm_msg = NULL;
  g_autofree char *mailindex = NULL;
  g_autofree char *email_filename = NULL;
  g_autofree char *email_dir = NULL;
  g_autofree char *folderpath = NULL;

  if (service->mailbox_active == FALSE)
    {
      if (vvm_service_activate_vvm_imap_server (service) == TRUE)
        activate_curl_structure (service);
      else
        return FALSE;
    }

  vvm_msg = g_try_new0 (struct voicemail, 1);
  if (vvm_msg == NULL)
    {
      g_critical ("Could not allocate space for VVM Message!");
      return FALSE;
    }

  if (sync_message)
    {
      //This is a sync message
      sms_msg = g_try_new0 (struct sms_control_message, 1);
      if (sms_msg == NULL)
        {
          g_critical ("Could not allocate space for SMS SYNC Message!");
          return FALSE;
        }
      vvm_util_parse_sync_sms_message (sync_message, sms_msg, mailbox_vvm_type);
      if (sms_msg->sync_status_reason != SYNC_SMS_NEW_MESSAGE && sms_msg->sync_status_reason != SYNC_SMS_MAILBOX_UPDATE)
        {
          vvm_util_delete_status_message (sms_msg);
          DBG ("This is not a New Message SYNC SMS.");
          return TRUE;
        }
      if (sms_msg->uid)
        mailindex = g_strdup (sms_msg->uid);
      else {
          // Some SYNC messages don't have a UID, so just do a normal sync
          DBG ("SYNC messages without UID.");
          vvm_util_delete_status_message (sms_msg);
          vvm_service_sync_vvm_imap_server (service);
          return TRUE;
        }
    }
  else
    //This was from vvm_service_sync_vvm_imap_server()
    mailindex = g_strdup (index);
  if (vvmd_service_retrieve_headers (service, mailindex, sms_msg, vvm_msg) == FALSE)
    {
      if (sms_msg)
        vvm_util_delete_status_message (sms_msg);
      vvm_util_delete_vvm_message (vvm_msg);
      return FALSE;
    }
  if (sms_msg)
    vvm_util_delete_status_message (sms_msg);

  vvm_msg->file_uuid = vvm_store_generate_uuid_objpath ();

  //Retrieve voicemail email
  if (vvm_service_retrieve_vvm_email (service, vvm_msg) == FALSE)
    {
      vvm_util_delete_vvm_message (vvm_msg);
      return FALSE;
    }

  //Store voicemail email
  email_filename = g_strdup_printf ("%s%s", vvm_msg->file_uuid, VVM_META_EMAIL_SUFFIX);
  if (vvm_store (service->identity,
                 vvm_msg->contents,
                 NULL,
                 strlen (vvm_msg->contents),
                 email_filename, NULL) == FALSE)
    {
      g_critical ("Error storing VVM Email!");
      vvm_util_delete_vvm_message (vvm_msg);
      return FALSE;
    }
  email_dir = vvm_store_get_path (service->identity, " ");
  g_strstrip (email_dir);
  vvm_msg->email_filepath = g_strdup_printf ("%s%s", email_dir, email_filename);

  folderpath = vvm_store_get_path (service->identity, " ");
  if (folderpath == NULL)
    {
      g_critical ("Could not make directory for attachments!");
      return FALSE;
    }
  g_strstrip (folderpath);
  //Decode voicemail email attachments
  if (vvm_util_decode_vvm_all_email_attachments (vvm_msg, folderpath) == FALSE)
    {
      g_warning ("Error decoding VVM Email!");
      vvm_store_remove (service->identity, vvm_msg->file_uuid);
      vvm_util_delete_vvm_message (vvm_msg);
      return FALSE;
    }

  // vvm_msg->contents contains the entire email, and you already decoded
  // it. Let's free up the memory.
  g_free (vvm_msg->contents);
  vvm_msg->contents = NULL;

  vvm_msg->lifetime_status = VVM_LIFETIME_STATUS_NOT_READ;

  //Save Headers
  if (vvm_service_store_headers (service, vvm_msg) == FALSE)
    {
      g_warning ("Error storing headers!");
      vvm_store_remove_attachments (service->identity, vvm_msg->file_uuid, vvm_msg->attachments);
      vvm_store_remove (service->identity, vvm_msg->file_uuid);
      vvm_util_delete_vvm_message (vvm_msg);
      return FALSE;
    }

  vvm_message_register (service, vvm_msg);

  //Emit voicemail added
  emit_message_added (service, vvm_msg);

  //DBG("NOT DELETING YET");
  //return TRUE;

  //Delete voicemail from server
  if (vvm_service_delete_vvm_email_from_server (service, vvm_msg) == FALSE)
    {
      /*
       * Sometimes curl says there was an error but it works fine.
       * I would rather have a duplicate VVM versus accidentally
       * deleting one.
       */
      g_critical ("Error deleting email from server!");
      return FALSE;
    }

  return TRUE;
}

static gboolean
retry_sync_vvm_imap_server (gpointer user_data)
{
  struct vvm_service *service = user_data;
  DBG ("retrying sync....");
  if (vvm_service_sync_vvm_imap_server (service) == FALSE)
    g_critical ("Error resyncing voicemail!");
  return FALSE;
}

int
vvm_service_sync_vvm_imap_server (struct vvm_service *service)
{
  g_autofree char *URI = NULL;
  struct struct_string chunk = {0};
  CURLcode res = CURLE_OK;

  chunk.response = g_string_new (NULL);
  if (service->mailbox_active == FALSE)
    {
      if (vvm_service_activate_vvm_imap_server (service) == TRUE)
        activate_curl_structure (service);
      else
        return FALSE;
    }
  DBG ("Checking INBOX for new messages");
  URI = g_strdup_printf ("%s?ALL", service->mailbox_URI);
  curl_easy_setopt (service->curl, CURLOPT_URL, URI);
  curl_easy_setopt (service->curl, CURLOPT_WRITEFUNCTION, curl_string_cb);
  curl_easy_setopt (service->curl, CURLOPT_WRITEDATA, (void *)&chunk);

  res = curl_easy_perform (service->curl);
  if (res == CURLE_OK)
    {
      g_autofree char **tokens = NULL;
      g_autofree char **lines = NULL;
      g_autofree char *inbox_response;
      const char *inbox_response_search = NULL;
      g_autofree char *inbox_response_to_parse = NULL;
      lines = g_strsplit_set (chunk.response->str, "\r\n", 2);
      g_string_free (chunk.response, TRUE);
      inbox_response = g_strdup (lines[0]);
      DBG ("Returned: %s", inbox_response);
      if (inbox_response == NULL)
        {
          g_critical ("Something weird happened!");
          vvm_service_deactivate_vvm_imap_server (service);
          return FALSE;
        }
      /*
       * The IMAP response I have seen is: " * SEARCH 1 2 3 4", where
       * "1", "2", "3", and "4" are emails coming in
       *
       * We need to reverse " * SEARCH 1 2 3 4" to "4 3 2 1" so we don't delete
       * messages then stumble over them!
       */

      /* remove " * SEARCH" */
      inbox_response_search = g_strrstr (inbox_response, "SEARCH");
      if (inbox_response_search)
        {
          inbox_response_search = inbox_response_search + 6;
          inbox_response_to_parse = g_strdup (inbox_response_search);
        }
      else
        inbox_response_to_parse = g_strdup (inbox_response);

      /* Now we have " 1 2 3 4" */
      g_strstrip (inbox_response_to_parse);
      /* Now we have "1 2 3 4" */
      g_strreverse (inbox_response_to_parse);
      g_debug ("inbox_response_to_parse:%s", inbox_response_to_parse);
      /* Now we have "4 3 2 1" !*/
      tokens = g_strsplit (inbox_response_to_parse, " ", -1);
      for (int i = 0; tokens[i] != NULL; i++)
        {
          DBG ("Token: %s", tokens[i]);
          if (vvm_service_new_vvm (service, NULL, tokens[i], service->mailbox_vvm_type) == FALSE)
            {
              g_critical ("Error creating VVM!");
              g_timeout_add_seconds (60, retry_sync_vvm_imap_server, service);
              return FALSE;
            }
        }
      return TRUE;
    }
  else {
      g_warning ("curl_easy_perform() failed: %s\n", curl_easy_strerror (res));
      return FALSE;
    }
}

static void
vvm_load_settings (struct vvm_service *service)
{
  g_autoptr(GError) error = NULL;
  service->settings = vvm_settings_open (service->identity,
                                         SETTINGS_STORE);
  if (service->settings == NULL)
    return;

  service->mailbox_hostname = g_key_file_get_string (service->settings,
                                                     SETTINGS_GROUP_SERVICE,
                                                     "MailboxHostname", &error);
  if (error)
    {
      service->mailbox_hostname = g_strdup ("mailbox.invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxHostname", service->mailbox_hostname);
      g_clear_error (&error);
    }

  service->mailbox_port = g_key_file_get_string (service->settings,
                                                 SETTINGS_GROUP_SERVICE,
                                                 "MailboxPort", &error);
  if (error)
    {
      // 143 is the default non-secure IMAP port
      service->mailbox_port = g_strdup ("143");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxPort", service->mailbox_port);
      g_clear_error (&error);
    }

  service->mailbox_username = g_key_file_get_string (service->settings,
                                                     SETTINGS_GROUP_SERVICE,
                                                     "MailboxUsername", &error);
  if (error)
    {
      service->mailbox_username = g_strdup ("username_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxUsername", service->mailbox_username);
      g_clear_error (&error);
    }

  service->mailbox_password = g_key_file_get_string (service->settings,
                                                     SETTINGS_GROUP_SERVICE,
                                                     "MailboxPassword", &error);
  if (error)
    {
      service->mailbox_password = g_strdup ("password_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxPassword", service->mailbox_password);
      g_clear_error (&error);
    }

  service->mailbox_active = g_key_file_get_boolean (service->settings,
                                                    SETTINGS_GROUP_SERVICE,
                                                    "MailboxActive", &error);
  if (error)
    {
      service->mailbox_active = FALSE;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxActive", service->mailbox_active);
      g_clear_error (&error);
    }

  service->mailbox_URI = g_key_file_get_string (service->settings,
                                                SETTINGS_GROUP_SERVICE,
                                                "MailboxURI", &error);
  if (error)
    {
      service->mailbox_URI = g_strdup ("mailboxURI.invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxURI", service->mailbox_URI);
      g_clear_error (&error);
    }

  service->mailbox_auth = g_key_file_get_string (service->settings,
                                                 SETTINGS_GROUP_SERVICE,
                                                 "MailboxAuth", &error);
  if (error)
    {
      service->mailbox_auth = g_strdup ("AUTH=invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxAuth", service->mailbox_auth);
      g_clear_error (&error);
    }

  service->use_mailbox_interface = g_key_file_get_boolean (service->settings,
                                                           SETTINGS_GROUP_SERVICE,
                                                           "UseMailboxInterface", &error);
  if (error)
    {
      service->use_mailbox_interface = FALSE;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "UseMailboxInterface", service->use_mailbox_interface);
      g_clear_error (&error);
    }

  service->mailbox_starttls = g_key_file_get_boolean (service->settings,
                                                      SETTINGS_GROUP_SERVICE,
                                                      "MailboxStartTLS", &error);
  if (error)
    {
      service->mailbox_starttls = FALSE;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxStartTLS", service->mailbox_starttls);
      g_clear_error (&error);
    }

  service->mailbox_legacytls = g_key_file_get_boolean (service->settings,
                                                       SETTINGS_GROUP_SERVICE,
                                                       "MailboxLegacyTLS", &error);
  if (error)
    {
      service->mailbox_legacytls = FALSE;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "MailboxLegacyTLS", service->mailbox_legacytls);
      g_clear_error (&error);
    }

  service->mailbox_language = g_key_file_get_string (service->settings,
                                                     SETTINGS_GROUP_SERVICE,
                                                     "MailboxLanguage", &error);
  if (error)
    {
      service->mailbox_language = g_strdup ("language_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxLanguage", service->mailbox_language);
      g_clear_error (&error);
    }

  service->mailbox_greeting_length = g_key_file_get_string (service->settings,
                                                            SETTINGS_GROUP_SERVICE,
                                                            "GreetingLength", &error);
  if (error)
    {
      service->mailbox_greeting_length = g_strdup ("greeting_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "GreetingLength", service->mailbox_greeting_length);
      g_clear_error (&error);
    }

  service->mailbox_voice_signature_length = g_key_file_get_string (service->settings,
                                                                   SETTINGS_GROUP_SERVICE,
                                                                   "VSLength", &error);
  if (error)
    {
      service->mailbox_voice_signature_length = g_strdup ("VS_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "VSLength", service->mailbox_voice_signature_length);
      g_clear_error (&error);
    }

  service->mailbox_TUI_password_length = g_key_file_get_string (service->settings,
                                                                SETTINGS_GROUP_SERVICE,
                                                                "TUIPasswordLength", &error);
  if (error)
    {
      service->mailbox_TUI_password_length = g_strdup ("VS_invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "TUIPasswordLength", service->mailbox_TUI_password_length);
      g_clear_error (&error);
    }

  service->mailbox_vvm_type = g_key_file_get_string (service->settings,
                                                     SETTINGS_GROUP_SERVICE,
                                                     "MailboxVVMType", &error);
  if (error)
    {
      service->mailbox_vvm_type = g_strdup ("VVMType.invalid");

      g_key_file_set_string (service->settings, SETTINGS_GROUP_SERVICE,
                             "MailboxVVMType", service->mailbox_vvm_type);
      g_clear_error (&error);
    }

  service->allow_plaintext_connection = g_key_file_get_boolean (service->settings,
                                                                SETTINGS_GROUP_SERVICE,
                                                                "AllowPlaintextConnection", &error);
  if (error)
    {
      service->allow_plaintext_connection = FALSE;

      g_key_file_set_boolean (service->settings, SETTINGS_GROUP_SERVICE,
                              "AllowPlaintextConnection", service->allow_plaintext_connection);
      g_clear_error (&error);
    }
}

static void
emit_msg_status_changed (const char *path,
                         const char *new_status)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GVariant *changedproperty;
  g_autoptr(GError) error = NULL;

  changedproperty = g_variant_new_parsed ("('status', <%s>)", new_status);
  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 path,
                                 VVM_MESSAGE_INTERFACE,
                                 "PropertyChanged",
                                 changedproperty,
                                 &error);

  if (error != NULL)
    {
      g_warning ("Error in Proxy call: %s\n", error->message);
      g_clear_error (&error);
    }
}

static void
destroy_message (gpointer data)
{
  struct voicemail *vvm_msg = data;
  vvm_util_delete_vvm_message (vvm_msg);
}

struct vvm_service *
vvm_service_create (void)
{
  struct vvm_service *service;

  service = g_try_new0 (struct vvm_service, 1);
  if (service == NULL)
    return NULL;

  /*
   * The key in the hash table is the dbus path, which is destroyed
   * in destroy_message(). Thus, no need to have a key_destroy_func
   */
  service->messages = g_hash_table_new_full (g_str_hash, g_str_equal,
                                             NULL, destroy_message);
  if (service->messages == NULL)
    {
      g_free (service);
      return NULL;
    }

  DBG ("service %p", service);

  return service;
}

static void
emit_message_removed (const char *svc_path,
                      const char *msg_path)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GVariant *msgpathvariant;
  g_autoptr(GError) error = NULL;

  msgpathvariant = g_variant_new ("(o)", msg_path);
  DBG ("Message Removed: %s", g_variant_print (msgpathvariant, TRUE));
  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 svc_path,
                                 VVM_SERVICE_INTERFACE,
                                 "MessageRemoved",
                                 msgpathvariant,
                                 &error);

  if (error != NULL)
    {
      g_warning ("Error in Proxy call: %s\n", error->message);
      g_clear_error (&error);
    }
}

static void
vvm_message_dbus_unregister (unsigned int message_registration_id)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  g_dbus_connection_unregister_object (connection,
                                       message_registration_id);
}

static int
vvm_message_unregister (const struct vvm_service *service,
                        struct voicemail         *vvm_msg)
{
  emit_message_removed (service->path, vvm_msg->dbus_path);

  vvm_message_dbus_unregister (vvm_msg->message_registration_id);

  DBG ("message unregistered %s", vvm_msg->dbus_path);

  g_hash_table_remove (service->messages, vvm_msg->dbus_path);

  return 0;
}

static void
dbus_unregister_message (gpointer key,
                         gpointer value,
                         gpointer user_data)
{
  struct voicemail *vvm_msg = value;

  vvm_message_dbus_unregister (vvm_msg->message_registration_id);
}

static void
destroy_message_table (struct vvm_service *service)
{
  if (service->messages == NULL)
    return;

  g_hash_table_foreach (service->messages, dbus_unregister_message, service);

  g_hash_table_destroy (service->messages);
  service->messages = NULL;
}

static void
append_properties (GVariantBuilder    *service_builder,
                   struct vvm_service *service)
{
  g_variant_builder_add (service_builder, "o", service->path);

  g_variant_builder_open (service_builder, G_VARIANT_TYPE ("a{sv}"));

  g_variant_builder_add_parsed (service_builder,
                                "{'Identity', <%s>}",
                                service->identity);

  g_variant_builder_close (service_builder);
}


static void
emit_service_added (struct vvm_service *service)
{
  GDBusConnection         *connection = vvm_dbus_get_connection ();
  GVariantBuilder          service_builder;
  GVariant                *service_added;
  g_autoptr(GError) error = NULL;

  g_variant_builder_init (&service_builder, G_VARIANT_TYPE ("(oa{sv})"));

  DBG ("Service Added %p", service);

  append_properties (&service_builder, service);

  service_added = g_variant_builder_end (&service_builder);

  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 VVM_PATH,
                                 VVM_MANAGER_INTERFACE,
                                 "ServiceAdded",
                                 service_added,
                                 &error);

  if (error != NULL)
    {
      g_warning ("Error in Proxy call: %s\n", error->message);
      g_clear_error (&error);
    }
}


static void
emit_service_removed (struct vvm_service *service)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GVariant *servicepathvariant;
  g_autoptr(GError) error = NULL;

  servicepathvariant = g_variant_new ("(o)", service->path);

  g_dbus_connection_emit_signal (connection,
                                 NULL,
                                 VVM_PATH,
                                 VVM_MANAGER_INTERFACE,
                                 "ServiceRemoved",
                                 servicepathvariant,
                                 &error);

  if (error != NULL)
    {
      g_warning ("Error in Proxy call: %s\n", error->message);
      g_clear_error (&error);
    }
}

static gboolean
load_message_from_store (const char       *service_id,
                         const char       *uuid,
                         struct voicemail *vvm_msg)
{
  GKeyFile               *meta;
  g_autofree char        *vvm_path = NULL;
  int                     number_of_attachments;
  gboolean                path_changed = FALSE;

  vvm_path = vvm_store_get_path (service_id, " ");
  g_strstrip (vvm_path);

  meta = vvm_store_meta_open (service_id, uuid);
  if (meta == NULL)
    return FALSE;

  vvm_msg->message_date = g_key_file_get_string (meta, "info", "Date", NULL);
  if (vvm_msg->message_date == NULL)
    {
      g_critical ("There is no date!");
      return FALSE;
    }

  vvm_msg->message_sender = g_key_file_get_string (meta, "info", "Sender", NULL);
  if (vvm_msg->message_sender == NULL)
    {
      g_critical ("There is no sender!");
      return FALSE;
    }

  vvm_msg->to = g_key_file_get_string (meta, "info", "To", NULL);
  if (vvm_msg->to == NULL)
    {
      g_critical ("There is no To!");
      return FALSE;
    }

  vvm_msg->message_context = g_key_file_get_string (meta, "info", "MessageContext", NULL);

  vvm_msg->mime_version = g_key_file_get_string (meta, "info", "MIMEVersion", NULL);

  vvm_msg->content_type = g_key_file_get_string (meta, "info", "ContentType", NULL);
  if (vvm_msg->content_type == NULL)
    {
      g_critical ("There is no Content Type!");
      return FALSE;
    }

  vvm_msg->uid = g_key_file_get_string (meta, "info", "UID", NULL);

  vvm_msg->attachments = g_key_file_get_string (meta, "info", "Attachments", NULL);
  if (vvm_msg->attachments)
    {
      g_autofree char **Attachment_list = NULL;

      Attachment_list = g_strsplit (vvm_msg->attachments, ";", -1);
      number_of_attachments = g_strv_length (Attachment_list);
      for (int i = 0; i < number_of_attachments; i++)
        if (!g_file_test (Attachment_list[i], G_FILE_TEST_EXISTS))
          {
            g_autofree char *basename = NULL;

            path_changed = TRUE;
            basename = g_path_get_basename (Attachment_list[i]);
            DBG ("Attachment %s cannot be found!", Attachment_list[i]);
            g_free (Attachment_list[i]);
            Attachment_list[i] = g_strdup_printf ("%s%s", vvm_path, basename);
            if (!g_file_test (Attachment_list[i], G_FILE_TEST_EXISTS))
              {
                g_warning ("Attachment %s does not exist!", Attachment_list[i]);
                return FALSE;
              }
          }
      g_free (vvm_msg->attachments);
      vvm_msg->attachments = g_strjoinv (";", Attachment_list);
      if (path_changed)
        g_key_file_set_string (meta, "info", "Attachments", vvm_msg->attachments);
    }
  else {
      g_warning ("There are no attachments!");
      return FALSE;
    }

  vvm_msg->email_filepath = g_key_file_get_string (meta, "info", "EmailFilepath", NULL);
  if (vvm_msg->email_filepath)
    {
      if (!g_file_test (vvm_msg->email_filepath, G_FILE_TEST_EXISTS))
        {
          g_autofree char *basename = NULL;

          path_changed = TRUE;
          basename = g_path_get_basename (vvm_msg->email_filepath);
          DBG ("Email %s cannot be found!", vvm_msg->email_filepath);
          g_free (vvm_msg->email_filepath);
          vvm_msg->email_filepath = g_strdup_printf ("%s%s", vvm_path, basename);
          if (!g_file_test (vvm_msg->email_filepath, G_FILE_TEST_EXISTS))
            {
              g_warning ("Email %s does not exist!", vvm_msg->email_filepath);
              return FALSE;
            }
        }
      if (path_changed)
        g_key_file_set_string (meta, "info", "EmailFilepath", vvm_msg->email_filepath);
    }
  else {
      g_warning ("Cannot find the Email!");
      return FALSE;
    }

  vvm_msg->lifetime_status = g_key_file_get_integer (meta, "info", "LifetimeStatus", NULL);

  vvm_store_meta_close (service_id, uuid, meta, path_changed);
  return TRUE;
}

static void
process_message_on_start (struct vvm_service *service,
                          const char         *uuid)
{
  struct voicemail *vvm_msg = NULL;

  vvm_msg = g_try_new0 (struct voicemail, 1);
  if (vvm_msg == NULL)
    {
      g_critical ("Could not allocate space for VVM Message!");
      return;
    }

  vvm_msg->file_uuid = g_strdup (uuid);
  if (load_message_from_store (service->identity, uuid, vvm_msg) == FALSE)
    {
      DBG ("Failed to load_message_from_store() from VVM with uuid %s", uuid);
      vvm_util_delete_vvm_message (vvm_msg);
      return;
    }
  vvm_message_register (service, vvm_msg);
}

static void
load_messages (struct vvm_service *service)
{
  GDir *dir;
  const char *file;
  const char *homedir;
  g_autofree char *service_path = NULL;

  homedir = g_get_home_dir ();
  if (homedir == NULL)
    return;

  service_path = g_strdup_printf ("%s/%s/%s/", homedir, STORAGE_FOLDER, service->identity);

  dir = g_dir_open (service_path, 0, NULL);
  if (dir == NULL)
    return;

  while ((file = g_dir_read_name (dir)) != NULL)
    {
      const size_t suffix_len = 7;
      g_autofree char *uuid = NULL;

      if (g_str_has_suffix (file, VVM_META_UUID_SUFFIX) == FALSE)
        continue;

      if (strlen (file) - suffix_len == 0)
        continue;

      uuid = g_strndup (file, strlen (file) - suffix_len);

      process_message_on_start (service, uuid);
    }

  g_dir_close (dir);
}

GKeyFile *
vvm_service_get_keyfile (struct vvm_service *service)
{
  return service->settings;
}

int
vvm_service_register (struct vvm_service *service)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GDBusNodeInfo   *introspection_data = vvm_dbus_get_introspection_data ();
  g_autoptr(GError)  error = NULL;

  DBG ("service %p", service);

  if (service == NULL)
    return -EINVAL;

  if (service->identity == NULL)
    return -EINVAL;

  if (service->path != NULL)
    return -EBUSY;

  service->path = g_strdup_printf ("%s/%s", VVM_PATH, service->identity);
  if (service->path == NULL)
    return -ENOMEM;

  service_registration_id = g_dbus_connection_register_object (connection,
                                                               service->path,
                                                               introspection_data->interfaces[0],
                                                               &interface_vtable_service,
                                                               service,         // user_data
                                                               NULL,    // user_data_free_func
                                                               &error);   // GError**

  if (error)
    g_critical ("Error Registering Service: %s", error->message);

  g_assert (service_registration_id > 0);

  service_list = g_list_append (service_list, service);

  emit_service_added (service);

  vvm_load_settings (service);

  load_messages (service);

  if (service->mailbox_active)
    activate_curl_structure (service);

  return 0;
}

int
vvm_service_unref (struct vvm_service *service)
{
  if (service->messages != NULL)
    destroy_message_table (service);

  if (service->settings != NULL)
    {
      vvm_settings_close (service->identity, SETTINGS_STORE,
                          service->settings, TRUE);
      g_free (service->mailbox_interface);
      g_free (service->mailbox_hostname);
      g_free (service->mailbox_port);
      g_free (service->mailbox_username);
      g_free (service->mailbox_password);
      g_free (service->mailbox_auth);
      g_free (service->mailbox_URI);
      g_free (service->mailbox_language);
      g_free (service->mailbox_greeting_length);
      g_free (service->mailbox_voice_signature_length);
      g_free (service->mailbox_TUI_password_length);
      g_free (service->mailbox_vvm_type);
      service->settings = NULL;
    }

  g_free (service->identity);
  g_free (service);
  return 0;
}

int
vvm_service_unregister (struct vvm_service *service)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();

  DBG ("service %p", service);

  if (service == NULL)
    return -EINVAL;

  if (service->path == NULL)
    return -EINVAL;

  if (service->messages != NULL)
    destroy_message_table (service);

  if (service->settings != NULL)
    {
      vvm_settings_close (service->identity, SETTINGS_STORE,
                          service->settings, TRUE);
      g_free (service->mailbox_interface);
      g_free (service->mailbox_hostname);
      g_free (service->mailbox_port);
      g_free (service->mailbox_username);
      g_free (service->mailbox_password);
      g_free (service->mailbox_auth);
      g_free (service->mailbox_URI);
      g_free (service->mailbox_language);
      g_free (service->mailbox_greeting_length);
      g_free (service->mailbox_voice_signature_length);
      g_free (service->mailbox_TUI_password_length);
      g_free (service->mailbox_vvm_type);
      service->settings = NULL;
    }

  //Disconnect the service dbus interface
  g_dbus_connection_unregister_object (connection,
                                       service_registration_id);

  service_list = g_list_remove (service_list, service);

  emit_service_removed (service);

  g_clear_pointer (&service->country_code, g_free);
  g_clear_pointer (&service->path, g_free);
  g_clear_pointer (&service->interface, g_free);
  g_clear_pointer (&service->own_number, g_free);

  if (service->mailbox_active)
    deactivate_curl_structure (service);

  return 0;
}

int
vvm_service_set_identity (struct vvm_service *service,
                          const char         *identity)
{
  DBG ("service %p identity %s", service, identity);

  if (service == NULL)
    return -EINVAL;

  if (service->path != NULL)
    return -EBUSY;

  g_free (service->identity);
  service->identity = g_strdup (identity);

  return 0;
}

int
vvm_service_set_country_code (struct vvm_service *service,
                              const char         *imsi)
{
  const char *country_code;
  if (imsi == NULL)
    {
      g_warning ("vvm_service_set_country_code(): IMSI is NULL!");
      return FALSE;
    }
  country_code = get_country_iso_for_mcc (imsi);
  if (country_code == NULL)
    {
      g_warning ("vvm_service_set_country_code(): Country Code is NULL!");
      return FALSE;
    }

  g_free (service->country_code);
  service->country_code = g_strdup (country_code);
  DBG ("Service Country Code set to %s", service->country_code);

  return TRUE;
}

const char *
vvm_service_get_country_code (struct vvm_service *service)
{
  return service->country_code;
}

int
vvm_service_set_own_number (struct vvm_service *service,
                            const char         *own_number)
{
  if (own_number == NULL)
    {
      g_warning ("vvm_service_set_own_number(): Number is NULL!");
      return FALSE;
    }
  if (service->country_code == NULL)
    {
      g_warning ("Country Code is NULL!");
      return FALSE;
    }

  g_clear_pointer (&service->own_number, g_free);
  service->own_number = phone_utils_format_number_e164 (own_number,
                                                        service->country_code, FALSE);

  if (service->own_number)
    DBG ("Service own number set");
  else {
      g_warning ("Error setting own number!");
      return FALSE;
    }

  return TRUE;
}

int
__vvm_service_init (gboolean enable_debug)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();
  GDBusNodeInfo   *introspection_data = vvm_dbus_get_introspection_data ();
  g_autoptr(GError)  error = NULL;
  global_debug = enable_debug;

  DBG ("Starting Up VVMD Service Manager");
  manager_registration_id = g_dbus_connection_register_object (connection,
                                                               VVM_PATH,
                                                               introspection_data->interfaces[1],
                                                               &interface_vtable_manager,
                                                               NULL,    // user_data
                                                               NULL,    // user_data_free_func
                                                               &error);    // GError**

  if (error)
    g_critical ("Error Registering Manager: %s", error->message);
  return 0;
}

void
__vvm_service_cleanup (void)
{
  GDBusConnection *connection = vvm_dbus_get_connection ();

  DBG ("Cleaning Up VVMD Service Manager");

  //Disconnect the manager dbus interface
  g_dbus_connection_unregister_object (connection,
                                       manager_registration_id);
}
