
/*---------------------------------------------------------------------------*/
/* ios_main.cpp                                                              */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#define IOS_MAIN_CPP_

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <net/if.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <signal.h>
#include <CoreFoundation/CoreFoundation.h>

#include "platform/platform.h"
#include "platform/phone_ios.h"
#include "ios_main.h"
#include "client/ios/ios_async/ios_async.h"
#include "client/ios/ios_async/ios_event.h"

#include "common/os/iomux.h"
#include "client/linux/os-cpp/iomux.h"
#include "common/interface/file.h"
#include "common/interface/task.h"
#include "common/interface/stask.h"
#include "common/interface/socket.h"
#include "common/interface/certificates.h"
#include "common/interface/media.h"
#include "common/lib/tasks_file.h"
#include "common/service/openssl_shared/ssl.h"
#include "client/logflags/debug_logflags.h"
#include "client/config/debug_readlog.h"
#include "client/linux/client_main/appclient_main.h"
#include "client/linux/client_main/plugin_main.h"
#if TARGET_OS_IPHONE
#include "ios_screen_capture_receiver.h"
#endif

#if !TARGET_OS_IPHONE
extern bool phone_ios_no_update_service;
#endif
extern char phone_ios_separator_char;
extern void *ios_dsp_context;
extern "C" void ios_start_audio_unit(void *context);
extern "C" void ios_stop_audio_unit(void *context);
extern "C" void ios_restart_dsp(void *context);

extern "C" void ns_log(const char *format, ...);

#define IOS_MAIN_USE_SIGNED_CERT
#define CERT_FILE_NAME "cert.pem"

#define FILE_ "ios_main.cpp"

USING_NAMESPACE

/*---------------------------------------------------------------------------*/
#ifdef _O_RDWR

#define _O_SYNC     0

#else

#define _open       open
#define _read       read
#define _write      write
#define _close      close
#define _chmod      chmod

#define _O_APPEND   O_APPEND
#define _O_BINARY   0
#define _O_CREAT    O_CREAT
#define _O_EXCL     O_EXCL
#define _O_RDONLY   O_RDONLY
#define _O_RDWR     O_RDWR
#define _O_SYNC     O_SYNC
#define _O_TRUNC    O_TRUNC
#define _O_WRONLY   O_WRONLY

#define _S_IREAD    (S_IRUSR|S_IRGRP|S_IROTH)
#define _S_IWRITE   (S_IWUSR|S_IWGRP|S_IWOTH)

#endif
/*---------------------------------------------------------------------------*/

extern char phone_ios_files_dir_container[];
char phone_ios_separator_char='/';

const char *get_files_dir()
{
    return phone_ios_files_dir_container;
}

char get_separator_char()
{
    return phone_ios_separator_char;
}

void copy_to_external(const char *file_name)
{
}


/*---------------------------------------------------------------------------*/

Dl_info dlinfo_ = {0,0,0,0};

typedef struct {
    const char *machine;
    const char *device;
} machine_device_map_t;

static machine_device_map_t machine_device_map[] =
{
    { "iPhone1,1", "iPhone 1G" },
    { "iPhone1,2", "iPhone 3G" },
    { "iPhone2,1", "iPhone 3GS" },
    { "iPhone3,1", "iPhone 4 (GSM)" },
    { "iPhone3,3", "iPhone 4 (CDMA)" },
    { "iPhone4,1", "iPhone 4S" },
    { "iPhone5,1", "iPhone 5 (GSM)" },
    { "iPhone5,2", "iPhone 5 (GSM+CDMA)" },
    { "iPhone5,3", "iPhone 5c (GSM)" },
    { "iPhone5,4", "iPhone 5c (GSM+CDMA)" },
    { "iPhone6,1", "iPhone 5s (GSM)" },
    { "iPhone6,2", "iPhone 5s (GSM+CDMA)" },
    { "iPhone7,1", "iPhone 6 (GSM)" },
    { "iPhone7,2", "iPhone 6 (GSM+CDMA)" },
    { "iPhone8,1", "iPhone 6+ (GSM)" },
    { "iPhone8,2", "iPhone 6+ (Global)" },
    { "iPod1,1"  , "iPod Touch 1G" },
    { "iPod2,1"  , "iPod Touch 2G" },
    { "iPod3,1"  , "iPod Touch 3G" },
    { "iPod4,1"  , "iPod Touch 4G" },
    { "iPod5,1"  , "iPod Touch 5G" },
    { "iPad1,1"  , "iPad" },
    { "iPad2,1"  , "iPad 2 (WiFi)" },
    { "iPad2,2"  , "iPad 2 (GSM)" },
    { "iPad2,3"  , "iPad 2 (CDMA)" },
    { "iPad2,4"  , "iPad 2 (WiFi)" },
    { "iPad2,5"  , "iPad Mini (WiFi)" },
    { "iPad2,6"  , "iPad Mini (GSM)" },
    { "iPad2,7"  , "iPad Mini (GSM+CDMA)" },
    { "iPad3,1"  , "iPad 3 (WiFi)" },
    { "iPad3,2"  , "iPad 3 (GSM+CDMA)" },
    { "iPad3,3"  , "iPad 3 (GSM)" },
    { "iPad3,4"  , "iPad 4 (WiFi)" },
    { "iPad3,5"  , "iPad 4 (GSM)" },
    { "iPad3,6"  , "iPad 4 (GSM+CDMA)" },
    { "iPad4,1"  , "iPad Air (WiFi)" },
    { "iPad4,2"  , "iPad Air (GSM)" },
    { "iPad4,4"  , "iPad Mini Retina (WiFi)" },
    { "iPad4,5"  , "iPad Mini Retina (GSM)" },
    { "i386"     , "Simulator" },
    { "x86_64"   , "Simulator" },
};

extern "C" void my_main(int argc, char * argv[])
{
    dladdr(__builtin_return_address(0), &dlinfo_);
    ERR(("BASE %s %x %s %x", dlinfo_.dli_fname, dlinfo_.dli_fbase, dlinfo_.dli_sname, dlinfo_.dli_saddr));

    struct tm tm, tm_gmt;
    struct timeval tv;
    time_t tlocal;
    tzset();
    gettimeofday(&tv, (struct timezone *)NULL);
    tlocal = tv.tv_sec;
    localtime_r(&tlocal, &tm);
    tlocal = tv.tv_sec + tm.tm_gmtoff;
    gmtime_r(&tlocal, &tm_gmt);
    ERR(("TIME %s/%s %i/%i: %02u.%02u.%02u %02u:%02u (%02u:%02u)",
        tzname[0], tzname[1], tm.tm_gmtoff, tm.tm_isdst,
        tm.tm_mday, tm.tm_mon + 1, tm.tm_year - 100, tm.tm_hour, tm.tm_min,
        tm_gmt.tm_hour, tm_gmt.tm_min));

    int mib[2], ret, ncpu, optional_neon, i;
    const char *device;
    size_t len;

    mib[0] = CTL_HW;
    mib[1] = HW_MACHINE;
    sysctl(mib, 2, NULL, &len, NULL, 0);
    char *machine = (char *)(malloc(len));
    sysctl(mib, 2, machine, &len, NULL, 0);
    i = 0;
    while ((i < sizeof(machine_device_map) / sizeof(machine_device_map[0])) && (strcmp(machine_device_map[i].machine, machine) != 0)) {
        i++;
    }
    if (i < sizeof(machine_device_map) / sizeof(machine_device_map[0])) {
        device = machine_device_map[i].device;
    }
    else {
        device = "unknown";
    }
    ERR(("Machine %s Device %s", machine, device));
    free(machine);

    mib[0] = CTL_HW;
    mib[1] = HW_MODEL;
    sysctl(mib, 2, NULL, &len, NULL, 0);
    char *model = (char *)(malloc(len));
    sysctl(mib, 2, model, &len, NULL, 0);
    mib[0] = CTL_HW;
    mib[1] = HW_NCPU;
    len = sizeof(ncpu);
    sysctl(mib, 2, &ncpu, &len, NULL, 0);
    len = sizeof(optional_neon);
    ret = sysctlbyname("hw.optional.neon", &optional_neon, &len, NULL, 0);
    ERR(("Model %s CPU %i,%s,%i", model, ncpu,
#ifdef __arm64__
        "arm64"
#endif
#ifdef __arm__
        "arm"
#endif
        "",
        (ret >= 0) && optional_neon));
    free(model);

/*
    kernel->update_mac(0);
    kernel->update_version();
*/
}


void my_exit(void)
{
}

/*---------------------------------------------------------------------------*/

t_ios_async_handler ios_async_handler_table[] =
{
DEFINE_IOS_ASYNC_HANDLER
    { 0, 0, 0 }
};

t_ios_event_handler ios_event_handler_table[] =
{
DEFINE_IOS_EVENT_HANDLER
    { 0, 0 }
};


#define IN_DOORBELL_FORMS_QUEUE_MASK IOS_EVENT_QUEUE_MASK
#define IN_DOORBELL_SWITCH_AUDIO_UNIT 0x00800000
#define IN_DOORBELL_RESTART_DSP_MASK 0x03000000
#define IN_DOORBELL_RESTART_DSP_LSB  (((IN_DOORBELL_RESTART_DSP_MASK ^ (IN_DOORBELL_RESTART_DSP_MASK - 1)) >> 1) + 1)
#define IN_DOORBELL_MANUALLY_STARTED 0x04000000
#define IN_DOORBELL_FLASH_BACKUP     0x08000000
#define IN_DOORBELL_RESTART_LISTEN   0x10000000
#define IN_DOORBELL_CONNECTIVITY_MASK 0x60000000
#define IN_DOORBELL_CONNECTIVITY_LSB (((IN_DOORBELL_CONNECTIVITY_MASK ^ (IN_DOORBELL_CONNECTIVITY_MASK - 1)) >> 1) + 1)
#define IN_DOORBELL_QUIT             0x80000000

#define OUT_DOORBELL_EXIT            0x80000000

class ios_async ios_async;
class ios_event ios_event;
volatile bool connectivity_on;
volatile bool audio_unit_on;
bool signal_pipe_created = false;
int signal_pipefd[2];
bool wait_pipe_created = false;
int wait_pipefd[2];
struct pollfd wait_pipe_pollfd;
volatile bool connectivity_lan = true;
volatile bool manually_started = false;
dword in_doorbell;
bool fresh_install = false;

void ios_main_sleep(dword ms)
{
    if (wait_pipe_created) {
        poll(&wait_pipe_pollfd, 1, ms);
    }
    else {
        poll(0, 0, ms);
    }
}


class ios_main *the_main;

class ios_main_shutdown_timer : public ITimer {
public:
    ios_main_shutdown_timer(IIoMux * ioMux, UTimerSubclass<ios_main_shutdown_timer> * owner) : ITimer(ioMux, owner) {};
};

class ios_main : public UIoContext, public IIoMainLoopExtension, public UAppClientMain,
#if TARGET_OS_IPHONE
    public UIosScreenCaptureReceiver,
#endif
    public UTimerSubclass<ios_main_shutdown_timer>
{
    void TimerOnTimeout(ios_main_shutdown_timer * timer);
    void IoContextNotify(bool indRX, bool indTX);
    void IoMainLoopExtension(int timeoutMs);
    void DebugLogFlagsChanged(class DebugLogFlags * debugLogFlags, const char * appName, const char * appDomain, ulong64 flags, bool noHexdump, UDebugLogFlags * src);
    void DebugReadLogAddFiles(class DebugReadLog * debugReadLog);
    void AppClientMainCertificate(const char * certString);
    void AppClientMainListening(const char * urlToLoad, const char * webserverUrl, const char * startFixCertUrl);
    void AppClientMainSetLanguageId(int languageId);
    void AppClientMainRegistrationCheckConfirm();
    void AppClientMainDisablePowerSaving(bool active);
    void AppClientMainSetFocus(const char * app, const char * title);
    void AppClientMainAppWindowOpened(const char * app, const char * title);
    void AppClientMainSetResize(const char * app, int width, int height);
    void AppClientMainRegistrationState(bool up);
    void AppClientMainDialGSM(const char * number);
    void AppClientMainShowIfLocked(const char * ref, bool show);
    void AppClientMainShowUnlock();
    void AppClientMainAddLocalNotification(const char * ref, int type, const char * title, const char * text, const char * smallIcon, const char * largeIcon,
        const char * soundUrl, const char * buttonTitle, const char * acceptTitle, const char * rejectTitle, const char * link);
    void AppClientMainRemoveLocalNotification(const char * ref);
    void AppClientMainSoftwareUpdate();
    void AppClientMainSetBadgeCount(const char * ref, unsigned count, const char * text);
    void AppClientMainSetPushPassword(const char * pwd);
    void AppClientMainDetectWebcams(const char *context);
    void AppClientMainStartWebcam(const char *id, const char *context, int width, int height, bool hdVideo);
    void AppClientMainStopWebcam(const char *id);
    void AppClientMainWebcamFullIntraRequest(const char *id);
    void AppClientMainVideoTranscode(int id, dword timestamp, dword duration, short sequenceDiff, const void * buf, int len, int width, int height, int stride, dword flags, int from, int to);
    void AppClientMainKeyboardEvent(int code, bool keyDown);
    void AppClientMainMouseEvent(int x, int y, bool right, bool keyDown);
    void AppClientMainMouseWheelEvent(bool direction);
    void AppClientMainContactsSearch(const char * ref, const char * search);
    void AppClientMainContactsLookup(const char * ref, const char * number, const char * prefixIntl, const char * prefixNtl, const char * prefixSubs, const char * areaCode, const char * countryCode);
    void AppClientMainWebserverGet(const char * context, const char * resourceName);
    void AppClientMainWebserverGetSent(const char * context);
    void AppClientMainShutdown();
    void AppClientMainEMailLog();
    void AppClientMainClearLog();
    void AppClientMainOrientationAdaptationOff(bool off);
    void AppClientMainDisplayOnIfCharging(int displayOnIfCharging);
    void AppClientMainStatusBarAppearance(bool hidden, bool blackText, dword color);
    void AppClientMainRingInHeadset(bool enable);
    void AppClientMainHideFromNativeCallList(bool hide);
    void AppClientMainActiveAfterClose(bool active);
    void AppClientMainActiveAfterCloseAsked(bool asked);
    void AppClientMainSetOffline(int autoOffline);
    void AppClientMainSetAutostart(bool autostart);
    void AppClientMainSetShowDesktopNotifications(bool showDesktopNotifications);
    void AppClientMainSetHotkey(int type, const char * hotkey);
    void AppClientMainStartExternalApp(const char * app, const char * param);
    void AppClientMainSetDocking(int docking);
    void AppClientMainCxUse(bool use, const char *ringtone, bool supportsVideo, int maximumCallGroups, int maximumCallsPerCallGroup);
    void AppClientMainCxReportNewIncomingCall(const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
        bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping);
    void AppClientMainCxReportOutgoingCallConnecting(const char *uuid);
    void AppClientMainCxReportOutgoingCallConnected(const char *uuid);
    void AppClientMainCxReportCallUpdated(const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
        bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping);
    void AppClientMainCxReportCallEnded(const char *uuid, int reason);
    void AppClientMainCxRequestStartCall(const char *uuid, const char *number, const char *name, bool video);
    void AppClientMainCxRequestEndCall(const char *uuid);
    void AppClientMainCxRequestSetHeldCall(const char *uuid, bool held);
    void AppClientMainCxRequestSetMutedCall(const char *uuid, bool muted);
    void AppClientMainCxRequestSetGroupCall(const char *uuid, const char *with);
    void AppClientMainCxRequestPlayDTMF(const char *uuid, const char *digits);
    void AppClientMainStartScreenCapture();
    void AppClientMainStopScreenCapture();
    void AppClientMainRccAppConnected(bool up);
    void AppClientMainFixCert(const char * url, const byte * cert, size_t certLen);
    int AppClientMainCertMobileconfig(byte * buf, const byte * cert, size_t certLen);
public:
    ios_main(class IIoMux *iomux);
    ~ios_main();

    class IIoMux *iomux;
    async_forms *async_forms;
#if TARGET_OS_IPHONE
    IosScreenCaptureReceiver *screenCaptureReceiver;
    void IosScreenCaptureRunning(bool running);
#endif
    ios_main_shutdown_timer shutdownTimer;
    int keep_active_counter;

    void start_keep_active();
    void stop_keep_active();
};

ios_main::ios_main(class IIoMux *iomux) :
    iomux(iomux),
    shutdownTimer(iomux, this)
{
    async_forms = new class async_forms(&ios_async);
#if TARGET_OS_IPHONE
    screenCaptureReceiver = NULL;
#endif
    keep_active_counter = 0;

    (static_cast<IIoMuxEx *>(iomux))->SetMainLoopExtension(this);
    iomux->FDUnblock(signal_pipefd[0]);
    iomux->FDAdd(signal_pipefd[0], this, false);
}

ios_main::~ios_main()
{
#if TARGET_OS_IPHONE
    if (screenCaptureReceiver) {
        delete screenCaptureReceiver;
        screenCaptureReceiver = NULL;
    }
#endif
    delete async_forms;
    iomux->FDClose(this);
    signal_pipefd[0] = -1;
}

extern "C" void onRegistrationCheckConfirm();

void ios_main::start_keep_active()
{
    keep_active_counter++;
}

void ios_main::stop_keep_active()
{
    keep_active_counter--;
}

void ios_main::IoMainLoopExtension(int timeoutMs)
{
    if ((ios_async_in_doorbell ^ in_doorbell) & IOS_ASYNC_QUEUE_MASK) {
        in_doorbell = ios_async_in_doorbell;
        phone_ios_async_signal_handler();
    }
}

void ios_main::DebugLogFlagsChanged(class DebugLogFlags * debugLogFlags, const char * appName, const char * appDomain, ulong64 flags, bool noHexdump, UDebugLogFlags * src)
{
    set_forms_property(&ios_async, "CPU/LOG-BROWSER", (debugLogFlags->CommonLogFlags() & LOG_CONSOLE) ? "1" : "0");
    set_forms_property(&ios_async, "CPU/SAVE-LOG", debugLogFlags->Enabled() ? "1" : "0");
}

void ios_main::DebugReadLogAddFiles(class DebugReadLog * debugReadLog)
{
#if 0 // sma
    char files_dir_buf[2048];
    const char * files_dir = get_files_dir();
    IDirectory * dir = FileSystem::OpenDirectory(files_dir, NULL);
    if (files_dir[1] != '\0') {
        unsigned l = strlen(files_dir);
        memcpy(files_dir_buf, files_dir, l);
        files_dir_buf[l++] = get_separator_char();
        files_dir_buf[l] = '\0';
        files_dir = files_dir_buf;
    }
    debugReadLog->Add(files_dir, "config.json");
    if (dir && dir->IsOpen()) {
        class IDirectoryEntry * dirEntry = dir->ReadNext();
        while (dirEntry) {
            if (dirEntry->IsFile()) {
                const char * f = dirEntry->GetName();
                unsigned l = strlen(f);
                if ((l >= 9) && (memcmp(f, "crash", 5) == 0) && (memcmp(&(f[l-4]), ".txt", 4) == 0)) {
                    debugReadLog->Add(files_dir, f);
                }
            }
            dirEntry = dir->ReadNext();
        }
        dir->Close();
    }
    debugReadLog->Add(files_dir, "trace3.txt");
    debugReadLog->Add(files_dir, "trace2.txt");
    debugReadLog->Add(files_dir, "trace1.txt");
    debugReadLog->Add(files_dir, "trace0.txt");
    debugReadLog->Add(files_dir, "trace.txt");
#endif
}

void ios_main::AppClientMainCertificate(const char * certString)
{
    set_forms_property(&ios_async, "FORMS/CERTIFICATE", certString);
}

void ios_main::AppClientMainListening(const char * urlToLoad, const char * webserverUrl, const char * startFixCertUrl)
{
    char buf[256];
    _sprintf(buf, PHONE_IOS_LOG_APP_NAME "/%s %s", _BUILD_STRING_,
#if TARGET_OS_IPHONE
#if TARGET_IPHONE_SIMULATOR
        "(iOS Simulator)"
#else
        "(iOS)"
#endif
#else
#if TARGET_IPHONE_SIMULATOR
        "(macOS Simulator)"
#else
        "(macOS)"
#endif
#endif
        );
    forms_webserver_started(&ios_async, urlToLoad, buf, webserverUrl, startFixCertUrl);
}

void ios_main::AppClientMainSetLanguageId(int languageId)
{
    forms_set_language(&ios_async, languageId);
}

void ios_main::AppClientMainRegistrationCheckConfirm()
{
    set_forms_property(&ios_async, "PHONE/RAS-REGISTRATION-CHECK-CONFIRM", "");
}

void ios_main::AppClientMainRegistrationState(bool up)
{
    set_forms_property(&ios_async, "PHONE/ACTIVE-USER-REGSTATE", up ? "1" : "0");
}

void ios_main::AppClientMainDisablePowerSaving(bool active)
{
    set_forms_property(&ios_async, "FORMS/DISABLE-POWER-SAVING", active ? "1" : "0");
}

void ios_main::AppClientMainSetFocus(const char * app, const char * title)
{
    forms_set_focus(&ios_async, app, title);
}

void ios_main::AppClientMainAppWindowOpened(const char * app, const char * title)
{
    forms_app_window_opened(&ios_async, app, title);
}

void ios_main::AppClientMainSetResize(const char * app, int width, int height)
{
    forms_set_resize(&ios_async, app, width, height);
}

void ios_main::AppClientMainDialGSM(const char * number)
{
    set_forms_property(&ios_async, "FORMS/DIAL-DIRECT-GSM", number);
}

void ios_main::AppClientMainShowIfLocked(const char * ref, bool show)
{
    set_forms_property(&ios_async, show ? "FORMS/SHOW-IF-LOCKED" : "FORMS/HIDE-IF-LOCKED", ref);
}

void ios_main::AppClientMainShowUnlock()
{
    set_forms_property(&ios_async, "FORMS/SHOW-UNLOCK", "1");
}

void ios_main::AppClientMainAddLocalNotification(const char * ref, int type, const char * title, const char * text, const char * smallIcon, const char * largeIcon,
    const char * soundUrl, const char * buttonTitle, const char * acceptTitle, const char * rejectTitle, const char * link)
{
    forms_add_local_notification(&ios_async, ref, type, title, text, smallIcon, largeIcon,
        soundUrl, buttonTitle, acceptTitle, rejectTitle, link);
}

void ios_main::AppClientMainRemoveLocalNotification(const char * ref)
{
    forms_remove_local_notification(&ios_async, ref);
}

void ios_main::AppClientMainSetBadgeCount(const char * ref, unsigned count, const char * text)
{
    forms_set_badge_count(&ios_async, ref, count, text);
}

void ios_main::AppClientMainSetPushPassword(const char * pwd)
{
    set_forms_property(&ios_async, "FORMS/PUSH-PASSWORD", pwd);
}

void ios_main::AppClientMainDetectWebcams(const char *context)
{
    forms_detect_webcams(&ios_async, context);
}

void ios_main::AppClientMainStartWebcam(const char *id, const char *context, int width, int height, bool hdVideo)
{
    forms_start_webcam(&ios_async, id, context, width, height, hdVideo);
}

void ios_main::AppClientMainKeyboardEvent(int code, bool keyDown)
{
    forms_keyboard_event(&ios_async, code, keyDown);
};

void ios_main::AppClientMainMouseEvent(int x, int y, bool right, bool keyDown)
{
    forms_mouse_event(&ios_async, x, y, right, keyDown);
}

void ios_main::AppClientMainMouseWheelEvent(bool direction)
{
    forms_mouse_wheel_event(&ios_async, direction);
}

void ios_main::AppClientMainStopWebcam(const char *id)
{
    forms_stop_webcam(&ios_async, id);
}

void ios_main::AppClientMainWebcamFullIntraRequest(const char *id)
{
    forms_webcam_full_intra_request(&ios_async, id);
}

void ios_main::AppClientMainVideoTranscode(int id, dword timestamp, dword duration, short sequenceDiff, const void * buf, int len, int width, int height, int stride, dword flags, int from, int to)
{
    forms_video_transcode(&ios_async, id, timestamp, duration, sequenceDiff, buf, len, width, height, stride, flags, from, to);
}

void ios_main::AppClientMainContactsSearch(const char * ref, const char * pattern)
{
    forms_contacts_search(&ios_async, ref, pattern);
}

void phone_contacts_search_more()
{
    forms_contacts_search_more(&ios_async);
}

void ios_main::AppClientMainContactsLookup(const char * ref, const char * number, const char * prefixIntl, const char * prefixNtl, const char * prefixSubs, const char * areaCode, const char * countryCode)
{
    forms_contacts_lookup(&ios_async, ref, number, prefixIntl, prefixNtl, prefixSubs, areaCode, countryCode);
}

void ios_main::AppClientMainWebserverGet(const char * context, const char * resourceName)
{
    forms_webserver_get(&ios_async, context, resourceName);
}

void ios_main::AppClientMainWebserverGetSent(const char * context)
{
    forms_webserver_get_sent(&ios_async, context);
}

void ios_main::AppClientMainShutdown()
{
    set_forms_property(&ios_async, "CPU/SHUTDOWN", "1");
}

void ios_main::AppClientMainEMailLog()
{
    set_forms_property(&ios_async, "CPU/EMAIL-LOGS", "1");
}

void ios_main::AppClientMainClearLog()
{
    set_forms_property(&ios_async, "CPU/CLEAR-LOGS", "1");
}

void ios_main::AppClientMainOrientationAdaptationOff(bool off)
{
    set_forms_property(&ios_async, "FORMS/ORIENTATION-ADAPTATION-OFF", off ? "1" : "0");
}

void ios_main::AppClientMainDisplayOnIfCharging(int displayOnIfCharging)
{
    char buf[10];
    _sprintf(buf, "%u", displayOnIfCharging);
    set_forms_property(&ios_async, "FORMS/DISPLAY-ON-IF-CHARGING", buf);
}

void ios_main::AppClientMainStatusBarAppearance(bool hidden, bool blackText, dword color)
{
    char buf[10];
    _sprintf(buf, "%u", (hidden ? FORMS_STATUS_BAR_APPEARANCE_HIDDEN : 0) | (blackText ? FORMS_STATUS_BAR_APPEARANCE_BLACK_TEXT : 0) | color);
    set_forms_property(&ios_async, "FORMS/STATUS-BAR-APPEARANCE", buf);
}

void ios_main::AppClientMainSoftwareUpdate()
{
    set_forms_property(&ios_async, "FORMS/SOFTWARE-UPDATE", "");
}

void ios_main::AppClientMainRingInHeadset(bool enable)
{
    set_forms_property(&ios_async, "FORMS/RING-IN-HEADSET", enable ? "1" : "0");
}

void ios_main::AppClientMainHideFromNativeCallList(bool hide)
{
    set_forms_property(&ios_async, "FORMS/HIDE-FROM-NATIVE-CALL-LIST", hide ? "1" : "0");
}

void ios_main::AppClientMainActiveAfterClose(bool active)
{
    set_forms_property(&ios_async, "FORMS/ACTIVE-AFTER-CLOSE", active ? "1" : "0");
}

void ios_main::AppClientMainActiveAfterCloseAsked(bool asked)
{
    set_forms_property(&ios_async, "FORMS/ACTIVE-AFTER-CLOSE-ASKED", asked ? "1" : "0");
}

void ios_main::AppClientMainSetOffline(int autoOffline)
{
    forms_set_offline(&ios_async, autoOffline);
}

void ios_main::AppClientMainSetDocking(int docking)
{
    forms_set_docking(&ios_async, docking);
}

void ios_main::AppClientMainSetAutostart(bool autostart)
{
    forms_set_autostart(&ios_async, autostart);
}

void ios_main::AppClientMainSetShowDesktopNotifications(bool showDesktopNotifications)
{
    forms_set_desktop_notifications(&ios_async, showDesktopNotifications);
}

void ios_main::AppClientMainSetHotkey(int type, const char * hotkey)
{
    forms_set_hotkey(&ios_async, type, hotkey);
}
    
void ios_main::AppClientMainStartExternalApp(const char * app, const char * param)
{
    forms_start_external_app(&ios_async, app, param);
}

void ios_main::AppClientMainCxUse(bool use, const char *ringtone, bool supportsVideo, int maximumCallGroups, int maximumCallsPerCallGroup)
{
    forms_cx_use(&ios_async, use, ringtone, supportsVideo, maximumCallGroups, maximumCallsPerCallGroup);
}

void ios_main::AppClientMainCxReportNewIncomingCall(const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
    bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping)
{
    forms_cx_report_new_incoming_call(&ios_async, uuid, number, name, transfer, called,
        hasVideo, supportsDTMF, supportsHolding, supportsGrouping, supportsUngrouping);
}

void ios_main::AppClientMainCxReportOutgoingCallConnecting(const char *uuid)
{
    forms_cx_report_outgoing_call_connecting(&ios_async, uuid);
}

void ios_main::AppClientMainCxReportOutgoingCallConnected(const char *uuid)
{
    forms_cx_report_outgoing_call_connected(&ios_async, uuid);
}

void ios_main::AppClientMainCxReportCallUpdated(const char *uuid, const char *number, const char *name, const char *transfer, const char *called,
    bool hasVideo, bool supportsDTMF, bool supportsHolding, bool supportsGrouping, bool supportsUngrouping)
{
    forms_cx_report_call_updated(&ios_async, uuid, number, name, transfer, called,
        hasVideo, supportsDTMF, supportsHolding, supportsGrouping, supportsUngrouping);
}

void ios_main::AppClientMainCxReportCallEnded(const char *uuid, int reason)
{
    forms_cx_report_call_ended(&ios_async, uuid, reason);
}

void ios_main::AppClientMainCxRequestStartCall(const char *uuid, const char *number, const char *name, bool video)
{
    forms_cx_request_start_call(&ios_async, uuid, number, name, video);
}

void ios_main::AppClientMainCxRequestEndCall(const char *uuid)
{
    forms_cx_request_end_call(&ios_async, uuid);
}

void ios_main::AppClientMainCxRequestSetHeldCall(const char *uuid, bool held)
{
    forms_cx_request_set_held_call(&ios_async, uuid, held);
}

void ios_main::AppClientMainCxRequestSetMutedCall(const char *uuid, bool muted)
{
    forms_cx_request_set_muted_call(&ios_async, uuid, muted);
}

void ios_main::AppClientMainCxRequestSetGroupCall(const char *uuid, const char *with)
{
    forms_cx_request_set_group_call(&ios_async, uuid, with);
}

void ios_main::AppClientMainCxRequestPlayDTMF(const char *uuid, const char *digits)
{
    forms_cx_request_play_dtmf(&ios_async, uuid, digits);
}

void ios_main::AppClientMainStartScreenCapture()
{
#if TARGET_OS_IPHONE
    if (!screenCaptureReceiver) {
        screenCaptureReceiver = new IosScreenCaptureReceiver(iomux, this);
    }
#endif
    forms_start_screen_capture(&ios_async);
}

void ios_main::AppClientMainStopScreenCapture()
{
#if TARGET_OS_IPHONE
    if (screenCaptureReceiver) {
        delete screenCaptureReceiver;
        screenCaptureReceiver = NULL;
    }
#endif
    forms_stop_screen_capture(&ios_async);
}

#if TARGET_OS_IPHONE
void ios_main::IosScreenCaptureRunning(bool running)
{
    set_forms_property(&ios_async, "FORMS/SCREEN-CAPTURE-RUNNING", running ? "1" : "0");
}
#endif

void ios_main::AppClientMainRccAppConnected(bool up)
{
    set_forms_property(&ios_async, "PHONE/RCC-APP-CONNECTED", up ? "1" : "0");
}

void ios_main::AppClientMainFixCert(const char * url, const byte * cert, size_t certLen)
{
    char path[1024];
    int file;
    int l;

    l = strlen(get_files_dir());
    memcpy(path, get_files_dir(), l);
    path[l++] = get_separator_char();
    memcpy(path + l, CERT_FILE_NAME, sizeof(CERT_FILE_NAME));
    file = _open(path, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, _S_IREAD | _S_IWRITE);
    if(file == -1) {
        ERR(("Cannot create file \"%s\" %s", path, strerror(errno)));
    }
    l = _write(file, cert, certLen);
    _close(file);
    if(_chmod(path, _S_IREAD | _S_IWRITE) != 0) {
        if(errno != EPERM) {
            ERR(("Cannot chmod file \"%s\" %s", path, strerror(errno)));
        }
    }
    set_forms_property(&ios_async, "FORMS/FIX-CERT", url);
}

#ifdef IOS_MAIN_USE_SIGNED_CERT

static byte localhostcert[] = {
#include "localhostcert.h"
};

static byte localhostmobileconfig[] = {
#include "localhostmobileconfig.h"
};

#endif

static const char * certMobileconfigHead =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
"<dict>\n"
"	<key>PayloadContent</key>\n"
"	<array>\n"
"		<dict>\n"
"			<key>PayloadCertificateFileName</key>\n"
"			<string>" CERT_FILE_NAME "</string>\n"
"			<key>PayloadContent</key>\n"
"			<data>";

static const char * certMobileconfigTail =
"			</data>\n"
"			<key>PayloadDescription</key>\n"
"			<string>Provides device authentication (certificate or identity).</string>\n"
"			<key>PayloadDisplayName</key>\n"
"			<string>" CERT_FILE_NAME "</string>\n"
"			<key>PayloadIdentifier</key>\n"
"			<string>com.innovaphone.clientios.cert</string>\n"
"			<key>PayloadOrganization</key>\n"
"			<string>innovaphone</string>\n"
"			<key>PayloadType</key>\n"
"			<string>com.apple.security.root</string>\n"
"			<key>PayloadUUID</key>\n"
"			<string>6cce58a9-9b1d-49ba-a4a2-848bfe98064e</string>\n"
"			<key>PayloadVersion</key>\n"
"			<integer>1</integer>\n"
"		</dict>\n"
"	</array>\n"
"	<key>PayloadDescription</key>\n"
"	<string>innovaphone settings</string>\n"
"	<key>PayloadDisplayName</key>\n"
"	<string>innovaphone self-signed certificate</string>\n"
"	<key>PayloadIdentifier</key>\n"
"	<string>com.innovaphone.clientios.cert</string>\n"
"	<key>PayloadOrganization</key>\n"
"	<string>innovaphone</string>\n"
"	<key>PayloadType</key>\n"
"	<string>Configuration</string>\n"
"	<key>PayloadUUID</key>\n"
"	<string>33190309-6846-4375-925b-12d4ef35ca00</string>\n"
"	<key>PayloadVersion</key>\n"
"	<integer>1</integer>\n"
"</dict>\n"
"</plist>\n";

int ios_main::AppClientMainCertMobileconfig(byte * buf, const byte * cert, size_t certLen)
{
#ifdef IOS_MAIN_USE_SIGNED_CERT
    if ((certLen == sizeof(localhostcert)) && (memcmp(cert, localhostcert, sizeof(localhostcert)) == 0)) {
        if (buf) {
            memcpy(buf, localhostmobileconfig, sizeof(localhostmobileconfig));
        }
        return (sizeof(localhostmobileconfig));
    }
#endif
    int headLen = strlen(certMobileconfigHead);
    int tailLen = strlen(certMobileconfigTail);
    const char * begin = strstr((const char *)cert, "-----BEGIN CERTIFICATE-----") + strlen("-----BEGIN CERTIFICATE-----");
    const char * end = strstr(begin, "-----END CERTIFICATE-----");
    if (buf) {
        memcpy(buf, certMobileconfigHead, headLen);
        memcpy(&buf[headLen], begin, (end - begin));
        memcpy(&buf[headLen + (end - begin)], certMobileconfigTail, tailLen);
    }
    return (headLen + (end - begin) + tailLen);
}

void ios_main::TimerOnTimeout(ios_main_shutdown_timer * timer)
{
    iomux->ShutdownTimeout();
}

void ios_main::IoContextNotify(bool indRX, bool indTX)
{
    byte buf[1];
    dword d, e;

    while(read(signal_pipefd[0], buf, sizeof(buf)) == sizeof(buf)) {
    }
    d = ios_event_in_doorbell_req ^ ios_event_in_doorbell_ack;
    while(d != 0) {
        if(d & IN_DOORBELL_FORMS_QUEUE_MASK) {
            e = ios_event.dequeue();
            d ^= (ios_event_in_doorbell_ack & IN_DOORBELL_FORMS_QUEUE_MASK) ^ (e & IN_DOORBELL_FORMS_QUEUE_MASK);
            ios_event_in_doorbell_ack = (ios_event_in_doorbell_ack & ~IN_DOORBELL_FORMS_QUEUE_MASK) | (e & IN_DOORBELL_FORMS_QUEUE_MASK);
        }
        if(d & IN_DOORBELL_RESTART_LISTEN) {
            d &= ~IN_DOORBELL_RESTART_LISTEN;
            ios_event_in_doorbell_ack ^= IN_DOORBELL_RESTART_LISTEN;
            IAppPluginMain::CloseListen();
            IAppPluginMain::RestartListen();
        }
        if(d & IN_DOORBELL_CONNECTIVITY_MASK) {
            ios_event_in_doorbell_ack ^= d & IN_DOORBELL_CONNECTIVITY_MASK;
            d &= ~IN_DOORBELL_CONNECTIVITY_MASK;
/*
            sockets->update_ip_config(connectivity_on, connectivity_lan);
            if(sockets6) {
                sockets6->update_ip_config(connectivity_on, connectivity_lan);
            }
            if (connectivity_on) {
                if (modman->find("PHONE/SIG")) {
                    queue_event(modman->find("PHONE/SIG"), ras_event_registration_check());
                }
            }
*/
        }
        if(d & IN_DOORBELL_MANUALLY_STARTED) {
            d &= ~IN_DOORBELL_MANUALLY_STARTED;
            ios_event_in_doorbell_ack ^= IN_DOORBELL_MANUALLY_STARTED;
/*
            sockets->update_manually_started(manually_started);
            if(sockets6) {
                sockets6->update_manually_started(manually_started);
            }
*/
        }
        if(d & IN_DOORBELL_SWITCH_AUDIO_UNIT) {
            d &= ~IN_DOORBELL_SWITCH_AUDIO_UNIT;
            ios_event_in_doorbell_ack ^= IN_DOORBELL_SWITCH_AUDIO_UNIT;
            if (audio_unit_on) {
                ios_start_audio_unit(ios_dsp_context);
            }
            else {
                ios_stop_audio_unit(ios_dsp_context);
            }
        }
        if(d & IN_DOORBELL_RESTART_DSP_MASK) {
            ios_event_in_doorbell_ack ^= d & IN_DOORBELL_RESTART_DSP_MASK;
            d &= ~IN_DOORBELL_RESTART_DSP_MASK;
            ios_restart_dsp(ios_dsp_context);
        }
    }
}

void phone_main_shutdown()
{
    the_main->iomux->Shutdown();
    the_main->shutdownTimer.Start((100 - 10) * 20);
}

/*---------------------------------------------------------------------------*/

static int set_high_prio(const char *name)
{
    struct sched_param param;
	int policy=SCHED_RR;
	int result=0;

	memset(&param,0,sizeof(param));
    policy=SCHED_FIFO;
	param.sched_priority=sched_get_priority_max(policy);
	if((result=pthread_setschedparam(pthread_self(),policy, &param))) {
		if(result==EPERM){
			/*
				The linux kernel has 
				sched_get_priority_max(SCHED_OTHER)=sched_get_priority_max(SCHED_OTHER)=0.
				As long as we can't use SCHED_RR or SCHED_FIFO, the only way to increase priority of a calling thread
				is to use setpriority().
			*/
			if(setpriority(PRIO_PROCESS,0,-20)==-1){
				ns_log("%s setpriority() failed: %s, nevermind.",name,strerror(errno));
			}
            else {
//				ns_log("%s priority increased to maximum.",name);
			}
		}
        else {
            ns_log("%s: Set pthread_setschedparam failed: %s",name,strerror(result));
        }
	} else {
//		ns_log("%s priority set to %s and value (%i)",name,
//		           policy==SCHED_FIFO ? "SCHED_FIFO" : "SCHED_RR", param.sched_priority);
	}
    return result;
}

extern "C" void onResetNotify();
static bool main_thread_created = false;
static pthread_t main_thread_id;

#define IOS_MAIN_SIGNAL  SIGHUP

extern "C" void ios_main_signal_handler(int arg)
{
    *((dword *)(0xdeaddea1)) = 0;
}

extern "C" void my_main(int argc, char * argv[]);
void my_exit(void);

static void *main_thread(void *arg)
{
    struct sigaction sigact;
    struct sigaction old_sigact;
    struct pollfd struct_pollfd_table[1];
    int argc, poll_result;
    char *argv[16];
    unsigned l;
/*
    char path[1024];
    int file;
    size_t certLen;
    byte *cert;
    char *httpsCert;
*/

    memset(&sigact, 0, sizeof(sigact));
    sigact.sa_handler = ios_main_signal_handler;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, IOS_MAIN_SIGNAL);
    pthread_sigmask(SIG_UNBLOCK, &sigact.sa_mask, NULL);
    sigaction(IOS_MAIN_SIGNAL, &sigact, &old_sigact);

    if((IOS_ASYNC_HANDLER_COUNT != sizeof(ios_async_handler_table) / sizeof(ios_async_handler_table[0]) - 1)
     ||!ios_async.init(ios_async_handler_table, sizeof(ios_async_handler_table) / sizeof(ios_async_handler_table[0]) - 1, "MAIN")) {
        ns_log("Async init() failed !");
        return NULL;
    }
    if((IOS_EVENT_HANDLER_COUNT != sizeof(ios_event_handler_table) / sizeof(ios_event_handler_table[0]) - 1)
     ||!ios_event.init(ios_event_handler_table, sizeof(ios_event_handler_table) / sizeof(ios_event_handler_table[0]) - 1)) {
        ns_log("Event init() failed !");
        return NULL;
    }
    in_doorbell = ios_async_in_doorbell;

    my_main(0, 0);

    set_high_prio("main_thread");

    class IIoMux *iomux = IIoMux::Create();
    the_main = new class ios_main(iomux);

    argc = 0;
    char appName[] = "client";
    argv[argc++] = appName;
    char webserverLocalPath[PATH_MAX];
    _sprintf(webserverLocalPath, "--webserver=%s%c%s",
#if TARGET_IPHONE_SIMULATOR
        ".",
#else
        get_files_dir(),
#endif
        get_separator_char(), "w");
    argv[argc++] = webserverLocalPath;
    char httpPort[16];
    _sprintf(httpPort, "--http=%u", 10016);
    argv[argc++] = httpPort;
    char httpsPort[16];
    _sprintf(httpsPort, "--https=%u", 10017);
    argv[argc++] = httpsPort;
    char apirelayLocalPath[PATH_MAX];
    _sprintf(apirelayLocalPath, "--apirelay=%s%c%s",
#if TARGET_IPHONE_SIMULATOR
        ".",
#else
        get_files_dir(),
#endif
        get_separator_char(), "a");
    argv[argc++] = apirelayLocalPath;
#if !TARGET_OS_IPHONE
    char noUpdateService[] = "--no-update-service";
    if (phone_ios_no_update_service) {
        argv[argc++] = noUpdateService;
    }
    char httpHostname[] = "--http-hostname=127.0.0.1";
    argv[argc++] = httpHostname;
#endif
    char *pbxServerArg = NULL;
    CFStringRef pbxServerVal = NULL;
    CFPropertyListRef serverVal = NULL;
    CFPropertyListRef managedConfigVal = CFPreferencesCopyValue(CFSTR("com.apple.configuration.managed"), kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
    if (managedConfigVal) {
        if (CFGetTypeID(managedConfigVal) == CFDictionaryGetTypeID()) {
            if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)managedConfigVal, CFSTR("server"), (const void **)&pbxServerVal)) {
                pbxServerVal = NULL;
            }
        }
    }
    else {
        serverVal = CFPreferencesCopyAppValue(CFSTR("server"), kCFPreferencesCurrentApplication);
        if (serverVal) {
            if ((CFGetTypeID(serverVal) == CFStringGetTypeID()) && (CFStringGetLength((CFStringRef)serverVal) != 0)) {
                pbxServerVal = (CFStringRef)serverVal;
            }
        }
    }
    if (pbxServerVal) {
        CFIndex maxSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(pbxServerVal), kCFStringEncodingUTF8) + 1;
        l = strlen("--server=");
        pbxServerArg = (char *)(malloc(l + maxSize));
        memcpy(pbxServerArg, "--server=", l);
        const char *p = CFStringGetCStringPtr(pbxServerVal, kCFStringEncodingUTF8);
        if (p) {
            strcpy(&pbxServerArg[l], p);
        }
        else if (!CFStringGetCString(pbxServerVal, &pbxServerArg[l], maxSize, kCFStringEncodingUTF8)) {
            pbxServerArg[l] = '\0';
        }
        argv[argc++] = pbxServerArg;
    }
    if (serverVal) {
        CFRelease(serverVal);
    }
    if (managedConfigVal) {
        CFRelease(managedConfigVal);
    }
//    char reuseAddr[] = "--do-not-reuse-addr";
//    argv[argc++] = reuseAddr;
/*
    l = strlen(get_files_dir());
    memcpy(path, get_files_dir(), l);
    path[l++] = get_separator_char();
    memcpy(path + l, CERT_FILE_NAME, sizeof(CERT_FILE_NAME));
    file = _open(path, _O_RDONLY | _O_BINARY);
    if(file == -1) {
        ERR(("Cannot open file \"%s\" %s", path, strerror(errno)));
        fresh_install = true;
#ifdef IOS_MAIN_USE_SIGNED_CERT
        certLen = sizeof(localhostcert);
        cert = (byte *)(malloc(sizeof(localhostcert)));
        memcpy(cert, localhostcert, sizeof(localhostcert));
#else
        cert = ISSLProvider::SharedInstance()->CreateCertificate(NULL, &certLen);
#endif    
        file = _open(path, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, _S_IREAD | _S_IWRITE);
        if(file == -1) {
            ERR(("Cannot create file \"%s\" %s", path, strerror(errno)));
        }
        l = _write(file, cert, certLen);
        _close(file);
        if(_chmod(path, _S_IREAD | _S_IWRITE) != 0) {
            if(errno != EPERM) {
                ERR(("Cannot chmod file \"%s\" %s", path, strerror(errno)));
            }
        }
//        copy_to_external(CERT_FILE_NAME);
    }
    else {
        struct stat st;
        stat(path, &st);
        certLen = st.st_size;
        cert = (byte *)(malloc(certLen));
        _read(file, cert, certLen);
        _close(file);
    }

    l = strlen("--https-cert=");
    httpsCert = (char *)(malloc(l + certLen + 1));
    memcpy(httpsCert, "--https-cert=", l);
    memcpy(&httpsCert[l], cert, certLen);
    httpsCert[l+certLen] = '\0';
    free(cert);
    argv[argc++] = httpsCert;
*/
/*
    char httpHostname[] = "--http-hostname=localhost.innovaphone.com";
    argv[argc++] = httpHostname;
    char httpsCert[] = "--https-cert="
        "Bag Attributes\n"
        " 1.3.6.1.4.1.311.17.2: <No Values>\n"
        " localKeyID: 01 00 00 00\n"
        " friendlyName: le-981bcc4d-d853-4d53-8dd6-94cef68c0fcf\n"
        " Microsoft CSP Name: Microsoft RSA SChannel Cryptographic Provider\n"
        "Key Attributes\n"
        " X509v3 Key Usage: 10\n"
        "-----BEGIN RSA PRIVATE KEY-----\n"
        "MIIEpgIBAAKCAQEAs8gUtbUn/3tPac8gh+stTYpFT7DHo7U8V+CIEJ23PbB2dKih\n"
        "CPL2xumYSbWfP/aGBmW6cYeOrqD6jxPPD1HX2u9eg4bDI5oYuP710zzVgPjEyauZ\n"
        "eTNeLO4PNljgbSTp+IWgkw+UDC2+rFvxiSlymAauncRqhVcdf0izB7x+6K0/7rQG\n"
        "E/fnAow5zb7n3P2e2k0ocFLY67cCAfJD2O9XWKQS5PNqyvWnwUJKOi+HJXbDQCzN\n"
        "/CRvUvf7QPchKUlFDbcyxCST1FHM3axkPOMjEvSJb+skPTkEi5olhKAVCZRo4Ouw\n"
        "NqRtY4tjlCM0c3mKDOHfOhVCyeCwPq8srXMSzwIDAQABAoIBAQCgDAPby70i5LzK\n"
        "vlkAhy3adLjFe+QOal6hAMNAv5cMdRbPPfu2Qs3MQNKQ8/4kbCvecButy/cKoUbN\n"
        "rt3LjpmyTxx9zK/V9u4BawCfH+4WFeDihEBptIAnUXr7b93DbHRaCWPs55ZtzR+M\n"
        "w4FgESTyECjNTLljoS0iTi8LORRlv65itlT8j00wEQcJguZcYwPAPb7ttHijnvgG\n"
        "BJ0eXGxycHJMJTSlnj5cPB8WIHglpqnXgjqT+mONUqZuOtkEDDevNDx4jigMs1sc\n"
        "sMwh1EJ1MzmzOcqezaUSNRYvXgPQHs1Pdb6U03L4qgCgfF+k3o68XZtNuSNDxCrH\n"
        "g89o32thAoGBAO0dY+qn/vkNM51p+E0gFTuLxviBgOdJxi/Dbzr0e9JHJqi8n9zl\n"
        "TnOWe995RU6fHqZa6GiKv8BwkEAP72CT6KnYMrCBawB9OzII5mWURZgQuyqextjH\n"
        "+dQYKnzFjR0VGDMg8xYl8WIqQOkRcUb9dQzD8SR64KVZN6x3jMl70Iv/AoGBAMIZ\n"
        "tG6ewqqJnEZ/7bEh2TWyx6PU2MCW2HdFZMIM/hfYwWssQ7Owu1k1CT8xwTKYpY+A\n"
        "PJuCUT4fWNpl/YcL0ULPR/v9N/yXj77H0iZdq1dtdKROJjjBH+nLSxSTXstLgbFg\n"
        "Uftl9Z8aOIEyCaT35rcYWQ1LQTroNaNtNQ7eo7kxAoGBANQPWPDgVlR+zg6WW47+\n"
        "hn6+v7A4ggRYIntNGglr5eFl6Nk3+PEh00vk5dwzJRzvigXVf80faea9xQTGO5vw\n"
        "LEDaLltVI1FIrsxM0WE/fg5FyF6Rqdzu3CwngLgeJdpAp2fyq/S+8maMy4imJN0M\n"
        "iMZl5yBtFMRMSlJgiPUZWqYZAoGBAJZJwONGGgRhuJb9xtU4GC7z1pCL5ah5I/J1\n"
        "0KZP2Q4tztVk2KbxWJ6h6h78L1M1zjtmV4cxSCaJrwqrnbpK9CR+yoXactcpoytd\n"
        "HOKH9HslTQNLX1zibAtiVSgBU4ylUi49Hj1BnQUsEEQ4si8Bo6pxN8KLWl56Hc4E\n"
        "aOiyiqKBAoGBAMcyyarBplIfpMkT2S8xN38elyJG/sCn+4BYFw924EdanPgtwYM2\n"
        "q83oGjDxDwdj+hZ7Ex4xXpfoqKebCMMLqazvLuXr08IAdz4HNxPbDbWD8KtMBMSA\n"
        "7Q5hh2Fn+u46tA1nHLyDWbBhZppYI1h2JdxNBZm4yknmMU/Dzg4gCpua\n"
        "-----END RSA PRIVATE KEY-----\n"
        "Bag Attributes\n"
        " localKeyID: 01 00 00 00\n"
        " 1.3.6.1.4.1.311.17.3.92: 00 08 00 00\n"
        " 1.3.6.1.4.1.311.17.3.20: 4E E7 E2 F8 A0 C9 76 6A 50 27 F5 FC ED 1A EB 94 F0 A8 05 D0\n"
        " friendlyName: inno-star-sha2-2017\n"
        " 1.3.6.1.4.1.311.17.3.71: 49 00 4E 00 4E 00 4F 00 2D 00 45 00 58 00 2D 00 32 00 30 00 31 00 30 00 2E 00 69 00 6E 00 6E 00 6F 00 76 00 61 00 70 00 68 00 6F 00 6E 00 65 00 2E 00 73 00 69 00 66 00 69 00 00 00\n"
        " 1.3.6.1.4.1.311.17.3.75: 31 00 42 00 42 00 30 00 39 00 42 00 45 00 45 00 43 00 31 00 35 00 35 00 32 00 35 00 38 00 38 00 33 00 35 00 43 00 31 00 39 00 33 00 41 00 37 00 41 00 41 00 38 00 35 00 41 00 41 00 35 00 42 00 5F 00 00 00\n"
        "subject=/OU=Domain Control Validated/CN=*.innovaphone.com\n"
        "issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA\n"
        "-----BEGIN CERTIFICATE-----\n"
        "MIIFOzCCBCOgAwIBAgIQdjhW755QiWjsGf9WBp4xKzANBgkqhkiG9w0BAQsFADCB\n"
        "kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
        "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxNjA0BgNV\n"
        "BAMTLUNPTU9ETyBSU0EgRG9tYWluIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZlciBD\n"
        "QTAeFw0xNzExMTQwMDAwMDBaFw0yMTAyMTEyMzU5NTlaMD8xITAfBgNVBAsTGERv\n"
        "bWFpbiBDb250cm9sIFZhbGlkYXRlZDEaMBgGA1UEAwwRKi5pbm5vdmFwaG9uZS5j\n"
        "b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzyBS1tSf/e09pzyCH\n"
        "6y1NikVPsMejtTxX4IgQnbc9sHZ0qKEI8vbG6ZhJtZ8/9oYGZbpxh46uoPqPE88P\n"
        "Udfa716DhsMjmhi4/vXTPNWA+MTJq5l5M14s7g82WOBtJOn4haCTD5QMLb6sW/GJ\n"
        "KXKYBq6dxGqFVx1/SLMHvH7orT/utAYT9+cCjDnNvufc/Z7aTShwUtjrtwIB8kPY\n"
        "71dYpBLk82rK9afBQko6L4cldsNALM38JG9S9/tA9yEpSUUNtzLEJJPUUczdrGQ8\n"
        "4yMS9Ilv6yQ9OQSLmiWEoBUJlGjg67A2pG1ji2OUIzRzeYoM4d86FULJ4LA+ryyt\n"
        "cxLPAgMBAAGjggHfMIIB2zAfBgNVHSMEGDAWgBSQr2o6lFoL2JDqElZz30O0Oija\n"
        "5zAdBgNVHQ4EFgQUTufi+KDJdmpQJ/X87RrrlPCoBdAwDgYDVR0PAQH/BAQDAgWg\n"
        "MAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCME8G\n"
        "A1UdIARIMEYwOgYLKwYBBAGyMQECAgcwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9z\n"
        "ZWN1cmUuY29tb2RvLmNvbS9DUFMwCAYGZ4EMAQIBMFQGA1UdHwRNMEswSaBHoEWG\n"
        "Q2h0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQURvbWFpblZhbGlkYXRp\n"
        "b25TZWN1cmVTZXJ2ZXJDQS5jcmwwgYUGCCsGAQUFBwEBBHkwdzBPBggrBgEFBQcw\n"
        "AoZDaHR0cDovL2NydC5jb21vZG9jYS5jb20vQ09NT0RPUlNBRG9tYWluVmFsaWRh\n"
        "dGlvblNlY3VyZVNlcnZlckNBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au\n"
        "Y29tb2RvY2EuY29tMC0GA1UdEQQmMCSCESouaW5ub3ZhcGhvbmUuY29tgg9pbm5v\n"
        "dmFwaG9uZS5jb20wDQYJKoZIhvcNAQELBQADggEBAAJ0sot5KWM9JlZZNc18XLJs\n"
        "6Uufx+YBjE1Rjwv4riZ7SjsrczOGglztdW28DntNfY1ZHxYx9rCKbXACKsQWYl/H\n"
        "99pdcoJZrONcxyySzqLqcGHQgLUmLE0TptUU03In1H9SU/tTk+C8Za+2OADGLLkk\n"
        "ImGLxozqCqytYTL7/XU1GbnGNMGhqu4WssG6EmkaV3bjmiQygbf1oDyvMP0/fPeL\n"
        "cTXDLK2yRnEUxtYa9Ie1wCyK6ueYTDm+DcJIHS0vinQZn3TsW75Z8ZVcOIi7vbl4\n"
        "/bVu+vQis20NpdhDEWMWZHKfQzVZ5cmItHNEf0Gqx+dXhz3WrgFlV34c/E/e0EY=\n"
        "-----END CERTIFICATE-----\n"
        "Bag Attributes\n"
        " 1.3.6.1.4.1.311.17.3.92: 00 08 00 00\n"
        " 1.3.6.1.4.1.311.17.3.29: 06 F9 58 3C 00 A7 63 C2 3F B9 E0 65 A3 36 6D 55\n"
        " 1.3.6.1.4.1.311.17.3.20: AD BD 98 7A 34 B4 26 F7 FA C4 26 54 EF 03 BD E0 24 CB 54 1A\n"
        " 1.3.6.1.4.1.311.17.3.98: 68 7F A4 51 38 22 78 FF F0 C8 B1 1F 8D 43 D5 76 67 1C 6E B2 BC EA B4 13 FB 83 D9 65 D0 6D 2F F2\n"
        " 1.3.6.1.4.1.311.17.3.83: 30 24 30 22 06 0C 2B 06 01 04 01 B2 31 01 02 01 05 01 30 12 30 10 06 0A 2B 06 01 04 01 82 37 3C 01 01 03 02 00 C0\n"
        " friendlyName: The USERTrust Network\"\n"
        " 1.3.6.1.4.1.311.17.3.9: 30 52 06 08 2B 06 01 05 05 07 03 01 06 08 2B 06 01 05 05 07 03 02 06 08 2B 06 01 05 05 07 03 04 06 08 2B 06 01 05 05 07 03 03 06 08 2B 06 01 05 05 07 03 08 06 0A 2B 06 01 04 01 82 37 0A 03 04 06 08 2B 06 01 05 05 07 03 06 06 08 2B 06 01 05 05 07 03 07\n"
        "subject=/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root\n"
        "issuer=/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root\n"
        "-----BEGIN CERTIFICATE-----\n"
        "MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU\n"
        "MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs\n"
        "IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290\n"
        "MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux\n"
        "FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h\n"
        "bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v\n"
        "dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt\n"
        "H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9\n"
        "uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX\n"
        "mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX\n"
        "a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN\n"
        "E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0\n"
        "WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD\n"
        "VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0\n"
        "Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU\n"
        "cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx\n"
        "IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN\n"
        "AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH\n"
        "YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5\n"
        "6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC\n"
        "Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX\n"
        "c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a\n"
        "mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=\n"
        "-----END CERTIFICATE-----\n"
        "Bag Attributes\n"
        " 1.3.6.1.4.1.311.17.3.92: 00 10 00 00\n"
        " 1.3.6.1.4.1.311.17.3.20: BB AF 7E 02 3D FA A6 F1 3C 84 8E AD EE 38 98 EC D9 32 32 D4\n"
        " 1.3.6.1.4.1.311.17.3.89: 52 00 53 00 41 00 2F 00 53 00 48 00 41 00 33 00 38 00 34 00 00 00\n"
        " 1.3.6.1.4.1.311.17.3.75: 35 00 34 00 35 00 37 00 41 00 38 00 43 00 45 00 34 00 42 00 32 00 41 00 37 00 34 00 39 00 39 00 46 00 38 00 32 00 39 00 39 00 41 00 30 00 31 00 33 00 42 00 36 00 45 00 31 00 43 00 37 00 43 00 5F 00 00 00\n"
        "subject=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority\n"
        "issuer=/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root\n"
        "-----BEGIN CERTIFICATE-----\n"
        "MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv\n"
        "MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk\n"
        "ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF\n"
        "eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow\n"
        "gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO\n"
        "BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD\n"
        "VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq\n"
        "hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw\n"
        "AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6\n"
        "2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr\n"
        "ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt\n"
        "4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq\n"
        "m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/\n"
        "vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT\n"
        "8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE\n"
        "IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO\n"
        "KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO\n"
        "GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/\n"
        "s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g\n"
        "JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD\n"
        "AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9\n"
        "MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy\n"
        "bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6\n"
        "Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ\n"
        "zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj\n"
        "Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY\n"
        "Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5\n"
        "B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx\n"
        "PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR\n"
        "pu/xO28QOG8=\n"
        "-----END CERTIFICATE-----\n"
        "Bag Attributes\n"
        " 1.3.6.1.4.1.311.17.3.92: 00 08 00 00\n"
        " 1.3.6.1.4.1.311.17.3.20: 90 AF 6A 3A 94 5A 0B D8 90 EA 12 56 73 DF 43 B4 3A 28 DA E7\n"
        " 1.3.6.1.4.1.311.17.3.75: 35 00 30 00 38 00 30 00 44 00 43 00 37 00 41 00 36 00 35 00 44 00 42 00 36 00 41 00 35 00 39 00 36 00 30 00 45 00 43 00 44 00 38 00 37 00 34 00 30 00 38 00 38 00 46 00 33 00 33 00 32 00 38 00 5F 00 00 00\n"
        "subject=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Domain Validation Secure Server CA\n"
        "issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority\n"
        "-----BEGIN CERTIFICATE-----\n"
        "MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB\n"
        "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
        "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n"
        "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy\n"
        "MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n"
        "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n"
        "Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh\n"
        "bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP\n"
        "ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh\n"
        "bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0\n"
        "Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6\n"
        "ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51\n"
        "UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n\n"
        "c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY\n"
        "MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz\n"
        "30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV\n"
        "HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG\n"
        "BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv\n"
        "bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB\n"
        "AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E\n"
        "T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v\n"
        "ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p\n"
        "mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/\n"
        "e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps\n"
        "P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY\n"
        "dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc\n"
        "2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG\n"
        "V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4\n"
        "HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX\n"
        "j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII\n"
        "0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap\n"
        "lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf\n"
        "+AZxAeKCINT+b72x\n"
        "-----END CERTIFICATE-----\n";
    argv[argc++] = httpsCert;
*/
//    char logFlags[] = "--log-flags=0xffffffffffffffff";
//    argv[argc++] = logFlags;
//    char noPlainHttp[] = "--no-plain-http";
//    argv[argc++] = noPlainHttp;

    IAppPluginMain::main(iomux, the_main);

/*
    free(httpsCert);
*/
    if(pbxServerArg) free(pbxServerArg);
    delete the_main;
    delete iomux;

    onResetNotify();
    struct_pollfd_table[0].fd = signal_pipefd[0];
    struct_pollfd_table[0].events = POLLIN;
    struct_pollfd_table[0].revents = 0;
	while(!((ios_event_in_doorbell_req ^ ios_event_in_doorbell_ack) & IN_DOORBELL_QUIT)) {
        poll_result = poll(struct_pollfd_table, sizeof(struct_pollfd_table) / sizeof(struct_pollfd_table[0]), 1000);
    }
    ios_event_in_doorbell_ack ^= IN_DOORBELL_QUIT;

    my_exit();

    ios_event_out_doorbell_req ^= OUT_DOORBELL_EXIT;
    return NULL;
}

extern "C" void main_signal()
{
    byte buf[1] = {0};

    if (signal_pipefd[1] != -1) {
        write(signal_pipefd[1], buf, sizeof(buf));
    }
}

const char *default_tunes = 0;
const char *default_tones = 0;

extern "C" void start_sys(void)
{
    pthread_attr_t attr;
    long flags;
    unsigned int i;
    int ret;

    if(!signal_pipe_created) {
        if(pipe(signal_pipefd) < 0) {
       		ns_log("Main cannot create signal pipe");
        }
        else {
            signal_pipe_created = true;
            for(i = 0; i < sizeof(signal_pipefd) / sizeof(signal_pipefd[0]); i++) {
                flags = fcntl(signal_pipefd[i], F_GETFL);
                if(flags == -1) {
            		ns_log("Main cannot fcntl(F_GETFL)");
                    flags = 0;
                }
                flags |= O_NONBLOCK;
                if(fcntl(signal_pipefd[i], F_SETFL, flags) == -1) {
            		ns_log("Main cannot fcntl(F_SETFL)");
                }
            }
        }
    }
    if(!wait_pipe_created) {
        if(pipe(wait_pipefd) < 0) {
       		ns_log("Main cannot create wait pipe");
        }
        else {
            wait_pipe_created = true;
            for(i = 0; i < sizeof(wait_pipefd) / sizeof(wait_pipefd[0]); i++) {
                flags = fcntl(wait_pipefd[i], F_GETFL);
                if(flags == -1) {
            		ns_log("Main cannot fcntl(F_GETFL)");
                    flags = 0;
                }
                flags |= O_NONBLOCK;
                if(fcntl(wait_pipefd[i], F_SETFL, flags) == -1) {
            		ns_log("Main cannot fcntl(F_SETFL)");
                }
            }
            wait_pipe_pollfd.fd = wait_pipefd[0];
            wait_pipe_pollfd.events = POLLIN;
            wait_pipe_pollfd.revents = 0;
        }
    }
    if (!main_thread_created){
        size_t stacksize = 0;

        pthread_attr_init(&attr);
        pthread_attr_getstacksize(&attr, &stacksize);
        if (stacksize < 1024 * 1024) {
            pthread_attr_setstacksize(&attr, 1024 * 1024);
        }
#if !TARGET_OS_IPHONE
        ret = pthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INITIATED, 0);
        if (ret != 0) {
            ns_log("Set QOS class failed: %s", strerror(ret));
        }
#endif
        ret = pthread_create(&main_thread_id, &attr, (void*(*)(void*))main_thread, NULL);
        if (ret == 0){
            main_thread_created = true;
        }
        else {
            ns_log("Main thread create failed: %s", strerror(ret));
        }
        pthread_attr_destroy(&attr);
    }
}

extern "C" void do_shutdown()
{
    unsigned int i;

    if(main_thread_created) {
        ns_log("Client iOS shutdown...");
        ios_event_in_doorbell_req ^= IN_DOORBELL_QUIT;
        main_signal();
        i = 0;
        while((i < 100)
            && !((ios_event_out_doorbell_req ^ ios_event_out_doorbell_ack) & OUT_DOORBELL_EXIT)) {
            i++;
            ios_main_sleep(20);
        }
        if(!((ios_event_out_doorbell_req ^ ios_event_out_doorbell_ack) & OUT_DOORBELL_EXIT)) {
            pthread_kill(main_thread_id, IOS_MAIN_SIGNAL);
            ios_main_sleep(20);
        }
        ios_event_out_doorbell_ack ^= OUT_DOORBELL_EXIT;
        main_thread_created = false;

        ns_log("Client iOS shutdown done");
    }
    if(signal_pipe_created) {
        for(i = 0; i < sizeof(signal_pipefd) / sizeof(signal_pipefd[0]); i++) {
            if (signal_pipefd[i] != -1) {
                close(signal_pipefd[i]);
                signal_pipefd[i] = -1;
            }
        }
        signal_pipe_created = false;
    }
    if(wait_pipe_created) {
        for(i = 0; i < sizeof(wait_pipefd) / sizeof(wait_pipefd[0]); i++) {
            close(wait_pipefd[i]);
            wait_pipefd[i] = -1;
        }
        wait_pipe_created = false;
    }
}

extern "C" void connectivity_changed(bool on, bool in_wlan)
{
    connectivity_on = on;
    connectivity_lan = in_wlan;
    ios_event_in_doorbell_req = (ios_event_in_doorbell_req & ~IN_DOORBELL_CONNECTIVITY_MASK) |
        ((ios_event_in_doorbell_req + IN_DOORBELL_CONNECTIVITY_LSB) & IN_DOORBELL_CONNECTIVITY_MASK);
    main_signal();
}

extern "C" void restart_listen_sockets()
{
    ios_event_in_doorbell_req ^= IN_DOORBELL_RESTART_LISTEN;
    main_signal();
}

extern "C" void switch_audio_unit(bool on)
{
    audio_unit_on = on;
    ios_event_in_doorbell_req ^= IN_DOORBELL_SWITCH_AUDIO_UNIT;
    main_signal();
}

extern "C" void request_restart_dsp()
{
    ios_event_in_doorbell_req = (ios_event_in_doorbell_req & ~IN_DOORBELL_RESTART_DSP_MASK) |
        ((ios_event_in_doorbell_req + IN_DOORBELL_RESTART_DSP_LSB) & IN_DOORBELL_RESTART_DSP_MASK);
    main_signal();
}

extern "C" void set_manually_started(bool started)
{
    manually_started = started;
    ios_event_in_doorbell_req ^= IN_DOORBELL_MANUALLY_STARTED;
    main_signal();
}

/*----------------------------------------------------------------------------*/
