/******************************************************************************
 *
 *    Name: UIDialogLibCall.c
 *
 *    $Id$
 *
 *    Purpose: Generic means of loading any UI Dialog Library implementation.
 *
 *    Copyright 2013-2017 Citrix Systems, Inc. All rights reserved.
 *
 *****************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

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

#include "UIDialogLibCall.h"

struct UIDialogCallTable dialogFunctions;


/* The V1 UIDialogLib did not have a UIDialogVersion function.
 * Use this function for compatibility. */
void UIDialogVersion(unsigned int *major, unsigned int *minor)
{
    *major = 1;
    *minor = 0;
}


/* Macro to check if dlerror() returns an error string.
 * If it does the string is output, the library is closed and -1 returned.
 * If dlclose() also generates an error this is also output.
 * This uses variables defined in UIDialogLibLoad().
 */
#define CHECK_DLERROR()         \
    errString = dlerror();      \
    if (errString != NULL)      \
    {                           \
        fprintf(stderr, "%s\n", errString);     \
        if (dlclose(lib) && ((errString = dlerror()) != NULL)) \
            fprintf(stderr, "%s\n", errString);     \
        return -1;              \
    }

/* Load the UIDialogLib.so and setup all the function pointers. 
 * Returns -1 on error, 1 on success and 0 if it was already loaded.
 */

int UIDialogLibLoad(const char *icaroot)
{
    void *lib;
    int libStringLen;
    char *errString;

    // Protect against reloading the library if this function is called again.
    if (dialogFunctions.UIDialogAllocate != NULL)
    {
        return 0;
    }

    const char *libSuffixWebKit = "/lib/UIDialogLibWebKit.so";
    const char *libSuffix = "/lib/UIDialogLib.so";
    
    /* Create a UIDialogLibWebKit.so explicit path string. */
    libStringLen = strlen(icaroot) + strlen(libSuffixWebKit) + 1;

    {
        /* Creating a buffer capable to accommodate the longest of the library names. */
        char libString[libStringLen];
        snprintf(libString, libStringLen, "%s%s", icaroot, libSuffixWebKit);

        /* Try to open the UIDialogLibWebKit.so library. */
        lib = dlopen(libString, RTLD_LAZY);
        if (lib == NULL)
        {
            /* Try to open the UIDialogLib.so library. */
            libStringLen = strlen(icaroot) + strlen(libSuffix) + 1;
            snprintf(libString, libStringLen, "%s%s", icaroot, libSuffix);
            lib = dlopen(libString, RTLD_LAZY);
        }
    }

    if (lib == NULL)
    {
        fprintf(stderr, "Failed to load UIDialogLib:\n%s\n", dlerror());
        return -1;
    }
    CHECK_DLERROR();

    /* Access all of the UIDialogLib.so function pointers. */

    dialogFunctions.UIDialogAllocate = (UIDIALOG* (*)(void)) dlsym(lib, "UIDialogAllocate");
    CHECK_DLERROR();

    dialogFunctions.UIDialogInit = (void (*)(const char*)) dlsym(lib, "UIDialogInit");
    CHECK_DLERROR();

    dialogFunctions.UIDialogLoopInit = (void (*)(int*, char***)) dlsym(lib, "UIDialogLoopInit");
    CHECK_DLERROR();

    dialogFunctions.UIDialogDisplay = (const char* (*)(UIDIALOG*, struct formsElement**,
                                       enum dialogStyle, UIDIALOG*, const char*,
                                       const char*, unsigned int, const char*)) dlsym(lib, "UIDialogDisplay");
    CHECK_DLERROR();

    dialogFunctions.UIDialogDisplayWMClass = (const char* (*)(UIDIALOG*, struct formsElement**,
                                              enum dialogStyle, UIDIALOG*, const char*,
                                              const char*, unsigned int, const char*,
                                              const char*, const char*)) dlsym(lib, "UIDialogDisplayWMClass");
    CHECK_DLERROR();

    dialogFunctions.UIDialogDisplayVarg = (const char* (*)(UIDIALOG*, struct formsElement**,
                                           const char*, const char*, ...)) dlsym(lib, "UIDialogDisplayVarg");
    CHECK_DLERROR();

    dialogFunctions.UIDialogClose = (void (*)(UIDIALOG*)) dlsym(lib, "UIDialogClose");
    CHECK_DLERROR();

    dialogFunctions.UIDialogDeInit = (void (*)(void)) dlsym(lib, "UIDialogDeInit");
    CHECK_DLERROR();

    dialogFunctions.UIDialogMnemonicAtChar = (char* (*)(char *, const char)) dlsym(lib, "UIDialogMnemonicAtChar");
    CHECK_DLERROR();

    dialogFunctions.UIDialogVersion = (void (*)(unsigned int*, unsigned int*)) dlsym(lib, "UIDialogVersion");

    errString = dlerror();

    if (errString != NULL)
    {
        /* We are running with the V1 version of the UIDialogLib before UIDialogVersion() was added. */
        dialogFunctions.UIDialogVersion = UIDialogVersion;
    }

    /* Get the UIDialogLib version before loading any version-dependent functions. */
    unsigned int major = 0;
    unsigned int minor = 0;
    dialogFunctions.UIDialogVersion(&major, &minor);

    /* The following functions should be loaded if the UIDialogLib version is equal to or greater than 2.1. */
    dialogFunctions.UIDialogDisplayWebview = NULL;
    dialogFunctions.UIDialogClearWebviewCookies = NULL;
    if (major > 2
            || (major == 2 && minor >= 1))
    {
        dialogFunctions.UIDialogDisplayWebview = (const char** (*)(const char*, const char*,
                                                                   const char *, UIDIALOG *)) dlsym(lib, "UIDialogDisplayWebview");
        CHECK_DLERROR();

        dialogFunctions.UIDialogClearWebviewCookies = (void (*)(void)) dlsym(lib, "UIDialogClearWebviewCookies");
        CHECK_DLERROR();
    }

    /* Having loaded the library and set up the function pointers we always 
     * need to initialise. Do it now.
     */
    UI_DIALOG_INIT(icaroot);
    return 1;
}

#ifdef __cplusplus
} /* End extern "C". */
#endif

