text:
#include "qpid/dispatch/connection_manager.h"

#include "connection_manager_private.h"
#include "dispatch_private.h"
#include "entity.h"
#include "server_private.h"

#include "qpid/dispatch/ctools.h"
#include "qpid/dispatch/failoverlist.h"
#include "qpid/dispatch/threading.h"

#include <proton/listener.h>

#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

struct qd_config_ssl_profile_t {
    DEQ_LINKS(qd_config_ssl_profile_t);
    char        *name;
    char        *ssl_password;
    char        *ssl_trusted_certificate_db;
    char        *ssl_uid_format;
    char        *uid_name_mapping_file;
    char        *ssl_certificate_file;
    char        *ssl_private_key_file;
    char        *ssl_ciphers;
    char        *ssl_protocols;
};

DEQ_DECLARE(qd_config_ssl_profile_t, qd_config_ssl_profile_list_t);

struct qd_config_sasl_plugin_t {
    DEQ_LINKS(qd_config_sasl_plugin_t);
    char        *name;
    char        *auth_service;
    char        *sasl_init_hostname;
    char        *auth_ssl_profile;
};

DEQ_DECLARE(qd_config_sasl_plugin_t, qd_config_sasl_plugin_list_t);

struct qd_connection_manager_t {
    qd_log_source_t              *log_source;
    qd_server_t                  *server;
    qd_listener_list_t            listeners;
    qd_connector_list_t           connectors;
    qd_config_ssl_profile_list_t  config_ssl_profiles;
    qd_config_sasl_plugin_list_t  config_sasl_plugins;
};

const char *qd_log_message_components[] =
    {"message-id",
     "user-id",
     "to",
     "subject",
     "reply-to",
     "correlation-id",
     "content-type",
     "content-encoding",
     "absolute-expiry-time",
     "creation-time",
     "group-id",
     "group-sequence",
     "reply-to-group-id",
     "app-properties",
     0};

const char *ALL = "all";
const char *NONE = "none";

/**
 * Search the list of config_ssl_profiles for an ssl-profile that matches the passed in name
 */
static qd_config_ssl_profile_t *qd_find_ssl_profile(qd_connection_manager_t *cm, char *name)
{
    qd_config_ssl_profile_t *ssl_profile = DEQ_HEAD(cm->config_ssl_profiles);
    while (ssl_profile) {
        if (strcmp(ssl_profile->name, name) == 0)
            return ssl_profile;
        ssl_profile = DEQ_NEXT(ssl_profile);
    }

    return 0;
}

/**
 * Read the file from the password_file location on the file system and populate password_field with the
 * contents of the file.
 */
static void qd_set_password_from_file(const char *password_file, char **password_field, qd_log_source_t *log_source)
{
    if (password_file) {
        FILE *file = fopen(password_file, "r");

        if (file == NULL) {
            //
            // The global variable errno (found in <errno.h>) contains information about what went wrong; you can use perror() to print that information as a readable string
            //
            qd_log(log_source, QD_LOG_ERROR, "Unable to open password file %s, error: %s", password_file, strerror(errno));
            return;
        }

        char buffer[200];

        int c;
        int i=0;

        while (i < 200 - 1) {
            c = fgetc(file);
            if (c == EOF || c == '\n')
                break;
            buffer[i++] = c;
        }

        if (i != 0) {
            buffer[i] = '\0';
            free(*password_field);
            *password_field = strdup(buffer);
        }
        fclose(file);
    }
}

/**
 * Search the list of config_sasl_plugins for an sasl-profile that matches the passed in name
 */
static qd_config_sasl_plugin_t *qd_find_sasl_plugin(qd_connection_manager_t *cm, char *name)
{
    qd_config_sasl_plugin_t *sasl_plugin = DEQ_HEAD(cm->config_sasl_plugins);
    while (sasl_plugin) {
        if (strcmp(sasl_plugin->name, name) == 0)
            return sasl_plugin;
        sasl_plugin = DEQ_NEXT(sasl_plugin);
    }

    return 0;
}

void qd_server_config_free(qd_server_config_t *cf)
{
    if (!cf) return;
    free(cf->host);
    free(cf->port);
    free(cf->host_port);
    free(cf->role);
    if (cf->http_root_dir)   free(cf->http_root_dir);
    if (cf->name)            free(cf->name);
    if (cf->protocol_family) free(cf->protocol_family);
    if (cf->sasl_username)   free(cf->sasl_username);
    if (cf->sasl_password)   free(cf->sasl_password);
    if (cf->sasl_mechanisms) free(cf->sasl_mechanisms);
    if (cf->ssl_profile)     free(cf->ssl_profile);
    if (cf->failover_list)   qd_failover_list_free(cf->failover_list);
    if (cf->log_message)     free(cf->log_message);

    if (cf->ssl_certificate_file)       free(cf->ssl_certificate_file);
    if (cf->ssl_private_key_file)       free(cf->ssl_private_key_file);
    if (cf->ssl_ciphers)                free(cf->ssl_ciphers);
    if (cf->ssl_protocols)              free(cf->ssl_protocols);
    if (cf->ssl_password)               free(cf->ssl_password);
    if (cf->ssl_trusted_certificate_db) free(cf->ssl_trusted_certificate_db);
    if (cf->ssl_uid_format)             free(cf->ssl_uid_format);
    if (cf->ssl_uid_name_mapping_file)  free(cf->ssl_uid_name_mapping_file);

    if (cf->sasl_plugin_config.auth_service)               free(cf->sasl_plugin_config.auth_service);
    if (cf->sasl_plugin_config.sasl_init_hostname)         free(cf->sasl_plugin_config.sasl_init_hostname);
    if (cf->sasl_plugin_config.ssl_certificate_file)       free(cf->sasl_plugin_config.ssl_certificate_file);
    if (cf->sasl_plugin_config.ssl_private_key_file)       free(cf->sasl_plugin_config.ssl_private_key_file);
    if (cf->sasl_plugin_config.ssl_ciphers)                free(cf->sasl_plugin_config.ssl_ciphers);
    if (cf->sasl_plugin_config.ssl_protocols)              free(cf->sasl_plugin_config.ssl_protocols);
    if (cf->sasl_plugin_config.ssl_password)               free(cf->sasl_plugin_config.ssl_password);
    if (cf->sasl_plugin_config.ssl_trusted_certificate_db) free(cf->sasl_plugin_config.ssl_trusted_certificate_db);
    if (cf->sasl_plugin_config.ssl_uid_format)             free(cf->sasl_plugin_config.ssl_uid_format);
    if (cf->sasl_plugin_config.ssl_uid_name_mapping_file)  free(cf->sasl_plugin_config.ssl_uid_name_mapping_file);

    memset(cf, 0, sizeof(*cf));
}

#define CHECK() if (qd_error_code()) goto error
#define SSTRDUP(S) ((S) ? strdup(S) : NULL)

/**
 * Private function to set the values of booleans strip_inbound_annotations and strip_outbound_annotations
 * based on the corresponding values for the settings in qdrouter.json
 * strip_inbound_annotations and strip_outbound_annotations are defaulted to true
 */
static void load_strip_annotations(qd_server_config_t *config, const char* stripAnnotations)
{
    if (stripAnnotations) {
    	if      (strcmp(stripAnnotations, "both") == 0) {
    		config->strip_inbound_annotations  = true;
    		config->strip_outbound_annotations = true;
    	}
    	else if (strcmp(stripAnnotations, "in") == 0) {
    		config->strip_inbound_annotations  = true;
    		config->strip_outbound_annotations = false;
    	}
    	else if (strcmp(stripAnnotations, "out") == 0) {
    		config->strip_inbound_annotations  = false;
    		config->strip_outbound_annotations = true;
    	}
    	else if (strcmp(stripAnnotations, "no") == 0) {
    		config->strip_inbound_annotations  = false;
    		config->strip_outbound_annotations = false;
    	}
    }
    else {
    	assert(stripAnnotations);
    	//This is just for safety. Default to stripInboundAnnotations and stripOutboundAnnotations to true (to "both").
		config->strip_inbound_annotations  = true;
		config->strip_outbound_annotations = true;
    }
}

/**
 * Since both the host and the addr have defaults of 127.0.0.1, we will have to use the non-default wherever it is available.
 */
static void set_config_host(qd_server_config_t *config, qd_entity_t* entity)
{
    config->host = qd_entity_opt_string(entity, "host", 0);

    assert(config->host);

    int hplen = strlen(config->host) + strlen(config->port) + 2;
    config->host_port = malloc(hplen);
    snprintf(config->host_port, hplen, "%s:%s", config->host, config->port);
}

static void qd_config_process_password(char **actual_val, char *pw, bool *is_file, bool allow_literal_prefix, qd_log_source_t *log_source)
{
    if (!pw)
        return;

    //
    // If the "password" starts with "env:" then the remaining
    // text is the environment variable that contains the password
    //
    if (strncmp(pw, "env:", 4) == 0) {
        char *env = pw + 4;
        // skip the leading whitespace if it is there
        while (*env == ' ') ++env;

        const char* passwd = getenv(env);
        if (passwd) {
            //
            // Replace the allocated directive with the looked-up password
            //
            *actual_val = strdup(passwd);
        } else {
            qd_error(QD_ERROR_NOT_FOUND, "Failed to find a password in the environment variable");
        }
    }

    //
    // If the "password" starts with "literal:" or "pass:" then
    // the remaining text is the password and the heading should be
    // stripped off
    //
    else if ( (strncmp(pw, "literal:", 8) == 0 && allow_literal_prefix) || strncmp(pw, "pass:", 5) == 0) {
        qd_log(log_source, QD_LOG_WARNING, "It is unsafe to provide plain text passwords in the config file");

        if (strncmp(pw, "l", 1) == 0) {
            // skip the "literal:" header
            pw += 8;
        }
        else {
            // skip the "pass:" header
            pw += 5;
        }

        //
        // Replace the password with a copy of the string after "literal: or "pass:"
        //
        char *copy = strdup(pw);
        *actual_val = copy;
    }
    //
    // If the password starts with a file: literal set the is_file to true.
    //
    else if (strncmp(pw, "file:", 5) == 0) {
        pw += 5;

        // Replace the password with a copy of the string after "file:"
        char *copy = strdup(pw);
        *actual_val = copy;
        *is_file = true;
    }
    else {
        //
        // THe password field does not have any prefixes. Use it as plain text
        //
        qd_log(log_source, QD_LOG_WARNING, "It is unsafe to provide plain text passwords in the config file");

    }
}

static qd_log_bits populate_log_message(const qd_server_config_t *config)
{
    //May have to copy this string since strtok modifies original string.
    char *log_message = config->log_message;

    int32_t ret_val = 0;

    if (!log_message || strcmp(log_message, NONE) == 0)
        return ret_val;

    //If log_message is set to 'all', turn on all bits.
    if (strcmp(log_message, ALL) == 0)
        return INT32_MAX;

    char *delim = ",";

    /* get the first token */
    char *token = strtok(log_message, delim);

    const char *component = 0;

    /* walk through other tokens */
    while( token != NULL ) {
       for (int i=0;; i++) {
           component = qd_log_message_components[i];
           if (component == 0)
               break;

           if (strcmp(component, token) == 0) {
                   ret_val |= 1 << i;
           }
       }
       token = strtok(NULL, delim);
    }

    return ret_val;
}

static qd_error_t load_server_config(qd_dispatch_t *qd, qd_server_config_t *config, qd_entity_t* entity, bool is_listener)
{
    qd_error_clear();

    bool authenticatePeer   = qd_entity_opt_bool(entity, "authenticatePeer",  false);    CHECK();
    bool verifyHostName     = qd_entity_opt_bool(entity, "verifyHostname",    true);     CHECK();
    bool requireEncryption  = qd_entity_opt_bool(entity, "requireEncryption", false);    CHECK();
    bool requireSsl         = qd_entity_opt_bool(entity, "requireSsl",        false);    CHECK();

    ZERO(config);
    config->log_message          = qd_entity_opt_string(entity, "messageLoggingComponents", 0);     CHECK();
    config->log_bits             = populate_log_message(config);
    config->port                 = qd_entity_get_string(entity, "port");              CHECK();
    config->name                 = qd_entity_opt_string(entity, "name", 0);           CHECK();
    config->role                 = qd_entity_get_string(entity, "role");              CHECK();
    config->inter_router_cost    = qd_entity_opt_long(entity, "cost", 1);             CHECK();
    config->protocol_family      = qd_entity_opt_string(entity, "protocolFamily", 0); CHECK();
    config->healthz              = qd_entity_opt_bool(entity, "healthz", true);       CHECK();
    config->metrics              = qd_entity_opt_bool(entity, "metrics", true);       CHECK();
    config->websockets           = qd_entity_opt_bool(entity, "websockets", true);    CHECK();
    config->http                 = qd_entity_opt_bool(entity, "http", false);         CHECK();
    config->http_root_dir        = qd_entity_opt_string(entity, "httpRootDir", false);   CHECK();
    config->http = config->http || config->http_root_dir; /* httpRoot implies http */
    config->max_frame_size       = qd_entity_get_long(entity, "maxFrameSize");        CHECK();
    config->max_sessions         = qd_entity_get_long(entity, "maxSessions");         CHECK();
    uint64_t ssn_frames          = qd_entity_opt_long(entity, "maxSessionFrames", 0); CHECK();
    config->idle_timeout_seconds = qd_entity_get_long(entity, "idleTimeoutSeconds");  CHECK();
    if (is_listener) {
        config->initial_handshake_timeout_seconds = qd_entity_get_long(entity, "initialHandshakeTimeoutSeconds");  CHECK();
    }
    config->sasl_username        = qd_entity_opt_string(entity, "saslUsername", 0);   CHECK();
    config->sasl_password        = qd_entity_opt_string(entity, "saslPassword", 0);   CHECK();
    config->sasl_mechanisms      = qd_entity_opt_string(entity, "saslMechanisms", 0); CHECK();
    config->ssl_profile          = qd_entity_opt_string(entity, "sslProfile", 0);     CHECK();
    config->sasl_plugin          = qd_entity_opt_string(entity, "saslPlugin", 0);     CHECK();
    config->link_capacity        = qd_entity_opt_long(entity, "linkCapacity", 0);     CHECK();
    config->multi_tenant         = qd_entity_opt_bool(entity, "multiTenant", false);  CHECK();
    config->policy_vhost         = qd_entity_opt_string(entity, "policyVhost", 0);    CHECK();
    config->conn_props           = qd_entity_opt_map(entity, "openProperties");       CHECK();

    char *unused                 = qd_entity_opt_string(entity, "trustedCertsFile", 0);
    if (unused) {
        qd_log(qd->connection_manager->log_source, QD_LOG_WARNING,
               "Configuration listener attribute 'trustedCertsFile' is not used. Specify sslProfile caCertFile instead.");
        free(unused);
    }

    set_config_host(config, entity);

    if (config->sasl_password) {
        //
        //Process the sasl password field and set the right values based on prefixes.
        //
        char *actual_pass = 0;
        bool is_file_path = 0;
        qd_config_process_password(&actual_pass, config->sasl_password, &is_file_path, false, qd->connection_manager->log_source);
        if (actual_pass) {
            if (is_file_path) {
                qd_set_password_from_file(actual_pass, &config->sasl_password, qd->connection_manager->log_source);
                free(actual_pass);
            }
            else {
                free(config->sasl_password);
                config->sasl_password = actual_pass;
            }
        }
    }

    //
    // Handle the defaults for various settings
    //
    if (config->link_capacity == 0)
        config->link_capacity = 250;

    if (config->max_sessions == 0 || config->max_sessions > 32768)
        // Proton disallows > 32768
        config->max_sessions = 32768;

    if (config->max_frame_size < QD_AMQP_MIN_MAX_FRAME_SIZE)
        // Silently promote the minimum max-frame-size
        // Proton will do this but the number is needed for the
        // incoming capacity calculation.
        config->max_frame_size = QD_AMQP_MIN_MAX_FRAME_SIZE;

    //
    // Given session frame count and max frame size, compute session incoming_capacity
    //   On 64-bit systems the capacity has no practial limit.
    //   On 32-bit systems the largest default capacity is half the process address space.
    //
    bool is_64bit = sizeof(size_t) == 8;
#define MAX_32BIT_CAPACITY ((size_t)(2147483647))
    if (ssn_frames == 0) {
        config->incoming_capacity = is_64bit ? MAX_32BIT_CAPACITY * (size_t)config->max_frame_size : MAX_32BIT_CAPACITY;
    } else {
        // Limited incoming frames.
        if (is_64bit) {
            // Specify this to proton by setting capacity to be
            // the product (max_frame_size * ssn_frames).
            config->incoming_capacity = (size_t)config->max_frame_size * (size_t)ssn_frames;
        } else {
            // 32-bit systems have an upper bound to the capacity
            uint64_t max_32bit_capacity = (uint64_t)MAX_32BIT_CAPACITY;
            uint64_t capacity     = (uint64_t)config->max_frame_size * (uint64_t)ssn_frames;
            if (capacity <= max_32bit_capacity) {
                config->incoming_capacity = (size_t)capacity;
            } else {
                config->incoming_capacity = MAX_32BIT_CAPACITY;
                uint64_t actual_frames = max_32bit_capacity / (uint64_t)config->max_frame_size;

                qd_log(qd->connection_manager->log_source, QD_LOG_WARNING,
                    "Server configuation for I/O adapter entity name:'%s', host:'%s', port:'%s', "
                    "requested maxSessionFrames truncated from %"PRId64" to %"PRId64,
                    config->name, config->host, config->port, ssn_frames, actual_frames);
            }
        }
    }

    //
    // For now we are hardwiring this attribute to true.  If there's an outcry from the
    // user community, we can revisit this later.
    //
    config->allowInsecureAuthentication = true;
    config->verify_host_name = verifyHostName;

    char *stripAnnotations  = qd_entity_opt_string(entity, "stripAnnotations", 0);
    load_strip_annotations(config, stripAnnotations);
    free(stripAnnotations);
    stripAnnotations = 0;
    CHECK();

    config->requireAuthentication = authenticatePeer;
    config->requireEncryption     = requireEncryption || requireSsl;

    if (config->ssl_profile) {
        config->ssl_required = requireSsl;
        config->ssl_require_peer_authentication = config->sasl_mechanisms &&
            strstr(config->sasl_mechanisms, "EXTERNAL") != 0;

        qd_config_ssl_profile_t *ssl_profile =
            qd_find_ssl_profile(qd->connection_manager, config->ssl_profile);
        if (ssl_profile) {
            config->ssl_certificate_file = SSTRDUP(ssl_profile->ssl_certificate_file);
            config->ssl_private_key_file = SSTRDUP(ssl_profile->ssl_private_key_file);
            config->ssl_ciphers = SSTRDUP(ssl_profile->ssl_ciphers);
            config->ssl_protocols = SSTRDUP(ssl_profile->ssl_protocols);
            config->ssl_password = SSTRDUP(ssl_profile->ssl_password);
            config->ssl_trusted_certificate_db = SSTRDUP(ssl_profile->ssl_trusted_certificate_db);
            config->ssl_uid_format = SSTRDUP(ssl_profile->ssl_uid_format);
            config->ssl_uid_name_mapping_file = SSTRDUP(ssl_profile->uid_name_mapping_file);
        }
    }

    if (config->sasl_plugin) {
        qd_config_sasl_plugin_t *sasl_plugin =
            qd_find_sasl_plugin(qd->connection_manager, config->sasl_plugin);
        if (sasl_plugin) {
            config->sasl_plugin_config.auth_service = SSTRDUP(sasl_plugin->auth_service);
            config->sasl_plugin_config.sasl_init_hostname = SSTRDUP(sasl_plugin->sasl_init_hostname);
            qd_log(qd->connection_manager->log_source, QD_LOG_INFO, "Using auth service %s from  SASL Plugin %s", config->sasl_plugin_config.auth_service, config->sasl_plugin);

            if (sasl_plugin->auth_ssl_profile) {
                config->sasl_plugin_config.use_ssl = true;
                qd_config_ssl_profile_t *auth_ssl_profile =
                    qd_find_ssl_profile(qd->connection_manager, sasl_plugin->auth_ssl_profile);

                config->sasl_plugin_config.ssl_certificate_file = SSTRDUP(auth_ssl_profile->ssl_certificate_file);
                config->sasl_plugin_config.ssl_private_key_file = SSTRDUP(auth_ssl_profile->ssl_private_key_file);
                config->sasl_plugin_config.ssl_ciphers = SSTRDUP(auth_ssl_profile->ssl_ciphers);
                config->sasl_plugin_config.ssl_protocols = SSTRDUP(auth_ssl_profile->ssl_protocols);
                config->sasl_plugin_config.ssl_password = SSTRDUP(auth_ssl_profile->ssl_password);
                config->sasl_plugin_config.ssl_trusted_certificate_db = SSTRDUP(auth_ssl_profile->ssl_trusted_certificate_db);
                config->sasl_plugin_config.ssl_uid_format = SSTRDUP(auth_ssl_profile->ssl_uid_format);
                config->sasl_plugin_config.ssl_uid_name_mapping_file = SSTRDUP(auth_ssl_profile->uid_name_mapping_file);
            } else {
                config->sasl_plugin_config.use_ssl = false;
            }
        } else {
            qd_error(QD_ERROR_RUNTIME, "Cannot find sasl plugin %s", config->sasl_plugin); CHECK();
        }
    }

    return QD_ERROR_NONE;

  error:
    qd_server_config_free(config);
    return qd_error_code();
}

bool is_log_component_enabled(qd_log_bits log_message, const char *component_name) {

    for(int i=0;;i++) {
        const char *component = qd_log_message_components[i];
        if (component == 0)
            break;
        if (strcmp(component_name, component) == 0)
            return (log_message >> i) & 1;
    }

    return 0;
}

static bool config_ssl_profile_free(qd_connection_manager_t *cm, qd_config_ssl_profile_t *ssl_profile)
{
    DEQ_REMOVE(cm->config_ssl_profiles, ssl_profile);

    free(ssl_profile->name);
    free(ssl_profile->ssl_password);
    free(ssl_profile->ssl_trusted_certificate_db);
    free(ssl_profile->ssl_uid_format);
    free(ssl_profile->uid_name_mapping_file);
    free(ssl_profile->ssl_certificate_file);
    free(ssl_profile->ssl_private_key_file);
    free(ssl_profile->ssl_ciphers);
    free(ssl_profile->ssl_protocols);
    free(ssl_profile);
    return true;

}

static bool config_sasl_plugin_free(qd_connection_manager_t *cm, qd_config_sasl_plugin_t *sasl_plugin)
{
    DEQ_REMOVE(cm->config_sasl_plugins, sasl_plugin);

    free(sasl_plugin->name);
    free(sasl_plugin->auth_service);
    free(sasl_plugin->sasl_init_hostname);
    free(sasl_plugin->auth_ssl_profile);
    free(sasl_plugin);
    return true;

}

qd_config_ssl_profile_t *qd_dispatch_configure_ssl_profile(qd_dispatch_t *qd, qd_entity_t *entity)
{
    qd_error_clear();
    qd_connection_manager_t *cm = qd->connection_manager;

    qd_config_ssl_profile_t *ssl_profile = NEW(qd_config_ssl_profile_t);
    ZERO(ssl_profile);
    DEQ_ITEM_INIT(ssl_profile);
    DEQ_INSERT_TAIL(cm->config_ssl_profiles, ssl_profile);
    ssl_profile->name                       = qd_entity_opt_string(entity, "name", 0); CHECK();
    ssl_profile->ssl_certificate_file       = qd_entity_opt_string(entity, "certFile", 0); CHECK();
    ssl_profile->ssl_private_key_file       = qd_entity_opt_string(entity, "privateKeyFile", 0); CHECK();
    ssl_profile->ssl_password               = qd_entity_opt_string(entity, "password", 0); CHECK();
    char *password_file                     = qd_entity_opt_string(entity, "passwordFile", 0); CHECK();

    if (ssl_profile->ssl_password) {
        //
        // Process the password to handle any modifications or lookups needed
        //
        char *actual_pass = 0;
        bool is_file_path = 0;
        qd_config_process_password(&actual_pass, ssl_profile->ssl_password, &is_file_path, true, cm->log_source); CHECK();
        if (actual_pass) {
            if (is_file_path) {
                qd_set_password_from_file(actual_pass, &ssl_profile->ssl_password, cm->log_source);
                free(actual_pass);
            }
            else {
                free(ssl_profile->ssl_password);
                ssl_profile->ssl_password = actual_pass;
            }
        }
    }
    else if (password_file) {
        //
        // Warn the user that the passwordFile attribute has been deprecated.
        //
        qd_log(cm->log_source, QD_LOG_WARNING, "Attribute passwordFile of entity sslProfile has been deprecated. Use password field with the file: prefix instead.");
        qd_set_password_from_file(password_file, &ssl_profile->ssl_password, cm->log_source);
    }

    free(password_file);

    ssl_profile->ssl_ciphers   = qd_entity_opt_string(entity, "ciphers", 0);                   CHECK();
    ssl_profile->ssl_protocols = qd_entity_opt_string(entity, "protocols", 0);                 CHECK();
    ssl_profile->ssl_trusted_certificate_db = qd_entity_opt_string(entity, "caCertFile", 0);   CHECK();
    ssl_profile->ssl_uid_format             = qd_entity_opt_string(entity, "uidFormat", 0);          CHECK();
    ssl_profile->uid_name_mapping_file      = qd_entity_opt_string(entity, "uidNameMappingFile", 0); CHECK();

    qd_log(cm->log_source, QD_LOG_INFO, "Created SSL Profile with name %s ", ssl_profile->name);
    return ssl_profile;

    error:
        qd_log(cm->log_source, QD_LOG_ERROR, "Unable to create ssl profile: %s", qd_error_message());
        config_ssl_profile_free(cm, ssl_profile);
        return 0;
}

qd_config_sasl_plugin_t *qd_dispatch_configure_sasl_plugin(qd_dispatch_t *qd, qd_entity_t *entity)
{
    qd_error_clear();
    qd_connection_manager_t *cm = qd->connection_manager;

    qd_config_sasl_plugin_t *sasl_plugin = NEW(qd_config_sasl_plugin_t);
    ZERO(sasl_plugin);
    DEQ_ITEM_INIT(sasl_plugin);
    DEQ_INSERT_TAIL(cm->config_sasl_plugins, sasl_plugin);
    sasl_plugin->name                       = qd_entity_opt_string(entity, "name", 0); CHECK();

    char *auth_host = qd_entity_opt_string(entity, "host", 0);
    char *auth_port = qd_entity_opt_string(entity, "port", 0);

    if (auth_host && auth_port) {
        int strlen_auth_host = strlen(auth_host);
        int strlen_auth_port = strlen(auth_port);

        if (strlen_auth_host > 0 && strlen_auth_port > 0) {

            int hplen = strlen(auth_host) + strlen(auth_port) + 2;
            if (hplen > 2) {
                sasl_plugin->auth_service = malloc(hplen);
                snprintf(sasl_plugin->auth_service, hplen, "%s:%s", auth_host, auth_port);
            }
        }
    }

    free(auth_host);
    free(auth_port);

    if (!sasl_plugin->auth_service) {
        sasl_plugin->auth_service               = qd_entity_opt_string(entity, "authService", 0); CHECK();
        qd_log(cm->log_source, QD_LOG_WARNING, "Attribute authService of entity authServicePlugin has been deprecated. Use host and port instead.");
    }

    sasl_plugin->sasl_init_hostname         = qd_entity_opt_string(entity, "realm", 0); CHECK();
    sasl_plugin->auth_ssl_profile           = qd_entity_opt_string(entity, "sslProfile", 0); CHECK();

    qd_log(cm->log_source, QD_LOG_INFO, "Created SASL plugin config with name %s", sasl_plugin->name);
    return sasl_plugin;

    error:
        qd_log(cm->log_source, QD_LOG_ERROR, "Unable to create SASL plugin config: %s", qd_error_message());
        config_sasl_plugin_free(cm, sasl_plugin);
        return 0;
}

static void log_config(qd_log_source_t *log, qd_server_config_t *c, const char *what) {
    qd_log(log, QD_LOG_INFO, "Configured %s: %s proto=%s, role=%s%s%s%s",
           what, c->host_port, c->protocol_family ? c->protocol_family : "any",
           c->role,
           c->http ? ", http" : "",
           c->ssl_profile ? ", sslProfile=":"",
           c->ssl_profile ? c->ssl_profile:"");
}

qd_listener_t *qd_dispatch_configure_listener(qd_dispatch_t *qd, qd_entity_t *entity)
{
    qd_connection_manager_t *cm = qd->connection_manager;
    qd_listener_t *li = qd_server_listener(qd->server);
    if (!li || load_server_config(qd, &li->config, entity, true) != QD_ERROR_NONE) {
        qd_log(cm->log_source, QD_LOG_ERROR, "Unable to create listener: %s", qd_error_message());
        qd_listener_decref(li);
        return 0;
    }
    char *fol = qd_entity_opt_string(entity, "failoverUrls", 0);
    if (fol) {
        li->config.failover_list = qd_failover_list(fol);
        free(fol);
        if (li->config.failover_list == 0) {
            qd_log(cm->log_source, QD_LOG_ERROR, "Unable to create listener, bad failover list: %s",
                   qd_error_message());
            qd_listener_decref(li);
            return 0;
        }
    } else {
        li->config.failover_list = 0;
    }
    DEQ_ITEM_INIT(li);
    DEQ_INSERT_TAIL(cm->listeners, li);
    log_config(cm->log_source, &li->config, "Listener");
    return li;
}

qd_error_t qd_entity_refresh_listener(qd_entity_t* entity, void *impl)
{
    return QD_ERROR_NONE;
}

/**
 * Calculates the total length of the failover  list string.
 * For example, the failover list string can look like this - "amqp://0.0.0.0:62616, amqp://0.0.0.0:61616"
 * This function calculates the length of the above string by adding up the scheme (amqp or amqps) and host_port for each failover item.
 * It also assumes that there will be a comma and a space between each failover item.
 *
 */
static int get_failover_info_length(qd_failover_item_list_t   conn_info_list)
{
    int arr_length = 0;
    qd_failover_item_t *item = DEQ_HEAD(conn_info_list);

    while(item) {
        if (item->scheme) {
            // The +3 is for the '://'
            arr_length += strlen(item->scheme) + 3;
        }
        if (item->host_port) {
            arr_length += strlen(item->host_port);
        }
        item = DEQ_NEXT(item);
        if (item) {
            // This is for the comma and space between the items
            arr_length += 2;
        }
    }

    if (arr_length > 0)
        // This is for the final '\0'
        arr_length += 1;

    return arr_length;
}

/**
 *
 * Creates a failover url list. This comma separated failover list shows a list of urls that the router will attempt
 * to connect to in case the primary connection fails. The router will attempt these failover connections to urls in
 * the order that they appear in the list.
 *
 */
qd_error_t qd_entity_refresh_connector(qd_entity_t* entity, void *impl)
{
    qd_connector_t *connector = (qd_connector_t*) impl;

    int conn_index = connector->conn_index;

    int i = 1;
    int num_items = 0;

    sys_mutex_lock(connector->lock);

    qd_failover_item_list_t   conn_info_list = connector->conn_info_list;

    int conn_info_len = DEQ_SIZE(conn_info_list);

    qd_failover_item_t *item = DEQ_HEAD(conn_info_list);

    int arr_length = get_failover_info_length(conn_info_list);

    // This is the string that will contain the comma separated failover list
    char *failover_info = qd_calloc(arr_length + 1, sizeof(char));
    while(item) {

        // Break out of the loop when we have hit all items in the list.
        if (num_items >= conn_info_len)
            break;

        if (num_items >= 1) {
            strcat(failover_info, ", ");
        }

        // We need to go to the elements in the list to get to the
        // element that matches the connection index. This is the first
        // url that the router will try to connect on failover.
        if (conn_index == i) {
            num_items += 1;
            if (item->scheme) {
                strcat(failover_info, item->scheme);
                strcat(failover_info, "://");
            }
            if (item->host_port) {
                strcat(failover_info, item->host_port);
            }
        }
        else {
            if (num_items > 0) {
                num_items += 1;
                if (item->scheme) {
                    strcat(failover_info, item->scheme);
                    strcat(failover_info, "://");
                }
                if (item->host_port) {
                    strcat(failover_info, item->host_port);
                }
            }
        }

        i += 1;

        item = DEQ_NEXT(item);
        if (item == 0)
            item = DEQ_HEAD(conn_info_list);
    }

    const char *state_info = 0;
    switch (connector->state) {
    case CXTR_STATE_CONNECTING:
      state_info = "CONNECTING";
      break;
    case CXTR_STATE_OPEN:
      state_info = "SUCCESS";
      break;
    case CXTR_STATE_FAILED:
      state_info = "FAILED";
      break;
    case CXTR_STATE_INIT:
      state_info = "INITIALIZING";
      break;
    case CXTR_STATE_DELETED:
      // deleted by management, waiting for connection to close
      state_info = "CLOSING";
      break;
    default:
      state_info = "UNKNOWN";
      break;
    }

    if (qd_entity_set_string(entity, "failoverUrls", failover_info) == 0
        && qd_entity_set_string(entity, "connectionStatus", state_info) == 0
        && qd_entity_set_string(entity, "connectionMsg", connector->conn_msg) == 0) {

        sys_mutex_unlock(connector->lock);
        free(failover_info);
        return QD_ERROR_NONE;
    }

    sys_mutex_unlock(connector->lock);
    free(failover_info);
    return qd_error_code();
}

qd_connector_t *qd_dispatch_configure_connector(qd_dispatch_t *qd, qd_entity_t *entity)
{
    qd_connection_manager_t *cm = qd->connection_manager;
    qd_connector_t *ct = qd_server_connector(qd->server);

    qd_error_clear();

    if (ct && load_server_config(qd, &ct->config, entity, false) == QD_ERROR_NONE) {
        ct->policy_vhost = qd_entity_opt_string(entity, "policyVhost", 0); CHECK();
        DEQ_ITEM_INIT(ct);
        DEQ_INSERT_TAIL(cm->connectors, ct);
        log_config(cm->log_source, &ct->config, "Connector");

        //
        // Add the first item to the ct->conn_info_list
        // The initial connection information and any backup connection information is stored in the conn_info_list
        //
        qd_failover_item_t *item = NEW(qd_failover_item_t);
        ZERO(item);
        if (ct->config.ssl_required)
            item->scheme   = strdup("amqps");
        else
            item->scheme   = strdup("amqp");

        item->host     = strdup(ct->config.host);
        item->port     = strdup(ct->config.port);

        int hplen = strlen(item->host) + strlen(item->port) + 2;
        item->host_port = malloc(hplen);
        snprintf(item->host_port, hplen, "%s:%s", item->host , item->port);

        DEQ_INSERT_TAIL(ct->conn_info_list, item);

        return ct;
    }

  error:
    qd_log(cm->log_source, QD_LOG_ERROR, "Unable to create connector: %s", qd_error_message());
    qd_connector_decref(ct);
    return 0;
}

qd_connection_manager_t *qd_connection_manager(qd_dispatch_t *qd)
{
    qd_connection_manager_t *cm = NEW(qd_connection_manager_t);
    if (!cm)
        return 0;

    cm->log_source = qd_log_source("CONN_MGR");
    cm->server     = qd->server;
    DEQ_INIT(cm->listeners);
    DEQ_INIT(cm->connectors);
    DEQ_INIT(cm->config_ssl_profiles);
    DEQ_INIT(cm->config_sasl_plugins);

    return cm;
}

// Called on router shutdown
//
void qd_connection_manager_free(qd_connection_manager_t *cm)
{
    if (!cm) return;
    qd_listener_t *li = DEQ_HEAD(cm->listeners);
    while (li) {
        DEQ_REMOVE_HEAD(cm->listeners);
        if (li->pn_listener) {
            // DISPATCH-1508: force cleanup of pn_listener context.  This is
            // usually done in the PN_LISTENER_CLOSE event handler in server.c,
            // but since the router is going down those events will no longer
            // be generated.
            pn_listener_set_context(li->pn_listener, 0);
            pn_listener_close(li->pn_listener);
            li->pn_listener = 0;
            qd_listener_decref(li);  // for the pn_listener's context
        }
        qd_listener_decref(li);
        li = DEQ_HEAD(cm->listeners);
    }

    qd_connector_t *connector = DEQ_HEAD(cm->connectors);
    while (connector) {
        DEQ_REMOVE_HEAD(cm->connectors);
        sys_mutex_lock(connector->lock);
        // setting DELETED below ensures the timer callback
        // will not initiate a re-connect once we drop
        // the lock
        connector->state = CXTR_STATE_DELETED;
        sys_mutex_unlock(connector->lock);
        // cannot cancel timer while holding lock since the
        // callback takes the lock
        qd_timer_cancel(connector->timer);
        qd_connector_decref(connector);

        connector = DEQ_HEAD(cm->connectors);
    }

    qd_config_ssl_profile_t *sslp = DEQ_HEAD(cm->config_ssl_profiles);
    while (sslp) {
        config_ssl_profile_free(cm, sslp);
        sslp = DEQ_HEAD(cm->config_ssl_profiles);
    }

    qd_config_sasl_plugin_t *saslp = DEQ_HEAD(cm->config_sasl_plugins);
    while (saslp) {
        config_sasl_plugin_free(cm, saslp);
        saslp = DEQ_HEAD(cm->config_sasl_plugins);
    }

    free(cm);
}

/** NOTE: non-static qd_connection_manager_* functions are called from the python agent */

void qd_connection_manager_start(qd_dispatch_t *qd)
{
    static bool first_start = true;
    qd_listener_t  *li = DEQ_HEAD(qd->connection_manager->listeners);
    qd_connector_t *ct = DEQ_HEAD(qd->connection_manager->connectors);

    while (li) {
        if (!li->pn_listener) {
            if (!qd_listener_listen(li) && first_start) {
                qd_log(qd->connection_manager->log_source, QD_LOG_CRITICAL,
                       "Listen on %s failed during initial config", li->config.host_port);
                exit(1);
            } else {
                li->exit_on_error = first_start;
            }
        }
        li = DEQ_NEXT(li);
    }

    while (ct) {

        if (ct->state == CXTR_STATE_OPEN || ct->state == CXTR_STATE_CONNECTING) {
            ct = DEQ_NEXT(ct);
            continue;
        }

        qd_connector_connect(ct);
        ct = DEQ_NEXT(ct);
    }

    first_start = false;
}

void qd_connection_manager_delete_listener(qd_dispatch_t *qd, void *impl)
{
    qd_listener_t *li = (qd_listener_t*) impl;
    if (li) {
        if (li->pn_listener) {
            pn_listener_close(li->pn_listener);
        }
        else if (li->http) {
            qd_lws_listener_close(li->http);
        }
        DEQ_REMOVE(qd->connection_manager->listeners, li);
        qd_listener_decref(li);
    }
}

void qd_connection_manager_delete_ssl_profile(qd_dispatch_t *qd, void *impl)
{
    qd_config_ssl_profile_t *ssl_profile = (qd_config_ssl_profile_t*) impl;
    config_ssl_profile_free(qd->connection_manager, ssl_profile);
}

void qd_connection_manager_delete_sasl_plugin(qd_dispatch_t *qd, void *impl)
{
    qd_config_sasl_plugin_t *sasl_plugin = (qd_config_sasl_plugin_t*) impl;
    config_sasl_plugin_free(qd->connection_manager, sasl_plugin);
}

static void deferred_close(void *context, bool discard) {
    if (!discard) {
        pn_connection_close((pn_connection_t*)context);
    }
}

// threading: called by management thread while I/O thread may be
// referencing the qd_connector_t via the qd_connection_t
//
void qd_connection_manager_delete_connector(qd_dispatch_t *qd, void *impl)
{
    qd_connector_t *ct = (qd_connector_t*) impl;
    if (ct) {
        // cannot free the timer while holding ct->lock since the
        // timer callback may be running during the call to qd_timer_free
        qd_timer_t *timer = 0;
        void *dct = qd_connection_new_qd_deferred_call_t();
        sys_mutex_lock(ct->lock);
        timer = ct->timer;
        ct->timer = 0;
        ct->state = CXTR_STATE_DELETED;
        qd_connection_t *conn = ct->qd_conn;
        if (conn && conn->pn_conn) {
            qd_connection_invoke_deferred_impl(conn, deferred_close, conn->pn_conn, dct);
            sys_mutex_unlock(ct->lock);
        } else {
            sys_mutex_unlock(ct->lock);
            qd_connection_free_qd_deferred_call_t(dct);
        }
        qd_timer_free(timer);
        DEQ_REMOVE(qd->connection_manager->connectors, ct);
        qd_connector_decref(ct);
    }
}

const char *qd_connector_name(qd_connector_t *ct)
{
    return ct ? ct->config.name : 0;
}


text:
class Chef
  class DelayedEvaluator < Proc
  end
end

redpajama_set_name: RedPajamaGithub


text:
config_path="${HOME}/config files/inp_rename.cfg"
inp_path="${HOME}/inp_rename"
tar_path="${HOME}/pool"
system_log="${HOME}/log"

## current time
now_day=$(date +"%d") 	# current date
now_mon=$(date +"%m") 	# current month
now_year=$(date +"%Y") 	# current year
now_sec=$(date +"%S") 	# current seconds
now_min=$(date +"%M") 	# current minute
now_hour=$(date +"%H") 	# current hour
now_time="${now_hour}:${now_min}:${now_sec}"
now_date="${now_day}.${now_mon}.${now_year}"

declare -A assoc_list
##################### END global variables ############################
#######################################################################

#######################################################################
## function clear_screen(void)
##
## Clears terminal.
#######################################################################
function clear_screen {
	/usr/bin/env clear
}
#######################################################################

#######################################################################
## function print_intro_msg(void)
##
## Prints the introduction message to the inp_rename compontent.
#######################################################################
function print_intro_msg {
	echo -e " \ninp_rename Component"
	echo -e " System Date-Time: ${now_date} - ${now_time}"
}
#######################################################################

#######################################################################
## function print_version(void)
##
## Prints current script version.
#######################################################################
function print_version {
	clear_screen
	print_intro_msg
	echo -e " Current version : 1.1"
	echo -e " Executable Type : Bash Shell Script"
	echo -e " Release Date    : 22 March 2017\n"
}
#######################################################################

#######################################################################
## function print_usage(void)
##
## Prints the help message.
#######################################################################
function print_usage {
	clear_screen
	print_intro_msg
	echo -e " --- NAME ---\n"
	echo -e "  inp_rename - System Script of inp_rename Component\n"
	echo -e " --- SYNOPSIS ---\n"
	echo -e "  inp_rename [OPTION] ...\n"
	echo -e " --- DESCRIPTION ---\n"
	echo -e "  '-v' or '--version'\n"
	echo -e "     Prints the current version information. This option"
	echo -e "     can not be combined.\n"
	echo -e "  '-h' or '--help'\n"
	echo -e "     Prints the help message. This option can not be"
	echo -e "     combined.\n"
	echo -e "  ' <filename> \n"
	echo -e "     Renames the file given and moves it to the"
	echo -e "     appropriate directory. This option cannot be "
	echo -e "     combined. Using this option in conjuction with"
	echo -e "     incron's command 'IN_CLOSE_WRITE' is suggested."
	echo -e "     \n"
}
#######################################################################

#######################################################################
## function print_invalid_args(void)
##
## This function prints error message of invalid given command-line
## arguments.
#######################################################################
function print_invalid_args {
	print_intro_msg
	echo -e " (X) Invalid input argument or argument combination"
	echo -e " (!) Please type -h or --help to view the help message\n"
}
#######################################################################

#######################################################################
## function read_config(void)
##
## Gets settings from the appropriate configuration file.
#######################################################################
function read_config {
	while IFS='' read -r line || [[ -n $line ]]; do
		[[ "${line}" =~ ^#.*$ ]] && continue
		[[ -z "${line}" ]] && continue
		temp_arr=($(echo ${line}))
		if [ "${#temp_arr[@]}" -eq "2" ]; then assoc_list["${temp_arr[0]}"]="${temp_arr[1]}"
		else echo "  line problem"; continue; fi
	done < "${config_path}"
}
#######################################################################

#######################################################################
## function print_missing(void)
##
## Prints in stderr when the directory $HOME cannot be found.
#######################################################################
function print_missing {
	>&2 echo -e "$HOME directory not found."
}
#######################################################################

#######################################################################
## function write_log_msg(string log_msg)
##
## This function takes as argument a string and write it to log file
## The default log file is in variable ${system_log}/[date].log.
## The output format is "[ time ] log_message".
## As a caller the only obligation is to pass the message, function
## does the appropriate checks.
#######################################################################
function write_log_msg {
    lock_file="${HOME}/tmp/.inp_rename.pid"
    until (umask 222; echo $$ > ${lock_file}) 2>/dev/null   # try to set lock
    do
        sleep 2 # wait 2 seconds until next try
    done
    echo -e "[$(date +'%H'):$(date +'%M'):$(date +'%S')] ${1}" >> "${system_log}/inp_rename/$(date +'%Y')$(date +'%m')$(date +'%d').log"
    rm -f ${lock_file}      # unlock file
}
#######################################################################

#######################################################################
## function contains_str(string str, string substr)
##
## Returns 0 if the specified string contains the specified substring,
## otherwise returns 1.
#######################################################################
function contains_str {
    local string="${1}"
    local substring="${2}"
    if test "${string#*$substring}" != "$string"; then
    	return 0    # $substring is in $string
    else
        return 1    # $substring is not in $string
    fi
}
#######################################################################

#######################################################################
## function find_assoc_key(string assoc_string_key)
##
## Find in global variable assoc_list the relative key according to the
## argument assoc_string_key. If it exists returns 0 else returns 1.
## This function must called after the successfull reading of inp_rename.cfg file.
#######################################################################
function find_assoc_key {
	for i in "${!assoc_list[@]}"
	do
  		if contains_str ${1} ${i}; then return 0
  		else continue; fi
	done
	return 1
}
#######################################################################

#######################################################################
## function get_assoc_key(string assoc_string_key)
##
## Find in global variable assoc_list the relative key according to the
## argument assoc_string_key. If exists returns the key.
## This function must called after the successfull reading of inp_rename.cfg file.
#######################################################################
function get_assoc_key {
	for i in "${!assoc_list[@]}"
	do
  		if contains_str ${1} ${i}; then echo ${i}
  		else continue; fi
	done
}
#######################################################################

#######################################################################
## function rename_file (string absolute_filename)
##
## Renames given file using the new naming standard.
#######################################################################
function rename_file {
	if read_config; then
		base_file_name=$(basename ${1})
		elif [[ ${base_file_name: (-4)} == ".tmp" ]]; then return 0
		elif find_assoc_key ${base_file_name}; then
			assoc_key=$(get_assoc_key ${base_file_name})
			/usr/bin/rename ${inp_path}/${assoc_key} ${tar_path}/${assoc_list["${assoc_key}"]} ${inp_path}/${base_file_name}
			if [ "$?" -eq 0 ]; then
				write_log_msg "INFO: Renamed ${assoc_key} -> ${assoc_list[${assoc_key}]}"
			else
				write_log_msg "ERROR: Rename not successfull for ${1}."
			fi
		else
			mv "${1}" "${HOME}/unrouted/${base_file_name}" && write_log_msg "INFO: Missing rename value. File ${base_file_name} moved to unrouted directory."
		fi
	else
		write_log_msg "ERROR: Could not read inp_rename.cfg configuration file."
		exit -1
	fi
}
#########################################################################

##########################################################################
########################## MAIN PROCEDURE ################################
##########################################################################

if [ "$#" -eq 1 ]; then
	if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then print_usage
	elif [ "$1" = "--version" ] || [ "$1" = "-v" ]; then print_version
	elif [ -f "${1}" ]; then
		if [ -d "${HOME}" ]; then
			rename_file ${1}
		else
			print_missing
		fi
	else
		print_invalid_args
	fi
else
	print_invalid_args
fi

##########################################################################
######################## END MAIN PROCEDURE ##############################
##########################################################################


text:
using System;
using System.IO;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        using (StreamReader reader = File.OpenText("sample.txt"))
        while (!reader.EndOfStream)
        {
            string line = reader.ReadLine();
            if (null == line)
                continue;
            // do something with line
            char[] allCharacters = line.ToCharArray();

            string newLine = null;
            char currentChar = '@';
            foreach (char character in allCharacters)
            {
                if (currentChar != character)
                {
                    currentChar = character;
                    newLine += character;
                }
            }
            Console.WriteLine(newLine);
            Console.ReadLine();
        }
    }
}


text:
title: "Pogo Connect stylus sensitivity fix"
excerpt: "How to adjust the pressure sensitivity of a Pogo Connect's tip with firmware v1.1.0."
last_modified_at: 2018-11-06T11:59:22-05:00
categories: [mastering-paper]
tags: [Pogo Connect, Paper for iOS, Apple]
comments_locked: true
---

About a month ago I received a replacement [Pogo Connect Smart Pen]({% post_url /mastering-paper/2013-07-08-pogo-connect-smart-pen %}) due to a defect that caused it to stop working. Using this replacement for a few days it was clear to me that something was different about this newer model.

I use Paper by FiftyThree almost exclusively for drawing with the occasional Procreate experiment. When using either app I noticed that the sensitivity of the Pogo's tip seemed off. To get the full range of stroke sizes I now had to press extremely hard to register a wide stroke (see screenshot below), which really started to screw with how I draw.

![Calibrated Pogo Connect line comparison](/assets/images/pogo-connect-sensitivity-lines.jpg)

Isolating the cause is hard because so many factors had changed around the same time.

* Apple released iOS 7, a major rewrite and update to the iPad's operating system.
* TenOne Design updated the Pogo Connect's firmware to 1.1.0 --- needed for some of the [new tips](http://tenonedesign.com/connect.php) they recently released.
* Design changes were made to the Pogo Connect to reinforce the internals and tip.

From my tests it seems to be related to the 1.1.0 firmware or the extra solder being used to keep the tip from busting off. Older Pogo's with the 1.0.6 firmware worked just fine with iOS 7 and updated apps.

## But how do you fix it?

To begin, open **Paper by FiftyThree**[^pogo-paper] or your Pogo Connect supported app of choice, and press the button on the stylus to establish a connection.

[^pogo-paper]: As of version 2.5 FiftyThree has dropped support for the Pogo Connect Smart Pen.

Next you'll need to open (or download) the free [**Pogo Connect app**](https://itunes.apple.com/us/app/pogo-connect/id566688179?mt=8&at=11l5Vp&ct=website). With it you can do fun stuff like locate your Pogo if it's lost somewhere in your house, update the firmware, rename your Pogo, draw, or adjust the tip's pressure sensitivity.

![Connected](/assets/images/pogo-connect-app-connecting.jpg)

Upon opening the app you should notice it trying to connect to the stylus. When that completes, tap the **Configuration** tab at the very bottom to customize the stylus to your liking. The two settings I altered to make it behave how it used to were **Locator Beacon** and **Pressure Amount**.

{% figure caption:"I turned off the Locator Beacon, set Pressure Amount to +6, and selected the R3 tip to profile." %}
![Pogo Connect app settings screen](/assets/images/pogo-connect-app-settings.jpg)
{% endfigure %}

After making these changes you can doodle around in the space below to verify the sensitivity settings are to you liking. You can then switch back to Paper and the new settings should be noticeable in the app.

![Pogo Connect app doodle test](/assets/images/pogo-connect-app-doodle.jpg)

{% notice %}
#### ProTip: sensitivity settings

Any sensitivity settings you made in the Pogo Connect app don't appear to "stick" if the apps go to sleep or you lose a connection with the stylus. Following these steps seems to work at restoring them when using Paper by FiftyThree:

1. Unscrew the cap on the Pogo Connect and take out the battery.
2. Wait a few seconds and then place the battery back in and screw the top on to power cycle the Pogo.
3. Reconnect the stylus to Paper. You may have to force quit[^force-quit] the app if it doesn't connect.
4. After establishing a connection reopen the Pogo Connect app, wait for it to connect to the stylus, and then tap the **Configuration** tab again.
5. Check that the **Pressure Amount** slider is set properly and make a few test strokes in the space below to verify it is receiving input from the tip.
6. Switch back to Paper and you're good to go...for now.
{% endnotice %}

[^force-quit]: To force quit an app press the home button twice in rapid succession. Doing so will bring up a screen showing your recently used apps. Swipe left or right to find the app you want to quit and then swipe it up to remove it from view.

It's sort of a pain having to open another app each time I sit down to work on an illustration, but what can you do? I'm not entirely sure why the sensitivity settings don't persist when the stylus or app goes to sleep.


text:
package com.haxademic.core.media.audio.interphase;

import com.haxademic.core.debug.DebugView;
import com.haxademic.core.draw.color.ColorsHax;
import com.haxademic.core.draw.color.EasingColor;
import com.haxademic.core.draw.context.PG;
import com.haxademic.core.math.easing.LinearFloat;

import processing.core.PGraphics;

public class SequencerTexture
implements ISequencerDrawable {

	protected int index;
	protected int lastStep = -1;
	protected SequenceRect[] rects;
	protected PGraphics buffer;

	public SequencerTexture(int index) {
		this.index = index;
		buffer = PG.newPG(120, 320);
		DebugView.setTexture("SequencerTexture_"+index, buffer);
		rects = new SequenceRect[Interphase.NUM_STEPS];
		for (int i = 0; i < Interphase.NUM_STEPS; i++) {
			rects[i] = new SequenceRect(i);
		}
	}

	public PGraphics buffer() {
		return buffer;
	}

	// ISequencerDrawable methods

	public void update(boolean[] steps, int curStep) {
		// draw update
		buffer.beginDraw();
		buffer.background(0);
		buffer.noStroke();
		if(curStep != lastStep) {
			lastStep = curStep;
			for (int i = 0; i < Interphase.NUM_STEPS; i++) {
				rects[i].setBeat(curStep);
			}
		}
		for (int i = 0; i < Interphase.NUM_STEPS; i++) {
			rects[i].setActive(steps[i]);
			rects[i].update(buffer);
		}

		// aftermarket image overlay
		/*
		if(P.store.getInt(Interphase.BEAT) % 128 >= 64) {
			PImage overlay = ImageCacher.get("images/_sketch/sheraton-window-overlay.png");
			buffer.image(overlay, 0, 0, buffer.width, buffer.height);
		}
		*/

		buffer.endDraw();
	}

	// internal objects

	public class SequenceRect {

		protected static final int COLOR_INT_WHITE = 0xffffffff;
		protected static final int COLOR_INT_BLACK = 0xff000000;
		protected static final int COLOR_INT_CLEAR = 0x00ffffff;

		protected EasingColor stepColors;
		protected EasingColor stepActiveColors;
		protected LinearFloat stepCirclesProgress;
		protected LinearFloat stepFlashProgress;

		protected int rectIndex;
		protected boolean active = false;

		public SequenceRect(int rectIndex) {
			this.rectIndex = rectIndex;
			// init lerping color objects
			stepColors = new EasingColor(0xffffffff, 8);
			stepActiveColors = new EasingColor(0xffffffff, 8);
			stepCirclesProgress = new LinearFloat(0, 0.05f);
			stepFlashProgress = new LinearFloat(0, 0.1f);
		}

		public void update(PGraphics buffer) {
			// update lerping colors
			stepColors.update();
			stepActiveColors.update();
			stepCirclesProgress.update();
			stepFlashProgress.update();

			// position rect
			float rectW = buffer.width;
			float rectH = buffer.height / Interphase.NUM_STEPS;
			float rectY = rectH * rectIndex;

			// draw rect
			buffer.push();

			// base color
			float padding = 1;
			float padding2 = padding * 2;
			float padding4 = padding * 4;
			buffer.fill(stepColors.colorInt());
			buffer.rect(padding2, rectY + padding2, rectW - padding4, rectH - padding4);

			// flash color
			buffer.fill(255, stepFlashProgress.value() * 255);
			buffer.rect(padding2, rectY + padding2, rectW - padding4, rectH - padding4);

			buffer.pop();
		}

		public void setBeat(int curStep) {
			if(curStep == rectIndex) {
				stepFlashProgress.setCurrent(1);
				stepFlashProgress.setTarget(0);
			}
		}

		public void setActive(boolean isActive) {
			// save state only when switching
//			if(isActive != active) {
				active = isActive;

				// show sequence pattern
				int activeColor = (active) ? ColorsHax.COLOR_GROUPS[1][rectIndex % 4] : 0xff333333;
				stepColors.setTargetInt(activeColor);
//			}
		}
	}

}


text:
package org.spongepowered.common.event.tracking.context.transaction.inventory;

import com.google.common.collect.ImmutableList;
import net.minecraft.world.entity.player.Player;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.event.Cause;
import org.spongepowered.api.event.CauseStackManager;
import org.spongepowered.api.event.entity.SpawnEntityEvent;
import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent;
import org.spongepowered.api.item.inventory.Inventory;
import org.spongepowered.api.item.inventory.ItemStackSnapshot;
import org.spongepowered.api.item.inventory.Slot;
import org.spongepowered.api.item.inventory.transaction.SlotTransaction;
import org.spongepowered.common.entity.EntityUtil;
import org.spongepowered.common.event.tracking.PhaseContext;
import org.spongepowered.common.event.tracking.context.transaction.GameTransaction;
import org.spongepowered.common.event.tracking.context.transaction.type.TransactionTypes;
import org.spongepowered.common.event.tracking.context.transaction.world.SpawnEntityTransaction;
import org.spongepowered.common.event.tracking.phase.packet.PacketPhaseUtil;
import org.spongepowered.common.util.PrettyPrinter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

abstract class InventoryBasedTransaction extends GameTransaction<ChangeInventoryEvent> {

    final Inventory inventory;
    @MonotonicNonNull List<net.minecraft.world.entity.Entity> entities;
    @MonotonicNonNull private List<SlotTransaction> acceptedTransactions;
    protected boolean used = false;

    protected InventoryBasedTransaction(final Inventory inventory) {
        super(TransactionTypes.CHANGE_INVENTORY_EVENT.get());
        this.inventory = inventory;
    }

    @Override
    public Optional<BiConsumer<PhaseContext<@NonNull ?>, CauseStackManager.StackFrame>> getFrameMutator(
        @Nullable final GameTransaction<@NonNull ?> parent
    ) {
        return Optional.of((context, frame) -> {
            frame.pushCause(this.inventory);
        });
    }

    @Override
    public Optional<ChangeInventoryEvent> generateEvent(
        final PhaseContext<@NonNull ?> context, @Nullable final GameTransaction<@NonNull ?> parent,
        final ImmutableList<GameTransaction<ChangeInventoryEvent>> gameTransactions, final Cause currentCause
    ) {
        final ImmutableList<InventoryBasedTransaction> containerBasedTransactions = gameTransactions.stream()
            .filter(tx -> tx instanceof InventoryBasedTransaction)
            .map(tx -> (InventoryBasedTransaction) tx)
            .filter(tx -> !tx.used)
            .collect(ImmutableList.toImmutableList());

        final List<SlotTransaction> slotTransactions = containerBasedTransactions
            .stream()
            .map(InventoryBasedTransaction::getSlotTransactions)
            .flatMap(List::stream)
            .collect(Collectors.toList());
        for (InventoryBasedTransaction transaction : containerBasedTransactions) {
            transaction.used = true;
        }

        final List<Entity> entities = containerBasedTransactions.stream()
                .map(InventoryBasedTransaction::getEntitiesSpawned)
                .flatMap(List::stream)
                .collect(Collectors.toList());

        // TODO on pickup grouping does not work?
        final Map<Slot, List<SlotTransaction>> collected = slotTransactions.stream().collect(Collectors.groupingBy(SlotTransaction::slot));
        slotTransactions.clear();
        collected.values().forEach(list -> {
            final SlotTransaction first = list.get(0);
            if (list.size() > 1) {
                final ItemStackSnapshot last = list.get(list.size() - 1).defaultReplacement();
                slotTransactions.add(new SlotTransaction(first.slot(), first.original(), last));
            } else {
                slotTransactions.add(first);
            }
        });

        return containerBasedTransactions.stream()
            .map(t -> t.createInventoryEvent(slotTransactions, entities, context, currentCause))
            .filter(Optional::isPresent)
            .map(Optional::get)
            .findFirst();
    }

    List<SlotTransaction> getSlotTransactions() {
        return this.acceptedTransactions == null ? Collections.emptyList() : this.acceptedTransactions;
    }

    Optional<ChangeInventoryEvent> createInventoryEvent(
            final List<SlotTransaction> slotTransactions,
            final List<Entity> entities, final PhaseContext<@NonNull ?> context,
            final Cause currentCause
    ) {
        return Optional.empty();
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    List<Entity> getEntitiesSpawned() {
        return this.entities == null ? Collections.emptyList() : (List<Entity>) (List) this.entities;
    }

    @Override
    public void restore(
        final PhaseContext<@NonNull ?> context,
        final ChangeInventoryEvent event
    ) {

    }

    protected void handleEventResults(final Player player, final ChangeInventoryEvent event) {
        PacketPhaseUtil.handleSlotRestore(player, null, event.transactions(), event.isCancelled());

        if (this.entities != null && event instanceof SpawnEntityEvent) {
            EntityUtil.despawnFilteredEntities(this.entities, (SpawnEntityEvent) event);
        }
    }

    @Override
    public boolean markCancelledTransactions(
        final ChangeInventoryEvent event,
        final ImmutableList<? extends GameTransaction<ChangeInventoryEvent>> gameTransactions
    ) {
        if (event.isCancelled()) {
            event.transactions().forEach(SlotTransaction::invalidate);
            return true;
        }
        boolean cancelledAny = false;
        for (final SlotTransaction transaction : event.transactions()) {
            if (!transaction.isValid()) {
                cancelledAny = true;
                for (final GameTransaction<ChangeInventoryEvent> gameTransaction : gameTransactions) {
                    ((InventoryBasedTransaction) gameTransaction).getSlotTransactions()
                        .forEach(tx -> {
                            if (tx == transaction) {
                                gameTransaction.markCancelled();
                            }
                        });
                }
            }
        }
        return cancelledAny;
    }

    @Override
    public boolean absorbSlotTransaction(
        final ContainerSlotTransaction slotTransaction
    ) {
        if (this.inventory == slotTransaction.menu) {
            if (this.acceptedTransactions == null) {
                this.acceptedTransactions = new ArrayList<>();
            }
            this.acceptedTransactions.add(slotTransaction.transaction);
            return true;
        }
        // or accept menu transactions as inventory transactions
        final SlotTransaction newTransaction = slotTransaction.transaction;
        if (newTransaction.slot().viewedSlot() != newTransaction.slot() && this.inventory == newTransaction.slot().viewedSlot().parent()) {
            if (this.acceptedTransactions == null) {
                this.acceptedTransactions = new ArrayList<>();
            }
            this.acceptedTransactions.add(new SlotTransaction(newTransaction.slot().viewedSlot(), newTransaction.original(), newTransaction.defaultReplacement()));
            return true;
        }
        return false;
    }

    @Override
    public boolean absorbSpawnEntity(final PhaseContext<@NonNull ?> context, final SpawnEntityTransaction spawn) {
        if (context.doesContainerCaptureEntitySpawn(spawn.entityToSpawn)) {
            if (this.entities == null) {
                this.entities = new LinkedList<>();
            }
            this.entities.add(spawn.entityToSpawn);
            return true;
        }
        return super.absorbSpawnEntity(context, spawn);
    }

    @Override
    public void addToPrinter(final PrettyPrinter printer) {

    }

}


text:
package com.squareup.wire.schema;

import com.google.common.collect.ImmutableList;
import com.squareup.wire.internal.protoparser.FieldElement;
import com.squareup.wire.internal.protoparser.OneOfElement;

public final class OneOf {
  private final OneOfElement element;
  private final ImmutableList<Field> fields;

  OneOf(String packageName, OneOfElement element) {
    this.element = element;

    ImmutableList.Builder<Field> fields = ImmutableList.builder();
    for (FieldElement field : element.fields()) {
      fields.add(new Field(packageName, field));
    }
    this.fields = fields.build();
  }

  public String name() {
    return element.name();
  }

  public String documentation() {
    return element.documentation();
  }

  public ImmutableList<Field> fields() {
    return fields;
  }

  void link(Linker linker) {
    for (Field field : fields) {
      field.link(linker);
    }
  }

  void linkOptions(Linker linker) {
    for (Field field : fields) {
      field.linkOptions(linker);
    }
  }
}


text:
<?php

namespace Zimbra\Enum;

/**
 * ConvActionOp enum class
 *
 * @package   Zimbra
 * @category  Enum
 * @author    Nguyen Van Nguyen - nguyennv1981@gmail.com
 * @copyright Copyright © 2013 by Nguyen Van Nguyen.
 */
class ConvActionOp extends Base
{
    /**
     * Constant for value 'delete'
     * @return string 'delete'
     */
    const DELETE = 'delete';

    /**
     * Constant for value 'read'
     * @return string 'read'
     */
    const READ = 'read';

    /**
     * Constant for value 'flag'
     * @return string 'flag'
     */
    const FLAG = 'flag';

    /**
     * Constant for value 'priority'
     * @return string 'priority'
     */
    const PRIORITY = 'priority';
    /**
     * Constant for value 'tag'
     * @return string 'tag'
     */
    const TAG = 'tag';

    /**
     * Constant for value 'move'
     * @return string 'move'
     */
    const MOVE = 'move';

    /**
     * Constant for value 'spam'
     * @return string 'spam'
     */
    const SPAM = 'spam';

    /**
     * Constant for value 'trash'
     * @return string 'trash'
     */
    const TRASH = 'trash';
}


text:
package com.google.zxing.web.generator.client;

import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * A generator for URL addresses.
 *
 * @author Yohann Coppel
 */
public final class UrlGenerator implements GeneratorSource {

  private Grid table;
  private final TextBox url = new TextBox();

  public UrlGenerator(ChangeHandler handler, KeyPressHandler keyListener) {
    url.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
    url.addChangeHandler(handler);
    url.addKeyPressHandler(keyListener);
  }

  @Override
  public Grid getWidget() {
    if (table != null) {
      // early termination if the table has already been constructed
      return table;
    }

    table = new Grid(1, 2);
    table.getColumnFormatter().addStyleName(0, "firstColumn");

    url.setText("http://");

    table.setText(0, 0, "URL");
    table.setWidget(0, 1, url);

    return table;
  }

  @Override
  public String getName() {
    return "URL";
  }

  @Override
  public String getText() throws GeneratorException {
    return getUrlField();
  }

  private String getUrlField() throws GeneratorException {
    String input = url.getText();
    Validators.validateUrl(input);
    return input;
  }

  @Override
  public void validate(Widget widget) throws GeneratorException {
    if (widget == url) {
      getUrlField();
    }
  }

  @Override
  public void setFocus() {
    url.setFocus(true);
  }
}


text:
import { ensure, isDefined, TinyType } from 'tiny-types';

import { Timestamp } from '../model';

/**
 * Represents an internal domain event that occurs during test execution.
 *
 * @group Events
 */
export abstract class DomainEvent extends TinyType {

    /**
     * @param timestamp
     */
    protected constructor(public readonly timestamp: Timestamp = new Timestamp()) {
        super();
        ensure('timestamp', timestamp, isDefined());
    }
}


text:
<?php

/**
 *  Example for handling logout requests
 *
 * PHP Version 5
 *
 * @file     example_logout.php
 * @category Authentication
 * @package  PhpCAS
 * @author   Joachim Fritschi <jfritschi@freenet.de>
 * @author   Adam Franco <afranco@middlebury.edu>
 * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
 * @link     https://wiki.jasig.org/display/CASC/phpCAS
 */

// Load the settings from the central config file
require_once 'config.php';
// Load the CAS lib
require_once $phpcas_path . '/CAS.php';

// Uncomment to enable debugging
phpCAS::setDebug();

// Initialize phpCAS
phpCAS::client(CAS_VERSION_2_0, $cas_host, $cas_port, $cas_context);

// For production use set the CA certificate that is the issuer of the cert
// on the CAS server and uncomment the line below
// phpCAS::setCasServerCACert($cas_server_ca_cert_path);

// For quick testing you can disable SSL validation of the CAS server.
// THIS SETTING IS NOT RECOMMENDED FOR PRODUCTION.
// VALIDATING THE CAS SERVER IS CRUCIAL TO THE SECURITY OF THE CAS PROTOCOL!
phpCAS::setNoCasServerValidation();

// handle incoming logout requests
phpCAS::handleLogoutRequests();

// Or as an advanced featue handle SAML logout requests that emanate from the
// CAS host exclusively.
// Failure to restrict SAML logout requests to authorized hosts could
// allow denial of service attacks where at the least the server is
// tied up parsing bogus XML messages.
// phpCAS::handleLogoutRequests(true, $cas_real_hosts);

// force CAS authentication
phpCAS::forceAuthentication();

// for this test, simply print that the authentication was successfull
?>

    <title>phpCAS simple client</title>

Successfull Authentication!

    <?php require 'script_info.php' ?>

the user's login is <?php echo phpCAS::getUser(); ?>.

phpCAS version is <?php echo phpCAS::getVersion(); ?>.


text:
package ac.bbt.sp2014f_groupa.models;

import ac.bbt.sp2014f_groupa.SQLiteHelper;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

/**
 * データベースのRssesテーブルの一行をモデル化したクラス
 *
 * @author ueharamasato
 */
public class RssModel {
	private long id;
	private String url;
	private String title;
	private String created_at;

	/**
	 * コンストラクタ
	 */
	public RssModel() {}

	/**
	 * コンストラクタ
	 */
	public RssModel(
			long id
			, String url
			, String title
			, String created_at
			) {
		this.setId(id);
		this.setUrl(url);
		this.setTitle(title);
		this.setCreatedAt(created_at);
	}

	/**
	 * 検索条件を元に DB からオブジェクトを生成する
	 *
	 * @param	selection		検索条件
	 * @param	selectionArgs	検索条件で使用する値
	 * @return	該当データ
	 */
	public static RssModel findBy(String selection, String[] selectionArgs) throws NotFoundException {
		SQLiteHelper helper = SQLiteHelper.getInstance();
        SQLiteDatabase db = helper.getReadableDatabase();

        Cursor cursor = db.query(
                "rsses"
                , new String[] {
                		"id"
                		, "url"
                		, "title"
                		, "created_at"
                		}
                , selection
                , selectionArgs
                , null
                , null
                , null
            );

        // 該当データがない時は例外を射出
        if (cursor.getCount() == 0) {
        	throw new NotFoundException(
        			"該当するデータがありません。"
        			);
        }

        cursor.moveToFirst();
        RssModel rss = new RssModel(
        		cursor.getLong(0)
        		, cursor.getString(1)
        		, cursor.getString(2)
        		, cursor.getString(3)
        		);

        return rss;
	}

	/**
	 * id を元に DB からオブジェクトを生成する
	 *
	 * @param	id	RSSのID
	 * @return
	 */
	public static RssModel findById(long id) throws NotFoundException {
		RssModel rss = RssModel.findBy("id=?", new String[] {String.valueOf(id)});

        return rss;
	}

	/**
	 * url を元に DB からオブジェクトを生成する
	 *
	 * @param	id	RSSのURL
	 * @return
	 */
	public static RssModel findByUrl(String url) throws NotFoundException {
		RssModel rss = RssModel.findBy("url=?", new String[] {url});

        return rss;
	}

	/**
	 * id を元に該当データが存在するかをチェックする
	 * @param id	対象のID
	 * @return	存在する時は true, それ以外は false を返す
	 */
	public static boolean isIdExists(long id) {
		boolean result = true;
		try {
			RssModel.findById(id);
		} catch (NotFoundException e) {
			result = false;
		}

		return result;
	}

	/**
	 * url を元に該当データが存在するかをチェックする
	 * @param url	対象URL
	 * @return	存在する時は true, それ以外は false を返す
	 */
	public static boolean isUrlExists(String url) {
		boolean result = true;
		try {
			RssModel.findByUrl(url);
		} catch (NotFoundException e) {
			result = false;
		}

		return result;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getCreatedAt() {
		return created_at;
	}

	public void setCreatedAt(String created_at) {
		this.created_at = created_at;
	}
}


text:
class G3MCanvas2DDemoScene : public G3MDemoScene {
protected:
  void rawActivate(const G3MContext* context);

  void rawSelectOption(const std::string& option,
                       int optionIndex) {
    // do nothing
  }
public:
  G3MCanvas2DDemoScene(G3MDemoModel* model) :
  G3MDemoScene(model, "Canvas 2D", "", -1)
  {
  }

};

#endif


text:
package templemore.sbt.cucumber

import scala.collection.JavaConverters._
import java.lang.reflect.InvocationTargetException
import java.util.Properties

import cucumber.runtime.Runtime
import cucumber.runtime.RuntimeOptions
import cucumber.runtime.snippets.SummaryPrinter
import cucumber.runtime.model.CucumberFeature
import gherkin.formatter.Formatter
import gherkin.formatter.Reporter

class CucumberLauncher(debug: (String) => Unit, error: (String) => Unit) {

  def apply(cucumberArguments: Array[String],
            testClassLoader: ClassLoader): Int = {
    debug("Cucumber arguments: " + cucumberArguments.mkString(" "))
    val runtime = buildRuntime(System.getProperties, cucumberArguments, testClassLoader)
    runCucumber(runtime).asInstanceOf[Byte].intValue
  }

  private def runCucumber(runtime: CucumberRuntime) = try {
    runtime.initialise
    runtime.run
    runtime.printSummary
    runtime.exitStatus
  } catch {
    case e: InvocationTargetException => {
      val cause = if ( e.getCause == null ) e else e.getCause
      error("Error running cucumber. Cause: " + cause.getMessage)
      throw cause
    }
  }

  case class CucumberRuntime(runtime: Runtime, options: RuntimeOptions, loader: AnyRef,
                             formatter: Formatter, reporter: Reporter, summaryPrinter: SummaryPrinter) {
    private val loaderClass = loader.getClass.getInterfaces()(0)

    def initialise = runtime.writeStepdefsJson()
    def printSummary = summaryPrinter.print(runtime)
    def exitStatus = runtime.exitStatus

    def run = {
      val featureList = (classOf[RuntimeOptions].getMethod("cucumberFeatures", loaderClass)
                                                .invoke(options, loader)
                                                .asInstanceOf[java.util.List[CucumberFeature]]
                                                .asScala)
      featureList foreach { feature => feature.run(formatter, reporter, runtime) }
    }
  }

  private def buildRuntime(properties: Properties,
                           arguments: Array[String],
                           classLoader: ClassLoader): CucumberRuntime = try {
    def buildLoader(clazz: Class[_]) =
      clazz.getConstructor(classOf[ClassLoader]).newInstance(classLoader).asInstanceOf[AnyRef]

    val loaderClass = loadCucumberClasses(classLoader)

    val options = new RuntimeOptions(properties, arguments :_*)
    val loader = buildLoader(loaderClass)

    val runtimeConstructor = classOf[Runtime].getConstructor(loaderClass.getInterfaces()(0), classOf[ClassLoader], classOf[RuntimeOptions])
    val runtime = runtimeConstructor.newInstance(loader, classLoader, options).asInstanceOf[Runtime]

    CucumberRuntime(runtime, options, loader,
                    options.formatter(classLoader), options.reporter(classLoader), new SummaryPrinter(System.out))
  } catch {
    case e: Exception =>
      error("Unable to construct cucumber runtime. Please report this as an error. (Details: " + e.getMessage + ")")
      throw e
  }

  private def loadCucumberClasses(classLoader: ClassLoader) = try {
    classLoader.loadClass("cucumber.runtime.io.MultiLoader")
  } catch {
    case e: ClassNotFoundException =>
      error("Unable to load Cucumber classes. Please check your project dependencies. (Details: " + e.getMessage + ")")
      throw e
  }
}


text:
<?php
namespace Intraxia\Jaxion\Test\Axolotl\Dictionary;

use Intraxia\Jaxion\Axolotl\Dictionary;
use PHPUnit_Framework_TestCase;

class GetOrElseTest extends PHPUnit_Framework_TestCase {
	public function test_get_or_else() {
		$d = new Dictionary( 'string', 'int', array(
			'a' => 1,
			'b' => 2,
			'c' => 3,
			'd' => 4,
		) );

		$result  = $d->get_or_else( 'a', 'default' );
		$result2 = $d->get_or_else( 'e', 'default' );

		$this->assertEquals( 1, $result );
		$this->assertEquals( 'default', $result2 );
	}
}


text:
'use strict';
var vows = require('vows');
var assert = require('assert');
var tough = require('../lib/cookie');
var Cookie = tough.Cookie;

function matchVows(func, table) {
  var theVows = {};
  table.forEach(function (item) {
    var str = item[0];
    var dom = item[1];
    var expect = item[2];
    var label = str + (expect ? " matches " : " doesn't match ") + dom;
    theVows[label] = function () {
      assert.equal(func(str, dom), expect);
    };
  });
  return theVows;
}

function defaultPathVows(table) {
  var theVows = {};
  table.forEach(function (item) {
    var str = item[0];
    var expect = item[1];
    var label = str + " gives " + expect;
    theVows[label] = function () {
      assert.equal(tough.defaultPath(str), expect);
    };
  });
  return theVows;
}

vows
  .describe('Domain and Path')
  .addBatch({
    "domain normalization": {
      "simple": function () {
        var c = new Cookie();
        c.domain = "EXAMPLE.com";
        assert.equal(c.canonicalizedDomain(), "example.com");
      },
      "extra dots": function () {
        var c = new Cookie();
        c.domain = ".EXAMPLE.com";
        assert.equal(c.cdomain(), "example.com");
      },
      "weird trailing dot": function () {
        var c = new Cookie();
        c.domain = "EXAMPLE.ca.";
        assert.equal(c.canonicalizedDomain(), "example.ca.");
      },
      "weird internal dots": function () {
        var c = new Cookie();
        c.domain = "EXAMPLE...ca.";
        assert.equal(c.canonicalizedDomain(), "example...ca.");
      },
      "IDN": function () {
        var c = new Cookie();
        c.domain = "δοκιμή.δοκιμή"; // "test.test" in greek
        assert.equal(c.canonicalizedDomain(), "xn--jxalpdlp.xn--jxalpdlp");
      }
    }
  })
  .addBatch({
    "Domain Match": matchVows(tough.domainMatch, [
      // str,          dom,          expect
      ["example.com", "example.com", true],
      ["eXaMpLe.cOm", "ExAmPlE.CoM", true],
      ["no.ca", "yes.ca", false],
      ["wwwexample.com", "example.com", false],
      ["www.example.com", "example.com", true],
      ["example.com", "www.example.com", false],
      ["www.subdom.example.com", "example.com", true],
      ["www.subdom.example.com", "subdom.example.com", true],
      ["example.com", "example.com.", false], // RFC6265 S4.1.2.3
      ["192.168.0.1", "168.0.1", false], // S5.1.3 "The string is a host name"
      [null, "example.com", null],
      ["example.com", null, null],
      [null, null, null],
      [undefined, undefined, null],
    ])
  })

  .addBatch({
    "default-path": defaultPathVows([
      [null, "/"],
      ["/", "/"],
      ["/file", "/"],
      ["/dir/file", "/dir"],
      ["noslash", "/"],
    ])
  })
  .addBatch({
    "Path-Match": matchVows(tough.pathMatch, [
      // request, cookie, match
      ["/", "/", true],
      ["/dir", "/", true],
      ["/", "/dir", false],
      ["/dir/", "/dir/", true],
      ["/dir/file", "/dir/", true],
      ["/dir/file", "/dir", true],
      ["/directory", "/dir", false],
    ])
  })
  .addBatch({
    "permuteDomain": {
      "base case": {
        topic: tough.permuteDomain.bind(null, 'example.com'),
        "got the domain": function (list) {
          assert.deepEqual(list, ['example.com']);
        }
      },
      "two levels": {
        topic: tough.permuteDomain.bind(null, 'foo.bar.example.com'),
        "got three things": function (list) {
          assert.deepEqual(list, ['example.com', 'bar.example.com', 'foo.bar.example.com']);
        }
      },
      "invalid domain": {
        topic: tough.permuteDomain.bind(null, 'foo.bar.example.localduhmain'),
        "got three things": function (list) {
          assert.equal(list, null);
        }
      }
    },
    "permutePath": {
      "base case": {
        topic: tough.permutePath.bind(null, '/'),
        "just slash": function (list) {
          assert.deepEqual(list, ['/']);
        }
      },
      "single case": {
        topic: tough.permutePath.bind(null, '/foo'),
        "two things": function (list) {
          assert.deepEqual(list, ['/foo', '/']);
        },
        "path matching": function (list) {
          list.forEach(function (e) {
            assert.ok(tough.pathMatch('/foo', e));
          });
        }
      },
      "double case": {
        topic: tough.permutePath.bind(null, '/foo/bar'),
        "four things": function (list) {
          assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
        },
        "path matching": function (list) {
          list.forEach(function (e) {
            assert.ok(tough.pathMatch('/foo/bar', e));
          });
        }
      },
      "trailing slash": {
        topic: tough.permutePath.bind(null, '/foo/bar/'),
        "three things": function (list) {
          assert.deepEqual(list, ['/foo/bar', '/foo', '/']);
        },
        "path matching": function (list) {
          list.forEach(function (e) {
            assert.ok(tough.pathMatch('/foo/bar/', e));
          });
        }
      }
    }
  })
  .export(module);


text:
#include "gkConstraint.h"
#include "gkGameObject.h"

gkConstraint::gkConstraint()
	:   m_object(0),
	    m_space(TRANSFORM_LOCAL),
	    m_influence(1.0)
{
	m_matrix.setIdentity();
}

void gkConstraint::_applyMatrix(void)
{
	///\todo do space conversion / blending here.

	if (m_object && m_object->isInstanced())
		m_object->setTransform(m_matrix);
}