publish final code

Fork of Pubnub_c_core by Srdjan Veljkovic

Files at this revision

API Documentation at this revision

Comitter:
sveljko
Date:
Thu Nov 10 22:20:11 2016 +0000
Child:
1:929314a174af
Commit message:
Initial commit of Pubnub C-core

Changed in this revision

pbntf_trans_outcome_common.h Show annotated file Show diff for this revision Revisions of this file
pbpal.h Show annotated file Show diff for this revision Revisions of this file
pubnub_alloc.h Show annotated file Show diff for this revision Revisions of this file
pubnub_alloc_std.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_api_types.h Show annotated file Show diff for this revision Revisions of this file
pubnub_assert.h Show annotated file Show diff for this revision Revisions of this file
pubnub_assert_std.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_blocking_io.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_blocking_io.h Show annotated file Show diff for this revision Revisions of this file
pubnub_ccore.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_ccore.h Show annotated file Show diff for this revision Revisions of this file
pubnub_coreapi.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_coreapi.h Show annotated file Show diff for this revision Revisions of this file
pubnub_internal_common.h Show annotated file Show diff for this revision Revisions of this file
pubnub_json_parse.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_json_parse.h Show annotated file Show diff for this revision Revisions of this file
pubnub_log.h Show annotated file Show diff for this revision Revisions of this file
pubnub_memory_block.h Show annotated file Show diff for this revision Revisions of this file
pubnub_mutex.h Show annotated file Show diff for this revision Revisions of this file
pubnub_netcore.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_netcore.h Show annotated file Show diff for this revision Revisions of this file
pubnub_timers.cpp Show annotated file Show diff for this revision Revisions of this file
pubnub_timers.h Show annotated file Show diff for this revision Revisions of this file
pubnub_version.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pbntf_trans_outcome_common.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,26 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+
+/** This macro does the common "stuff to do" on the outcome of a
+    transaction. Should be used by all `pbntf_trans_outcome()`
+    functions.
+
+    In case of PubNub protocol error, resets the time-token. This means 
+    some messages were (possibly) lost, but allows us to recover from bad 
+    situations, e.g. too many messages queued or unexpected problem caused 
+    by a particular message.
+*/
+#define PBNTF_TRANS_OUTCOME_COMMON(pb) do {                             \
+        enum pubnub_res M_pbrslt_ = (pb)->core.last_result;             \
+        PUBNUB_LOG_INFO("Pubnub Transaction outcome: %d\n", M_pbrslt_); \
+        switch (M_pbrslt_) {                                            \
+        case PNR_FORMAT_ERROR:                                          \
+        case PNR_TIMEOUT:                                               \
+        case PNR_IO_ERROR:                                              \
+            (pb)->core.timetoken[0] = '0';                              \
+            (pb)->core.timetoken[1] = '\0';                             \
+            break;                                                      \
+        default:                                                        \
+            break;                                                      \
+        }                                                               \
+        (pb)->state = PBS_IDLE;                                         \
+    } while(0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pbpal.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,167 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PBPAL
+#define      INC_PBPAL
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+
+#include "pubnub_api_types.h"
+
+
+/** Initializes the Pubnub PAL for the given Pubnub context.
+ */
+void pbpal_init(pubnub_t *pb);
+
+/** Results that functions for (DNS) resolving and
+    connecting can return.
+*/
+enum pbpal_resolv_n_connect_result {
+    pbpal_resolv_resource_failure,
+    pbpal_resolv_failed_send,
+    pbpal_resolv_send_wouldblock,
+    pbpal_resolv_sent,
+    pbpal_resolv_failed_rcv,
+    pbpal_resolv_rcv_wouldblock,
+    pbpal_resolv_failed_processing,
+    pbpal_connect_resource_failure,
+    pbpal_connect_failed,
+    pbpal_connect_wouldblock,
+    pbpal_connect_success
+};
+
+/** Handles start of a TCP (HTTP) connection. It first handles DNS
+    resolving for the context @p pb.  If DNS is already resolved, it
+    proceeds to establishing TCP connection. Otherwise, will issue a
+    DNS request.
+    
+    Call this function on start of a transaction or on receiving
+    response from DNS server.
+    
+    It also establishes "a link" between the TCP connection (socket,
+    or whatever in a given platform) and the Pubnub context.
+    
+    @note This uses a "non-conventional" interpretation of #PNR_IN_PROGRESS.
+    Here it is not an error, but an indication that "DNS resolution is
+    in progress", while #PNR_STARTED means "DNS resolved, TCP connect in
+    progress". We could have provided another enum for results specific to
+    this function, but then would have to map its errors to the generic
+    Pubnub errors, which would take time and code.
+    
+    @param pb The context for which to handle starting of connection
+    @return PNR_IN_PROGRESS: DNS not yet resolved, PNR_STARTED: await
+    TCP connection, PNR_OK: TCP connected, other: the actual error
+*/
+enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb);
+
+
+enum pbpal_resolv_n_connect_result pbpal_check_resolv_and_connect(pubnub_t *pb);
+
+/** Sends data over an established connection (with the Pubnub server).
+    At one time, only one sending of data can take place.
+
+    If it cannot send all the data, it will send as much as it can.
+    You should check if sending was completed by calling pbpal_sent().
+
+    @param pb The context of an established TCP connection
+    @param data Pointer to the first octet(byte) of the data to send
+    @param n Number of octets (bytes) to send
+    @return 0: sent, -1: error: sending already in progress, 
+    +1: sending started, not finished
+*/
+int pbpal_send(pubnub_t *pb, void const *data, size_t n);
+
+/** Helper macro for optimisation of sending of literal strings.
+    We know their length, we don't have to call strlen().
+ */
+#define pbpal_send_literal_str(pb, litstr) {          \
+        uint8_t s_[] = litstr;                        \
+        pbpal_send((pb), s_, sizeof s_ - 1); }
+
+/** The effect of this is the same as:
+
+    return pbpal_send(pb, s, strlen(s));
+
+    But, it doesn't have to be implemented that way.
+*/
+int pbpal_send_str(pubnub_t *pb, char const *s);
+
+/** Returns the status of sending. Don't try another
+    sending until previous is complete.
+
+    @return 0: sending finished, +1: sending still in progress
+    -1: sending failed
+*/
+int pbpal_send_status(pubnub_t *pb);
+
+/** Starts reading a line from the TCP connection. In other words,
+    reading until it finds a newline character.
+
+    @param pb The Pubnub context of the connection 
+    @return 0: newline read, else: not yet, try again later
+*/
+int pbpal_start_read_line(pubnub_t *pb);
+
+/** Returns the status of reading a line. Line reading was
+    started with pbpal_start_read_line().
+    @return 0: line was read, +1: line reading still in progress
+    -1: line reading failed
+*/
+enum pubnub_res pbpal_line_read_status(pubnub_t *pb);
+
+/** Returns the length of the data in the receive buffer
+    at this time.
+*/
+int pbpal_read_len(pubnub_t *pb);
+
+/** Starts reading a given number of octets (bytes) from an
+    established TCP connection. Only one reading can take place at any
+    given time.
+
+    To check if reading is complete, call pbpal_read_over().
+
+    @param pb The Pubnub context of an established TCP connection
+    @param n Number of octets (bytes) to read
+    @return 0: OK (started), -1: error (reading already started)
+*/
+int pbpal_start_read(pubnub_t *pb, size_t n);
+
+/** Returns if reading is done (has the requested number of octets
+    (bytes) been read).
+*/
+bool pbpal_read_over(pubnub_t *pb);
+
+/** Returns whether for the given Pubnub context the TCP
+    connection has closed.
+*/
+bool pbpal_closed(pubnub_t *pb);
+
+/** Breaks the link between a Pubnub context and the TCP connection.
+*/
+void pbpal_forget(pubnub_t *pb);
+
+/** Closes (or starts the closing of) the TCP connection of the given
+    Pubnub context 
+
+    @return 0: OK, closed; +1: close initiated, call pbpal_closed()
+    later to check; -1: error, can't close socket
+*/
+int pbpal_close(pubnub_t *pb);
+
+/** Checks whether a TCP connection is established. Call after
+    starting a TCP connection (thus, after DNS resolution is over).
+*/
+enum pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb);
+
+/** Sets blocking I/O option on the context for the communication */
+int pbpal_set_blocking_io(pubnub_t *pb);
+
+/** Frees-up any resources allocated by the PAL for the given
+    context. After this call, context is not safe for use by PAL any
+    more (it is assumed it will be freed-up by the caller).
+*/
+void pbpal_free(pubnub_t *pb);
+
+
+#endif /* !defined INC_PBPAL */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_alloc.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,38 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_ALLOC
+#define      INC_PUBNUB_ALLOC
+
+
+#include "pubnub_api_types.h"
+
+
+/** Returns an allocated context. After successful allocation, please
+    call pubnub_init() to prepare the context for regular use.
+
+    Do not make a context on your own - always get a context pointer
+    by calling this funciton.
+
+    @return Context pointer on success, NULL on error
+ */
+pubnub_t *pubnub_alloc(void);
+
+/** Frees a previously allocated context, if it is not in a
+    transaction.  If a context is in a transaction, first cancel it
+    (call pubnub_cancel()), then wait for the context to finish the
+    cancelling.
+
+    It's OK to call this function on a context whose transaction is
+    not over and done, it will just fail, but will not affect the
+    transaction in any way.
+
+    You don't have to free a context when you finish a transaction.
+    Just start a new one. Free a context if you're done doing Pubnub
+    transactions for a long time.
+
+    @param pb Pointer to a context which to free
+    @return 0: OK, context freed; else: error, context untouched
+*/
+int pubnub_free(pubnub_t *pb);
+
+
+#endif  /* !defined INC_PUBNUB_ALLOC */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_alloc_std.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,147 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_internal.h"
+#include "pubnub_assert.h"
+#include "pubnub_log.h"
+
+#include "pbpal.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+#if defined PUBNUB_ASSERT_LEVEL_EX
+static pubnub_t **m_allocated;
+static unsigned m_n;
+static unsigned m_cap;
+pubnub_mutex_static_decl_and_init(m_lock);
+#endif
+
+
+static void save_allocated(pubnub_t *pb)
+{
+#if defined PUBNUB_ASSERT_LEVEL_EX
+    pubnub_mutex_init_static(m_lock);
+    pubnub_mutex_lock(m_lock);
+    if (m_n == m_cap) {
+        pubnub_t **npalloc = (pubnub_t**)realloc(m_allocated, sizeof m_allocated[0] * (m_n+1));
+        if (NULL == npalloc) {
+            pubnub_mutex_unlock(m_lock);
+            return;
+        }
+        m_allocated = npalloc;
+        m_allocated[m_n++] = pb;
+        m_cap = m_n;
+    }
+    else {
+        m_allocated[m_n++] = pb;
+    }
+    pubnub_mutex_unlock(m_lock);
+#endif
+}
+
+
+static void remove_allocated(pubnub_t *pb)
+{
+#if defined PUBNUB_ASSERT_LEVEL_EX
+    size_t i;
+    for (i = 0; i < m_n; ++i) {
+        if (m_allocated[i] == pb) {
+            if (i != m_n - 1) {
+                memmove(m_allocated + i, m_allocated + i + 1, sizeof m_allocated[0] * (m_n - i - 1));
+            }
+            --m_n;
+            break;
+        }
+    }
+#endif
+}
+
+
+static bool check_ctx_ptr(pubnub_t const *pb)
+{
+#if defined PUBNUB_ASSERT_LEVEL_EX
+    size_t i;
+    for (i = 0; i < m_n; ++i) {
+        if (m_allocated[i] == pb) {
+            return true;
+        }
+    }
+    return false;
+#else
+    return pb != NULL; 
+#endif
+}
+
+
+bool pb_valid_ctx_ptr(pubnub_t const *pb)
+{
+#if defined PUBNUB_ASSERT_LEVEL_EX
+    bool result;
+
+    pubnub_mutex_init_static(m_lock);
+    pubnub_mutex_lock(m_lock);
+    result = check_ctx_ptr(pb);
+    pubnub_mutex_unlock(m_lock);
+
+    return result;
+#else
+    return pb != NULL; 
+#endif
+}
+
+
+pubnub_t *pubnub_alloc(void)
+{
+    pubnub_t *pb = (pubnub_t*)malloc(sizeof(pubnub_t));
+    if (pb != NULL) {
+        save_allocated(pb);
+    }
+    return pb;
+}
+
+
+void pballoc_free_at_last(pubnub_t *pb)
+{
+    PUBNUB_ASSERT_OPT(pb != NULL);
+
+    pubnub_mutex_init_static(m_lock);
+    pubnub_mutex_lock(m_lock);
+    pubnub_mutex_lock(pb->monitor);
+
+    PUBNUB_ASSERT_OPT(pb->state == PBS_NULL);
+
+    pbcc_deinit(&pb->core);
+    pbpal_free(pb);
+    remove_allocated(pb);
+    pubnub_mutex_unlock(m_lock);
+    pubnub_mutex_unlock(pb->monitor);
+    pubnub_mutex_destroy(pb->monitor);
+    free(pb);
+}
+
+
+int pubnub_free(pubnub_t *pb)
+{
+    int result = -1;
+
+    PUBNUB_ASSERT(check_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (PBS_IDLE == pb->state) {
+        pb->state = PBS_NULL;
+#if defined(PUBNUB_CALLBACK_API)
+            pbntf_requeue_for_processing(pb);
+            pubnub_mutex_unlock(pb->monitor);
+#else
+            pubnub_mutex_unlock(pb->monitor);
+            pballoc_free_at_last(pb);
+#endif
+
+        result = 0;
+    }
+    else {
+        pubnub_mutex_unlock(pb->monitor);
+    }
+
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_api_types.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,144 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_API_TYPES
+#define      INC_PUBNUB_API_TYPES
+
+
+/** @file pubnub_api_types.h
+
+    These are the definitions of types used by most functions of the
+    Pubnub C-core API. Users of the SDK in general don't need to
+    include it, as other headers of the API will include it for them.
+ */
+
+struct pubnub_;
+
+/** A pubnub context. An opaque data structure that holds all the data
+    needed for a context.
+ */
+typedef struct pubnub_ pubnub_t;
+
+
+/** Result codes for Pubnub functions and transactions.  */
+enum pubnub_res {
+    /** Success. Transaction finished successfully. */
+    PNR_OK,
+    /** Pubnub host name resolution failed. We failed to get
+        an IP address from the Pubnub host name ("origin").
+        Most of the time, this comes down to a DNS error.
+    */
+    PNR_ADDR_RESOLUTION_FAILED,
+    /** Connecting to Pubnub server failed. Most often,
+        this means a network outage, but could be many things.
+        If using SSL/TLS, it could be some of its errors.
+    */
+    PNR_CONNECT_FAILED,
+    /** A time-out happened in the network. Mostly, this is because
+        a network outage happened while being connected to the Pubnub
+        server, but could be other things.
+    */
+    PNR_CONNECTION_TIMEOUT,
+    /** Time-out before the request has completed. This is reported
+        for a time-out detected by Pubnub client itself, not some
+        reported by others (i.e. the TCP/IP stack). 
+    */
+    PNR_TIMEOUT,
+    /** Connection to Pubnub aborted (in most cases, a TCP reset was
+        received) */
+    PNR_ABORTED,
+    /** Communication error (network or HTTP response format). */
+    PNR_IO_ERROR,
+    /** HTTP error. Call pubnub_last_http_code() to get the error
+     * code. */
+    PNR_HTTP_ERROR,
+    /** Unexpected input in received JSON. */
+    PNR_FORMAT_ERROR,
+    /** Request cancelled by user. */
+    PNR_CANCELLED,
+    /** Transaction started. Await the outcome. */
+    PNR_STARTED,
+    /** Transaction (already) ongoing. Can't start a new transaction
+        while the old one is in progress. */
+    PNR_IN_PROGRESS,
+    /** Receive buffer (from previous transaction) not read, new
+        subscription not allowed.
+    */
+    PNR_RX_BUFF_NOT_EMPTY,
+    /** The buffer is too small. Increase #PUBNUB_BUF_MAXLEN.
+    */
+    PNR_TX_BUFF_TOO_SMALL,
+    /** Channel specification / name is invalid. 
+     */
+    PNR_INVALID_CHANNEL,
+    /** Publish transaction failed - error returned from Pubnub. To
+        see the reason describing the failure, call
+        pubnub_last_publish_result().
+     */
+    PNR_PUBLISH_FAILED,
+    /** A transaction related to channel registry failed - error
+        returned from Pubnub. To see the reason describing the
+        failure, get the value for key "message" from the response
+        (which is a JSON object) and value for key "status" for the
+        numeric code of the error.
+     */
+    PNR_CHANNEL_REGISTRY_ERROR,
+    /** Reply is too big to fit in our reply buffer. This same error
+        is reported if the reply buffer is statically or dynamically
+        allocated.
+    */
+    PNR_REPLY_TOO_BIG,
+    /** An internal error in processing */
+    PNR_INTERNAL_ERROR,
+    /** Encryption (and decryption) not supported */
+    PNR_CRYPTO_NOT_SUPPORTED
+};
+
+
+/** Type of Pubnub operation/transaction */
+enum pubnub_trans {
+    /** No transaction at all */
+    PBTT_NONE,
+    /** Subscribe operation/transaction */
+    PBTT_SUBSCRIBE,
+    /** Publish operation/transaction */
+    PBTT_PUBLISH,
+    /** Leave (channel(s)) operation/transaction */
+    PBTT_LEAVE,
+    /** Time (get from Pubnub server) operation/transaction */
+    PBTT_TIME,
+    /** History V2 (get message history for the channel from Pubnub
+     * server) operation/transaction */
+    PBTT_HISTORY,
+    /** Here-now (get UUIDs of currently present users in channel(s))
+     * operation/transaction */
+    PBTT_HERENOW,
+    /** Global here-now (get UUIDs of currently present users in all
+        channels) operation/transaction */
+    PBTT_GLOBAL_HERENOW,
+    /** Where-now (get channels in which an user (identified by UUID)
+        is currently present) operation/transaction */
+    PBTT_WHERENOW,
+    /** Set state (for a user (identified by UUID) on channel(s))
+     * operation/transaction */
+    PBTT_SET_STATE,
+    /** Get state (for a user (identified by UUID) on channel(s))
+     * operation/transaction */
+    PBTT_STATE_GET,
+    /** Remove a channel group (from the channel-registry)
+     * operation/transaction */
+    PBTT_REMOVE_CHANNEL_GROUP,
+    /** Remove a channel from a channel group (in the channel-registry)
+     * operation/transaction */
+    PBTT_REMOVE_CHANNEL_FROM_GROUP,
+    /** Add a channel to a channel group (in the channel-registry)
+     * operation/transaction */
+    PBTT_ADD_CHANNEL_TO_GROUP,
+    /** Get a list of all channels in a channel group (from the
+     * channel-registry) operation/transaction */
+    PBTT_LIST_CHANNEL_GROUP,
+    /** Inform Pubnub that we're still working on @p channel and/or @p
+        channel_group operation/transaction */
+    PBTT_HEARTBEAT,
+};
+
+
+#endif /* !defined INC_PUBNUB_API_TYPES */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_assert.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,151 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_ASSERT
+#define      INC_PUBNUB_ASSERT
+
+#include <stdbool.h>
+
+
+/** The Pubnub ASSERT macros. There are several layers:
+    - highest (PUBNUB_ASSERT_LEVEL_EX): all checks enabled, 
+    even the long lasting ones
+    - regular (PUBNUB_ASSERT_LEVEL): only the long lasting
+    checks are disabled
+    - lowest (PUBNUB_ASSERT_LEVEL_OPT): only the checks with
+    negligible execution length are enabled
+    - none (PUBNUB_ASSERT_LEVEL_NONE): all checks disabled
+
+    Define at most one of above mentioned macros before including this
+    header.  If none is defined, the highest (_EX) level is assumed.
+ */
+
+/* Let's make sure only one of @c PUBNUB_ASSERT_LEVEL_EX, @c
+ * PUBNUB_ASSERT_LEVEL, @c PUBNUB_ASSERT_LEVEL_OPT or @c
+ * PUBNUB_ASSERT_LEVEL_NONE is defined.
+ */
+
+#if defined(PUBNUB_ASSERT_LEVEL_EX) && (                                \
+    defined(PUBNUB_ASSERT_LEVEL) || defined(PUBNUB_ASSERT_LEVEL_OPT) || \
+    defined(PUBNUB_ASSERT_LEVEL_NONE) ) 
+#error Cannott define PUBNUB_ASSERT_LEVEL_EX and any lower level (regular, _OPT, _NONE)
+#endif
+
+#if defined(PUBNUB_ASSERT_LEVEL) && (                                   \
+    defined(PUBNUB_ASSERT_LEVEL_OPT) || defined(PUBNUB_ASSERT_LEVEL_NONE) ) 
+#error Cannott define PUBNUB_ASSERT_LEVEL and any lower level (_OPT, _NONE)
+#endif
+
+
+#if defined(PUBNUB_ASSERT_LEVEL_OPT) && defined(PUBNUB_ASSERT_LEVEL_NONE) 
+#error Cannott define PUBNUB_ASSERT_LEVEL_OPT and PUBNUB_ASSERT_LEVEL_NONE
+#endif
+
+/* If none of ASSERT level defining macros is defined, let's assume
+ * the highest level.
+ */
+
+#if !defined(PUBNUB_ASSERT_LEVEL_EX) &&             \
+    !defined(PUBNUB_ASSERT_LEVEL) &&                \
+    !defined(PUBNUB_ASSERT_LEVEL_OPT) &&                        \
+    !defined(PUBNUB_ASSERT_LEVEL_NONE)
+#define PUBNUB_ASSERT_LEVEL_EX
+#endif
+
+#ifdef _MSC_VER
+#define PUBNUB_ANALYSIS_ASSUME(X) __analysis_assume(X)
+#else
+#define PUBNUB_ANALYSIS_ASSUME(X)
+#endif
+
+/** The common ASSERT implementation */
+#define PUBNUB_ASSERT_IMPL(X) do {                                      \
+        PUBNUB_ANALYSIS_ASSUME(X);                                      \
+        (X) ? (void)0 : pubnub_assert_failed(#X, __FILE__, __LINE__);   \
+    } while (false)
+
+/** Should make the compiler not report "unused variable"
+    warnings.
+*/
+#define PUBNUB_UNUSED(x) do { (void)sizeof(x); } while (false)
+
+
+/* Define the ASSERT macro for the highest (_EX) level.
+ */
+#if defined PUBNUB_ASSERT_LEVEL_EX
+#define PUBNUB_ASSERT_EX(X) PUBNUB_ASSERT_IMPL(X)
+#else
+#define PUBNUB_ASSERT_EX(X) PUBNUB_UNUSED(X)
+#endif
+
+/* Determine if regular level is to be used. 
+ */
+#if defined(PUBNUB_ASSERT_LEVEL_EX) || defined(PUBNUB_ASSERT_LEVEL)
+#define PUBNUB_ASSERT_IS_ACTIVE       // also usable directly in client code
+#endif
+
+/* Define the ASSERT macro for the regular level.
+ */
+#if defined(PUBNUB_ASSERT_IS_ACTIVE)
+#define PUBNUB_ASSERT(X) PUBNUB_ASSERT_IMPL(X)
+#else
+#define PUBNUB_ASSERT(X) PUBNUB_UNUSED(X)
+#endif
+
+
+/* Define the ASSERT macro for the lowest level
+ */
+#if !defined(PUBNUB_ASSERT_LEVEL_NONE)
+#define PUBNUB_ASSERT_OPT(X) PUBNUB_ASSERT_IMPL(X)
+#else
+#define PUBNUB_ASSERT_OPT(X) PUBNUB_UNUSED(X)
+#endif
+
+
+
+/** This will invoke the installed assert handler.  The default
+    behavior is pubnub_assert_handler_abort().
+ */
+void pubnub_assert_failed(char const *s, char const *file, long line);
+
+/** Prototype of a Pubnub assertion failure handler. There are several
+    standard handlers, but you can also provide your own.
+    @param s The string that defines the failure condition
+    @param file The name of the source file with the condition
+    @param line Number of the line of @c file with the condition
+ */
+typedef void (*pubnub_assert_handler_t)(char const *s, char const *file, long line);
+
+/** This will install an assert handler. It can be one of the standard
+    ones, or your own.
+
+    @param handler The handler to install. If NULL, will install
+    pubnub_assert_handler_abort()
+ */
+void pubnub_assert_set_handler(pubnub_assert_handler_t handler);
+
+/** This handler will print a message formed from the parameters and
+    then go to infinite loop. Useful for debugging.
+*/
+void pubnub_assert_handler_loop(char const *s, char const *file, long line);
+
+/** This handler will print a message  formed from the parameters and
+    then abort (exit, end) the process. Useful for testing.
+ */
+void pubnub_assert_handler_abort(char const *s, char const *file, long line);
+
+/** This handler will print a message formed from the parameters and
+    that's it. Useful for getting data from a program execution "in
+    the field", where we must not stop or crash because of ASSERT
+    failure.
+ */
+void pubnub_assert_handler_printf(char const *s, char const *file, long line);
+
+
+#define PUBNUB_CTASTR2(pre,post,lex) pre ## post ## lex
+#define PUBNUB_CTASTR(pre,post,lex) PUBNUB_CTASTR2(pre,post,lex)
+
+#define PUBNUB_STATIC_ASSERT(cond) \
+    typedef struct { int PUBNUB_CTASTR(static_assertion_failed_,msg) : !!(cond); } \
+        PUBNUB_CTASTR(static_assertion_failed_,__FILE__,__LINE__)
+
+
+#endif /* !defined INC_PUBNUB_ASSERT */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_assert_std.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,51 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_assert.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+static pubnub_assert_handler_t m_handler;
+
+
+void pubnub_assert_set_handler(pubnub_assert_handler_t handler)
+{
+    if (handler == NULL) {
+        handler = pubnub_assert_handler_abort;
+    }
+    m_handler = handler;
+}
+
+
+void pubnub_assert_failed(char const *s, char const *file, long line)
+{
+    if (m_handler == NULL) {
+        m_handler = pubnub_assert_handler_abort;
+    }
+    m_handler(s, file, line);
+}
+
+
+static void report(char const *s, char const *file, long line)
+{
+    printf("Pubnub assert failed '%s', file '%s', line %ld\n", s, file, line);
+}
+
+
+void pubnub_assert_handler_loop(char const *s, char const *file, long line)
+{
+    report(s, file, line);
+    for (;;) continue;
+}
+
+
+void pubnub_assert_handler_abort(char const *s, char const *file, long line)
+{
+    report(s, file, line);
+    abort();
+}
+
+void pubnub_assert_handler_printf(char const *s, char const *file, long line)
+{
+    report(s, file, line);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_blocking_io.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,27 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_blocking_io.h"
+
+#include "pubnub_assert.h"
+#include "pubnub_internal.h"
+
+
+
+
+int pubnub_set_non_blocking_io(pubnub_t *p)
+{
+    if (PUBNUB_BLOCKING_IO_SETTABLE) {
+        p->options.use_blocking_io = false;
+        return 0;
+    }
+    return -1;
+}
+
+
+int  pubnub_set_blocking_io(pubnub_t *p)
+{
+    if (PUBNUB_BLOCKING_IO_SETTABLE) {
+        p->options.use_blocking_io = true;
+        return 0;
+    }
+    return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_blocking_io.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,90 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_BLOCKING_IO
+#define INC_PUBNUB_BLOCKING_IO
+
+
+#include "pubnub_api_types.h"
+
+#include <stdbool.h>
+
+
+/** @file pubnub_blocking_io.h 
+    This is the "Blocking I/O" API of the Pubnub client library.
+    Functions here influence the way that Pubnub client library
+    works with lower levels (the TCP/IP stack).
+
+    It is available on platforms that support setting blocking /
+    non-blocking behavior. Be aware that, even on such platforms,
+    most of these functions provide more of a hint, rather than
+    a command.
+*/
+
+
+/** Sets the usage of non-blocking I/O for a context. If non-blocking
+    I/O is supported by a platform, it will be used, unless some other
+    reason prevents it. 
+
+    The exact behavior when using non-blocking I/O depends on the
+    platform, but, in general:
+
+    - getting (or trying to get) the outcome of a Pubnub transaction
+    will not block the caller's thread
+
+    - if outcome is gotten by polling (calling a Punbub SDK API to get
+    the outcome), each poll will indicate whether the outcome is
+    reached or not, so user will have to "call until the outcome is
+    reached", though the user, is, of course, free to do other things
+    between two "poll calls"
+
+    - if outcome is gotten via a callback or similar means, it is most
+    likely that the actual I/O is done non-blocking anyway, but, in
+    any case, user would probably see little difference between
+    blocking and non-blocking I/O
+
+    In general, non-blocking I/O gives to more complex code, but that
+    scales better.
+
+    @pre Call this after pubnub_init() on the context
+    @param p The Context to set non-blocking I/O  for
+
+    @return 0: OK, otherwise: error, non-blocking I/O not supported
+    
+*/
+int pubnub_set_non_blocking_io(pubnub_t *p);
+
+
+/** Sets the usage of blocking I/O for a context. If blocking
+    I/O is supported by a platform, it will be used, unless some other
+    reason prevents it. 
+
+    The exact behavior when using blocking I/O depends on the
+    platform, but, in general:
+
+    - getting (or trying to get) the outcome of a Pubnub transaction
+    will block the caller's thread until the outcome is actually
+    reached
+
+    - if outcome is gotten by polling (calling a Punbub SDK API to get
+    the outcome), the user will call just once and the poll will
+    return when the outcome is reached (making it impossible for the
+    caller to do anything on that thread until the outcome is reached)
+
+    - if outcome is gotten via a callback or similar means, it is most
+    likely that the actual I/O is done non-blocking, but, in any case,
+    user would probably see little difference between blocking and
+    non-blocking I/O
+
+    In general, blocking I/O gives to simpler code, but that scales
+    poorly.
+
+    @pre Call this after pubnub_init() on the context
+    @param p The Context to set blocking I/O  for
+
+    @return 0: OK, otherwise: error, blocking I/O not supported
+    
+*/
+int  pubnub_set_blocking_io(pubnub_t *p);
+
+
+
+#endif /* defined INC_PUBNUB_BLOCKING_IO */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_ccore.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,748 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_ccore.h"
+#include "pubnub_version.h"
+#include "pubnub_assert.h"
+#include "pubnub_internal.h"
+#include "pubnub_json_parse.h"
+#include "pubnub_log.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+void pbcc_init(struct pbcc_context *p, const char *publish_key, const char *subscribe_key)
+{
+    p->publish_key = publish_key;
+    p->subscribe_key = subscribe_key;
+    p->timetoken[0] = '0';
+    p->timetoken[1] = '\0';
+    p->uuid = p->auth = NULL;
+    p->msg_ofs = p->msg_end = 0;
+    if (PUBNUB_DYNAMIC_REPLY_BUFFER) {
+        p->http_reply = NULL;
+    }
+
+#if PUBNUB_CRYPTO_API
+    p->secret_key = NULL;
+#endif
+}
+
+
+void pbcc_deinit(struct pbcc_context *p)
+{
+    if (PUBNUB_DYNAMIC_REPLY_BUFFER) {
+        if (p->http_reply != NULL) {
+            free(p->http_reply);
+            p->http_reply = NULL;
+        }
+    }
+}
+
+
+int pbcc_realloc_reply_buffer(struct pbcc_context *p, unsigned bytes)
+{
+#if PUBNUB_DYNAMIC_REPLY_BUFFER
+    char *newbuf = (char*)realloc(p->http_reply, bytes + 1);
+    if (NULL == newbuf) {
+        return -1;
+    }
+    p->http_reply = newbuf;
+    return 0;
+#else
+    if (bytes < sizeof p->http_reply / sizeof p->http_reply[0]) {
+        return 0;
+    }
+    return -1;
+#endif
+}
+
+
+char const *pbcc_get_msg(struct pbcc_context *pb)
+{
+    if (pb->msg_ofs < pb->msg_end) {
+        char const *rslt = pb->http_reply + pb->msg_ofs;
+        pb->msg_ofs += strlen(rslt);
+        if (pb->msg_ofs++ <= pb->msg_end) {
+            return rslt;
+        }
+    }
+
+    return NULL;
+}
+
+
+char const *pbcc_get_channel(struct pbcc_context *pb)
+{
+    if (pb->chan_ofs < pb->chan_end) {
+        char const* rslt = pb->http_reply + pb->chan_ofs;
+        pb->chan_ofs += strlen(rslt);
+        if (pb->chan_ofs++ <= pb->chan_end) {
+            return rslt;
+        }
+    }
+
+    return NULL;
+}
+
+
+void pbcc_set_uuid(struct pbcc_context *pb, const char *uuid)
+{
+    pb->uuid = uuid;
+}
+
+
+void pbcc_set_auth(struct pbcc_context *pb, const char *auth)
+{
+    pb->auth = auth;
+}
+
+
+/* Find the beginning of a JSON string that comes after comma and ends
+ * at @c &buf[len].
+ * @return position (index) of the found start or -1 on error. */
+static int find_string_start(char const *buf, int len)
+{
+    int i;
+    for (i = len-1; i > 0; --i) {
+        if (buf[i] == '"') {
+            return (buf[i-1] == ',') ? i : -1;
+        }
+    }
+    return -1;
+}
+
+
+/** Split @p buf string containing a JSON array (with arbitrary
+ * contents) to multiple NUL-terminated C strings, in-place.
+ */
+static bool split_array(char *buf)
+{
+    bool escaped = false;
+    bool in_string = false;
+    int bracket_level = 0;
+
+    for (; *buf != '\0'; ++buf) {
+        if (escaped) {
+            escaped = false;
+        }
+        else if ('"' == *buf) {
+            in_string = !in_string;
+        }
+        else if (in_string) {
+            escaped = ('\\' == *buf);
+        }
+        else {
+            switch (*buf) {
+            case '[': case '{': bracket_level++; break;
+            case ']': case '}': bracket_level--; break;
+                /* if at root, split! */
+            case ',': if (bracket_level == 0) { *buf = '\0'; } break;
+            default: break;
+            }
+        }
+    }
+
+    PUBNUB_LOG_TRACE("escaped = %d, in_string = %d, bracket_level = %d\n", escaped, in_string, bracket_level);
+    return !(escaped || in_string || (bracket_level > 0));
+}
+
+
+static int simple_parse_response(struct pbcc_context *p)
+{
+    char *reply = p->http_reply;
+    int replylen = p->http_buf_len;
+    if (replylen < 2) {
+        return -1;
+    }
+    if ((reply[0] != '[') || (reply[replylen-1] != ']')) {
+        return -1;
+    }
+
+    p->chan_ofs = 0;
+    p->chan_end = 0;
+
+    p->msg_ofs = 1;
+    p->msg_end = replylen - 1;
+    reply[replylen-1] = '\0';
+
+    return split_array(reply + p->msg_ofs) ? 0 : -1;
+}
+
+
+enum pubnub_res pbcc_parse_publish_response(struct pbcc_context *p)
+{
+    char *reply = p->http_reply;
+    int replylen = p->http_buf_len;
+    if (replylen < 2) {
+        return PNR_FORMAT_ERROR;
+    }
+    if ((reply[0] != '[') || (reply[replylen-1] != ']')) {
+        return PNR_FORMAT_ERROR;
+    }
+
+    p->chan_ofs = p->chan_end = 0;
+    p->msg_ofs = p->msg_end = 0;
+
+    reply[replylen-1] = '\0';
+
+    if (split_array(reply + 1)) {
+        if (1 != strtol(reply+1, NULL, 10)) {
+            return PNR_PUBLISH_FAILED;
+        }
+        return PNR_OK;
+    }
+    else {
+        return PNR_FORMAT_ERROR;
+    }
+}
+
+int pbcc_parse_time_response(struct pbcc_context *p)
+{
+    return simple_parse_response(p);
+}
+
+
+int pbcc_parse_history_response(struct pbcc_context *p)
+{
+    return simple_parse_response(p);
+}
+
+
+int pbcc_parse_presence_response(struct pbcc_context *p)
+{
+    char *reply = p->http_reply;
+    int replylen = p->http_buf_len;
+    if (replylen < 2) {
+        return -1;
+    }
+    if ((reply[0] != '{') || (reply[replylen-1] != '}')) {
+        return -1;
+    }
+
+    p->chan_ofs = p->chan_end = 0;
+
+    p->msg_ofs = 0;
+    p->msg_end = replylen;
+
+    return 0;
+}
+
+
+enum pubnub_res pbcc_parse_channel_registry_response(struct pbcc_context *p)
+{
+    enum pbjson_object_name_parse_result result;
+    struct pbjson_elem el;
+    struct pbjson_elem found;
+
+    el.start = p->http_reply;
+    el.end = p->http_reply + p->http_buf_len;
+    p->chan_ofs = 0;
+    p->chan_end = p->http_buf_len;
+
+    p->msg_ofs = p->msg_end = 0;
+
+    /* We should probably also check that there is a key "service"
+       with value "channel-registry".  Maybe even that there is a key
+       "status" (with value 200).
+    */
+    result = pbjson_get_object_value(&el, "error", &found);
+    if (jonmpOK == result) {
+        if (pbjson_elem_equals_string(&found, "false")) {
+            return PNR_OK;
+        }
+        else {
+            return PNR_CHANNEL_REGISTRY_ERROR;
+        }
+    }
+    else {
+        return PNR_FORMAT_ERROR;
+    }
+}
+
+
+int pbcc_parse_subscribe_response(struct pbcc_context *p)
+{
+    int i;
+    int previous_i;
+    unsigned time_token_length;
+    char *reply = p->http_reply;
+    int replylen = p->http_buf_len;
+    if (replylen < 2) {
+        return -1;
+    }
+    if (reply[replylen-1] != ']' && replylen > 2) {
+        replylen -= 2; /* XXX: this seems required by Manxiang */
+    }
+    if ((reply[0] != '[') || (reply[replylen-1] != ']') || (reply[replylen-2] != '"')) {
+        return -1;
+    }
+
+    /* Extract the last argument. */
+    previous_i = replylen - 2;
+    i = find_string_start(reply, previous_i);
+    if (i < 0) {
+        return -1;
+    }
+    reply[replylen - 2] = 0;
+
+    /* Now, the last argument may either be a timetoken, a channel group list
+        or a channel list. */
+    if (reply[i-2] == '"') {
+        int k;
+        /* It is a channel list, there is another string argument in front
+         * of us. Process the channel list ... */
+        for (k = replylen - 2; k > i+1; --k) {
+            if (reply[k] == ',') {
+                reply[k] = '\0';
+            }
+        }
+
+        /* The previous argument is either a timetoken or a channel group
+            list. */
+        reply[i-2] = '\0';
+        p->chan_ofs = i+1;
+        p->chan_end = replylen - 1;
+        previous_i = i-2;
+        i = find_string_start(reply, previous_i);
+        if (i < 0) {
+            p->chan_ofs = 0;
+            p->chan_end = 0;
+            return -1;
+        }
+        if (reply[i-2] == '"') {
+            /* It is a channel group list. For now, we shall skip it. In
+                the future, we may process it like we do the channel list.
+                */
+            reply[i-2] = '\0';
+            previous_i = i-2;
+            i = find_string_start(reply, previous_i);
+            if (i < 0) {
+                return -1;
+            }
+        }
+    }
+    else {
+        p->chan_ofs = 0;
+        p->chan_end = 0;
+    }
+
+    /* Now, `i` points to:
+     * [[1,2,3],"5678"]
+     * [[1,2,3],"5678","a,b,c"]
+     * [[1,2,3],"5678","gr-a,gr-b,gr-c","a,b,c"]
+     *          ^-- here */
+
+    /* Setup timetoken. */
+    time_token_length = previous_i - (i+1);
+    if (time_token_length >= sizeof p->timetoken) {
+        p->timetoken[0] = '\0';
+        return -1;
+    }
+    memcpy(p->timetoken, reply + i+1, time_token_length+1);
+    
+    /* terminate the [] message array (before the `]`!) */
+    reply[i-2] = 0; 
+
+    /* Set up the message list - offset, length and NUL-characters
+     * splitting the messages. */
+    p->msg_ofs = 2;
+    p->msg_end = i-2;
+
+    return split_array(reply + p->msg_ofs) ? 0 : -1;
+}
+
+
+static enum pubnub_res append_url_param(struct pbcc_context *pb, char const *param_name, size_t param_name_len, char const *param_val, char separator)
+{
+    size_t param_val_len = strlen(param_val);
+    if (pb->http_buf_len + 1 + param_name_len + 1 + param_val_len > sizeof pb->http_buf) {
+        return PNR_TX_BUFF_TOO_SMALL;
+    }
+
+    pb->http_buf[pb->http_buf_len++] = separator;
+    memcpy(pb->http_buf + pb->http_buf_len, param_name, param_name_len);
+    pb->http_buf_len += param_name_len;
+    pb->http_buf[pb->http_buf_len++] = '=';
+    memcpy(pb->http_buf + pb->http_buf_len, param_val, param_val_len+1);
+    pb->http_buf_len += param_val_len;
+
+    return PNR_OK;
+}
+
+
+#define APPEND_URL_PARAM_M(pbc, name, var, separator)                   \
+    if ((var) != NULL) {                                                \
+        const char param_[] = name;                                     \
+        enum pubnub_res rslt_ = append_url_param((pbc), param_, sizeof param_ - 1, (var), (separator)); \
+        if (rslt_ != PNR_OK) {                                          \
+            return rslt_;                                               \
+        }                                                               \
+    }
+
+
+#define APPEND_URL_PARAM_INT_M(pbc, name, var, separator)               \
+    do { char v_[20];                                                   \
+        snprintf(v_, sizeof v_, "%d", (var));                           \
+        APPEND_URL_PARAM_M(pbc, name, v_, separator);                   \
+    } while (0)
+
+
+#define APPEND_URL_PARAM_UNSIGNED_M(pbc, name, var, separator)          \
+    do { char v_[20];                                                   \
+        snprintf(v_, sizeof v_, "%u", (var));                           \
+        APPEND_URL_PARAM_M(pbc, name, v_, separator);                   \
+    } while (0)
+
+#define APPEND_URL_OPT_PARAM_UNSIGNED_M(pbc, name, var, separator)      \
+    if ((var) != NULL) { char v_[20];                                   \
+        snprintf(v_, sizeof v_, "%u", *(var));                          \
+        APPEND_URL_PARAM_M(pbc, name, v_, separator);                   \
+    } while (0)
+
+
+#define APPEND_URL_PARAM_BOOL_M(pbc, name, var, separator)              \
+    do { char const *v_ = (var) ? "true" : "false";                     \
+        APPEND_URL_PARAM_M(pbc, name, v_, separator);                   \
+    } while (0)
+
+#define APPEND_URL_PARAM_TRIBOOL_M(pbc, name, var, separator)           \
+    if ((var) != pbccNotSet) {                                          \
+        char const *v_ = (var) ? "1" : "0";                             \
+        APPEND_URL_PARAM_M(pbc, name, v_, separator);                   \
+    }
+
+
+enum pubnub_res pbcc_publish_prep(struct pbcc_context *pb, const char *channel, const char *message, bool store_in_history, bool eat_after_reading)
+{
+    char const *const uname = pubnub_uname();
+    char const *pmessage = message;
+    pb->http_content_len = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/publish/%s/%s/0/%s/0/",
+        pb->publish_key, pb->subscribe_key, channel
+        );
+
+
+    while (pmessage[0]) {
+        /* RFC 3986 Unreserved characters plus few
+         * safe reserved ones. */
+        size_t okspan = strspn(pmessage, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ",=:;@[]");
+        if (okspan > 0) {
+            if (okspan > sizeof(pb->http_buf)-1 - pb->http_buf_len) {
+                pb->http_buf_len = 0;
+                return PNR_TX_BUFF_TOO_SMALL;
+            }
+            memcpy(pb->http_buf + pb->http_buf_len, pmessage, okspan);
+            pb->http_buf_len += okspan;
+            pb->http_buf[pb->http_buf_len] = 0;
+            pmessage += okspan;
+        }
+        if (pmessage[0]) {
+            /* %-encode a non-ok character. */
+            char enc[4] = {'%'};
+            enc[1] = "0123456789ABCDEF"[pmessage[0] / 16];
+            enc[2] = "0123456789ABCDEF"[pmessage[0] % 16];
+            if (3 > sizeof pb->http_buf - 1 - pb->http_buf_len) {
+                pb->http_buf_len = 0;
+                return PNR_TX_BUFF_TOO_SMALL;
+            }
+            memcpy(pb->http_buf + pb->http_buf_len, enc, 4);
+            pb->http_buf_len += 3;
+            ++pmessage;
+        }
+    }
+    APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?');
+    APPEND_URL_PARAM_M(pb, "uuid", pb->uuid, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+    if (!store_in_history) {
+        APPEND_URL_PARAM_BOOL_M(pb, "store", store_in_history, '?');
+    }
+    if (eat_after_reading) {
+        APPEND_URL_PARAM_BOOL_M(pb, "ear", eat_after_reading, '?');
+    }
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_subscribe_prep(struct pbcc_context *p, const char *channel, const char *channel_group, unsigned *heartbeat)
+{
+    if (NULL == channel) {
+        if (NULL == channel_group) {
+            return PNR_INVALID_CHANNEL;
+        }
+        channel = ",";
+    }
+    if (p->msg_ofs < p->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    p->http_content_len = 0;
+    p->msg_ofs = p->msg_end = 0;
+
+    p->http_buf_len = snprintf(
+        p->http_buf, sizeof(p->http_buf),
+        "/subscribe/%s/%s/0/%s?pnsdk=%s",
+        p->subscribe_key, channel, p->timetoken,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(p, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(p, "uuid", p->uuid, '&');
+    APPEND_URL_PARAM_M(p, "auth", p->auth, '&');
+    APPEND_URL_OPT_PARAM_UNSIGNED_M(p, "heartbeat", heartbeat, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_leave_prep(struct pbcc_context *pb, const char *channel, const char *channel_group)
+{
+    if (NULL == channel) {
+        if (NULL == channel_group) {
+            return PNR_INVALID_CHANNEL;
+        }
+        channel = ",";
+    }
+    pb->http_content_len = 0;
+
+    /* Make sure next subscribe() will be a join. */
+    pb->timetoken[0] = '0';
+    pb->timetoken[1] = '\0';
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s/channel/%s/leave?pnsdk=%s",
+        pb->subscribe_key, channel,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(pb, "uuid", pb->uuid, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_time_prep(struct pbcc_context *pb)
+{
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_content_len = 0;
+    pb->msg_ofs = pb->msg_end = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/time/0?pnsdk=%s",
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "uuid", pb->uuid, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
+
+
+
+enum pubnub_res pbcc_history_prep(struct pbcc_context *pb, const char *channel, unsigned count, bool include_token)
+{
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_content_len = 0;
+    pb->msg_ofs = pb->msg_end = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/history/sub-key/%s/channel/%s?pnsdk=%s",
+        pb->subscribe_key, channel,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+    APPEND_URL_PARAM_UNSIGNED_M(pb, "count", count, '&');
+    APPEND_URL_PARAM_BOOL_M(pb, "include_token", include_token, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_heartbeat_prep(struct pbcc_context *pb, const char *channel, const char *channel_group)
+{
+    if (NULL == channel) {
+        if (NULL == channel_group) {
+            return PNR_INVALID_CHANNEL;
+        }
+        channel = ",";
+    }
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_content_len = 0;
+    pb->msg_ofs = pb->msg_end = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s/channel/%s/heartbeat?pnsdk=%s",
+        pb->subscribe_key,
+        channel,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+    APPEND_URL_PARAM_M(pb, "uuid", pb->uuid, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_here_now_prep(struct pbcc_context *pb, const char *channel, const char *channel_group, enum pbcc_tribool disable_uuids, enum pbcc_tribool state)
+{
+    if (NULL == channel) {
+        if (channel_group != NULL) {
+            channel = ",";
+        }
+    }
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_content_len = 0;
+    pb->msg_ofs = pb->msg_end = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s%s%s?pnsdk=%s",
+        pb->subscribe_key,
+        channel ? "/channel/" : "",
+        channel ? channel : "",
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+    APPEND_URL_PARAM_M(pb, "uuid", pb->uuid, '&');
+    APPEND_URL_PARAM_TRIBOOL_M(pb, "disable_uuids", disable_uuids, '&');
+    APPEND_URL_PARAM_TRIBOOL_M(pb, "state", state, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_where_now_prep(struct pbcc_context *pb, const char *uuid)
+{
+    PUBNUB_ASSERT_OPT(uuid != NULL);
+
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_content_len = 0;
+    pb->msg_ofs = pb->msg_end = 0;
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s/uuid/%s?pnsdk=%s",
+        pb->subscribe_key, uuid,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_set_state_prep(struct pbcc_context *pb, char const *channel, char const *channel_group, const char *uuid, char const *state)
+{
+    PUBNUB_ASSERT_OPT(uuid != NULL);
+    PUBNUB_ASSERT_OPT(state != NULL);
+
+    if (NULL == channel) {
+        if (NULL == channel_group) {
+            return PNR_INVALID_CHANNEL;
+        }
+        channel = ",";
+    }
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s/channel/%s/uuid/%s/data?pnsdk=%s&state=%s",
+        pb->subscribe_key, channel, uuid,
+        pubnub_uname(), state
+        );
+    APPEND_URL_PARAM_M(pb, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_state_get_prep(struct pbcc_context *pb, char const *channel, char const *channel_group, const char *uuid)
+{
+    PUBNUB_ASSERT_OPT(uuid != NULL);
+
+    if (NULL == channel) {
+        if (NULL == channel_group) {
+            return PNR_INVALID_CHANNEL;
+        }
+        channel = ",";
+    }
+    if (pb->msg_ofs < pb->msg_end) {
+        return PNR_RX_BUFF_NOT_EMPTY;
+    }
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v2/presence/sub-key/%s/channel/%s/uuid/%s?pnsdk=%s",
+        pb->subscribe_key, channel, uuid,
+        pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "channel-group", channel_group, '&');
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_remove_channel_group_prep(struct pbcc_context *pb, char const *channel_group)
+{
+    PUBNUB_ASSERT_OPT(channel_group != NULL);
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v1/channel-registration/sub-key/%s/channel-group/%s/remove?pnsdk=%s",
+        pb->subscribe_key, channel_group, pubnub_uname()
+        );
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
+
+
+enum pubnub_res pbcc_channel_registry_prep(struct pbcc_context *pb, char const *channel_group, char const *param, char const *channel)
+{
+    PUBNUB_ASSERT_OPT(channel_group != NULL);
+
+    pb->http_buf_len = snprintf(
+        pb->http_buf, sizeof pb->http_buf,
+        "/v1/channel-registration/sub-key/%s/channel-group/%s?pnsdk=%s",
+        pb->subscribe_key, channel_group, pubnub_uname()
+        );
+    if (NULL != param) {
+        enum pubnub_res rslt;
+        PUBNUB_ASSERT_OPT(channel != NULL);
+        rslt = append_url_param(pb, param, strlen(param), channel, '&');
+        if (rslt != PNR_OK) {
+            return rslt;
+        }
+    }
+    APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&');
+
+    return PNR_STARTED;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_ccore.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,251 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_CCORE
+#define      INC_PUBNUB_CCORE
+
+#include "pubnub_config.h"
+#include "pubnub_api_types.h"
+
+#include <stdbool.h>
+
+
+/** @file pubnub_ccore.h 
+
+    The "C core" module is the internal module of the Pubnub C
+    clients, shared by all. It has all the "generic" code (formatting,
+    parsing, HTTP...)  while individual C clients deal with
+    interacting with the environment (OS, frameworks, libraries...).
+
+    The interface of this module is subject to change, so user of a
+    Pubnub C client should *not* use this module directly. Use the
+    official and documented API for your environment.
+*/
+
+/** The 3-state bool. For Electrical Enginners among you, this would
+    be a digital line utilizing high impedance ("High Z"). For
+    mathematicians, it would be a "Maybe monad". For the rest of us,
+    it has `true`, `false` and the third `not set` or `indeterminate`
+    state.
+*/
+enum pbcc_tribool {
+    pbccFalse,
+    pbccTrue,
+    pbccNotSet
+};
+
+
+/** The Pubnub "(C) core" context, contains context data 
+    that is shared among all Pubnub C clients.
+ */
+struct pbcc_context {
+    /** The publish key (to use when publishing) */
+    char const *publish_key;
+    /** The subscribe key (to use when subscribing) */
+    char const *subscribe_key;
+    /** The UUID to be sent to server. If NULL, don't send any */
+    char const *uuid;
+    /** The `auth` parameter to be sent to server. If NULL, don't send
+     * any */
+    char const *auth;
+
+    /** The last used time token. */
+    char timetoken[20];
+
+    /** The result of the last Pubnub transaction */
+    enum pubnub_res last_result;
+
+    /** The "scratch" buffer for HTTP data */
+    char http_buf[PUBNUB_BUF_MAXLEN];
+
+    /** The length of the data currently in the HTTP buffer ("scratch"
+        or reply, depending on the state).
+     */
+    unsigned http_buf_len;
+
+    /** The total length of data to be received in a HTTP reply or
+        chunk of it.
+     */
+    unsigned http_content_len;
+
+#if PUBNUB_DYNAMIC_REPLY_BUFFER
+    char *http_reply;
+#else
+    /** The contents of a HTTP reply/reponse */
+    char http_reply[PUBNUB_REPLY_MAXLEN+1];
+#endif
+
+    /* These in-string offsets are used for yielding messages received
+     * by subscribe - the beginning of last yielded message and total
+     * length of message buffer.
+     */
+    unsigned msg_ofs, msg_end;
+
+    /* Like the offsets for the messages, these are the offsets for
+       the channels. Unlikey the message(s), the channels don't have
+       to be received at all.
+    */
+    unsigned chan_ofs, chan_end;
+
+#if PUBNUB_CRYPTO_API
+    /** Secret key to use for encryption/decryption */
+    char const *secret_key;
+#endif
+};
+
+
+/** Initializes the Pubnub C core context */
+void pbcc_init(struct pbcc_context *pbcc, const char *publish_key, const char *subscribe_key);
+
+/** Deinitializes the Pubnub C core context */
+void pbcc_deinit(struct pbcc_context *p);
+
+/** Reallocates the reply buffer in the C core context @p p to have
+    @p bytes.
+    @return 0: OK, allocated, -1: failed
+*/
+int pbcc_realloc_reply_buffer(struct pbcc_context *p, unsigned bytes);
+
+/** Returns the next message from the Pubnub C Core context. NULL if
+    there are no (more) messages
+*/
+char const *pbcc_get_msg(struct pbcc_context *pb);
+
+/** Returns the next channel from the Pubnub C Core context. NULL if
+    there are no (more) messages.
+*/
+char const *pbcc_get_channel(struct pbcc_context *pb);
+
+/** Sets the UUID for the context */
+void pbcc_set_uuid(struct pbcc_context *pb, const char *uuid);
+
+/** Sets the `auth` for the context */
+void pbcc_set_auth(struct pbcc_context *pb, const char *auth);
+
+/** Parses the string received as a response for a subscribe operation
+    (transaction). This checks if the response is valid, and, if it
+    is, prepares for giving the messages (and possibly channels) that
+    are received in the response to the user (via pbcc_get_msg() and
+    pbcc_get_channel()).
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return 0: OK, -1: error (invalid response)
+*/
+int pbcc_parse_subscribe_response(struct pbcc_context *p);
+
+/** Parses the string received as a response for a publish operation
+    (transaction). This checks if the response is valid, and, if it
+    is, enables getting it as the gotten message (like for
+    `subscribe`).
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return The result of the parsing, expressed as the "Pubnub
+    result" enum
+*/
+enum pubnub_res pbcc_parse_publish_response(struct pbcc_context *p);
+
+/** Parses the string received as a response for a time operation
+    (transaction). This checks if the response is valid, and, if it
+    is, enables getting it as the gotten message (like for
+    `subscribe`).
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return 0: OK, -1: error (invalid response)
+*/
+int pbcc_parse_time_response(struct pbcc_context *p);
+
+/** Parses the string received as a response for a history v2
+    operation (transaction). This checks if the response is valid,
+    and, if it is, enables getting the gotten message, as a JSON
+    array, and the timestamps for the first and last of them.
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return 0: OK, -1: error (invalid response)
+*/
+int pbcc_parse_history_response(struct pbcc_context *p);
+
+/** Parses the string received as a response for a presence query
+    operation (transaction). Presence query is done on several
+    user requests: "where-now", "here-now", etc. 
+
+    This checks if the response is valid (a JSON object), and, if it
+    is, enables getting it, as a whole, in one pubnub_get().
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return 0: OK, -1: error (invalid response)
+*/
+int pbcc_parse_presence_response(struct pbcc_context *p);
+
+/** Parses the string received as a response for a channel-registry
+    operation (transaction). It is done on several user requests
+    (add/remove channel (from/to) channel group, list (channels
+    in a) channel group, remove channel group).
+
+    This checks if the response is valid (a JSON object), and, if it
+    is, enables getting it, as a whole, in one pubnub_get().
+
+    @param p The Pubnub C core context to parse the response "in"
+    @return The result of the parsing, expressed as the "Pubnub
+    result" enum
+*/
+enum pubnub_res pbcc_parse_channel_registry_response(struct pbcc_context *p);
+
+/** Prepares the Publish operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_publish_prep(struct pbcc_context *pb, const char *channel, const char *message, bool store_in_history, bool eat_after_reading);
+
+/** Prepares the Subscribe operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_subscribe_prep(struct pbcc_context *p, const char *channel, const char *channel_group, unsigned *heartbeat);
+
+/** Prepares the Leave operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_leave_prep(struct pbcc_context *p, const char *channel, const char *channel_group);
+
+/** Prepares the Time operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_time_prep(struct pbcc_context *p);
+
+/** Prepares the History v2 operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_history_prep(struct pbcc_context *p, const char *channel, unsigned count, bool include_token);
+
+/** Prepares the Heartbeat operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_heartbeat_prep(struct pbcc_context *p, const char *channel, const char *channel_group);
+
+/** Prepares the Here-now operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_here_now_prep(struct pbcc_context *p, const char *channel, const char *channel_group, enum pbcc_tribool disable_uuids, enum pbcc_tribool state);
+
+/** Prepares the Where-now operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_where_now_prep(struct pbcc_context *p, const char *uuid);
+
+/** Prepares the Set state operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+ */
+enum pubnub_res pbcc_set_state_prep(struct pbcc_context *p, char const *channel, char const *channel_group, const char *uuid, char const *state);
+
+/** Prepares the Get state operation (transaction), mostly by
+    formatting the URI of the HTTP request.
+*/
+enum pubnub_res pbcc_state_get_prep(struct pbcc_context *p, char const *channel, char const *channel_group, const char *uuid);
+
+/** Preparse the Remove channel group operation (transaction) , mostly by
+    formatting the URI of the HTTP request.
+*/
+enum pubnub_res pbcc_remove_channel_group_prep(struct pbcc_context *p, char const *channel_group);
+
+/** Preparse an operation (transaction) against the channel registry,
+    mostly by formatting the URI of the HTTP request.
+*/
+enum pubnub_res pbcc_channel_registry_prep(struct pbcc_context *p, char const *channel_group, char const *param, char const *channel);
+
+#endif /* !defined INC_PUBNUB_CCORE */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_coreapi.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,612 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_coreapi.h"
+
+#include "pubnub_ccore.h"
+#include "pubnub_netcore.h"
+#include "pubnub_internal.h"
+#include "pubnub_assert.h"
+#include "pubnub_timers.h"
+
+#include "pbpal.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+
+pubnub_t* pubnub_init(pubnub_t *p, const char *publish_key, const char *subscribe_key)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(p));
+
+    pubnub_mutex_init(p->monitor);
+    pubnub_mutex_lock(p->monitor);
+    pbcc_init(&p->core, publish_key, subscribe_key);
+    if (PUBNUB_TIMERS_API) {
+        p->transaction_timeout_ms = PUBNUB_DEFAULT_TRANSACTION_TIMER;
+#if defined(PUBNUB_CALLBACK_API)
+        p->previous = p->next = NULL;
+#endif
+    }
+#if defined(PUBNUB_CALLBACK_API)
+    p->cb = NULL;
+    p->user_data = NULL;
+#endif
+    if (PUBNUB_ORIGIN_SETTABLE) {
+        p->origin = PUBNUB_ORIGIN;
+    }
+
+    p->state = PBS_IDLE;
+    p->trans = PBTT_NONE;
+    pbpal_init(p);
+    pubnub_mutex_unlock(p->monitor);
+
+#if PUBNUB_PROXY_API
+    p->proxy_type = pbproxyNONE;
+    p->proxy_hostname[0] = '\0';
+    p->proxy_tunnel_established = false;
+    p->proxy_port = 80;
+#endif
+
+    return p;
+}
+
+
+enum pubnub_res pubnub_publish(pubnub_t *pb, const char *channel, const char *message)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+
+    rslt = pbcc_publish_prep(&pb->core, channel, message, true, false);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_PUBLISH;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    pubnub_mutex_unlock(pb->monitor);
+    
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_publishv2(pubnub_t *pb, const char *channel, const char *message, bool store_in_history, bool eat_after_reading)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+
+    rslt = pbcc_publish_prep(&pb->core, channel, message, store_in_history, eat_after_reading);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_PUBLISH;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+char const *pubnub_get(pubnub_t *pb)
+{
+    char const *result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    result = pbcc_get_msg(&pb->core);
+    pubnub_mutex_unlock(pb->monitor);
+
+    return result;
+}
+
+
+char const *pubnub_get_channel(pubnub_t *pb)
+{
+    char const *result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    result = pbcc_get_channel(&pb->core);
+    pubnub_mutex_unlock(pb->monitor);
+
+    return result;
+}
+
+
+enum pubnub_res pubnub_subscribe(pubnub_t *p, const char *channel, const char *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(p));
+    
+    pubnub_mutex_lock(p->monitor);
+    if (p->state != PBS_IDLE) {
+        pubnub_mutex_unlock(p->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_subscribe_prep(&p->core, channel, channel_group, NULL);
+    if (PNR_STARTED == rslt) {
+        p->trans = PBTT_SUBSCRIBE;
+        p->core.last_result = PNR_STARTED;
+        pbnc_fsm(p);
+        rslt = p->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(p->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_leave(pubnub_t *p, const char *channel, const char *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(p));
+
+    pubnub_mutex_lock(p->monitor);
+    if (p->state != PBS_IDLE) {
+        pubnub_mutex_unlock(p->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_leave_prep(&p->core, channel, channel_group);
+    if (PNR_STARTED == rslt) {
+        p->trans = PBTT_LEAVE;
+        p->core.last_result = PNR_STARTED;
+        pbnc_fsm(p);
+        rslt = p->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(p->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_time(pubnub_t *p)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(p));
+
+    pubnub_mutex_lock(p->monitor);
+    if (p->state != PBS_IDLE) {
+        pubnub_mutex_unlock(p->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_time_prep(&p->core);
+    if (PNR_STARTED == rslt) {
+        p->trans = PBTT_TIME;
+        p->core.last_result = PNR_STARTED;
+        pbnc_fsm(p);
+        rslt = p->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(p->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_history(pubnub_t *pb, const char *channel, unsigned count, bool include_token)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_history_prep(&pb->core, channel, count, include_token);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_HISTORY;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_heartbeat(pubnub_t *pb, const char *channel, const char *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_heartbeat_prep(&pb->core, channel, channel_group);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_HEARTBEAT;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_here_now(pubnub_t *pb, const char *channel, const char *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_here_now_prep(&pb->core, channel, channel_group, pbccNotSet, pbccNotSet);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_HERENOW;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_global_here_now(pubnub_t *pb)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_here_now_prep(&pb->core, NULL, NULL, pbccNotSet, pbccNotSet);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_GLOBAL_HERENOW;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_where_now(pubnub_t *pb, const char *uuid)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_where_now_prep(&pb->core, uuid ? uuid : pb->core.uuid);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_WHERENOW;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_set_state(pubnub_t *pb, char const *channel, char const *channel_group, const char *uuid, char const *state)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_set_state_prep(&pb->core, channel, channel_group, uuid ? uuid : pb->core.uuid, state);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_SET_STATE;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_state_get(pubnub_t *pb, char const *channel, char const *channel_group, const char *uuid)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_state_get_prep(&pb->core, channel, channel_group, uuid ? uuid : pb->core.uuid);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_STATE_GET;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_remove_channel_group(pubnub_t *pb, char const *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_remove_channel_group_prep(&pb->core, channel_group);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_REMOVE_CHANNEL_GROUP;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_remove_channel_from_group(pubnub_t *pb, char const *channel, char const *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_channel_registry_prep(&pb->core, channel_group, "remove", channel);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_REMOVE_CHANNEL_FROM_GROUP;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_add_channel_to_group(pubnub_t *pb, char const *channel, char const *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_channel_registry_prep(&pb->core, channel_group, "add", channel);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_ADD_CHANNEL_TO_GROUP;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+enum pubnub_res pubnub_list_channel_group(pubnub_t *pb, char const *channel_group)
+{
+    enum pubnub_res rslt;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (pb->state != PBS_IDLE) {
+        pubnub_mutex_unlock(pb->monitor);
+        return PNR_IN_PROGRESS;
+    }
+    
+    rslt = pbcc_channel_registry_prep(&pb->core, channel_group, NULL, NULL);
+    if (PNR_STARTED == rslt) {
+        pb->trans = PBTT_LIST_CHANNEL_GROUP;
+        pb->core.last_result = PNR_STARTED;
+        pbnc_fsm(pb);
+        rslt = pb->core.last_result;
+    }
+    
+    pubnub_mutex_unlock(pb->monitor);
+    return rslt;
+}
+
+
+void pubnub_cancel(pubnub_t *pb)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    pbnc_stop(pb, PNR_CANCELLED);
+    pubnub_mutex_unlock(pb->monitor);
+}
+
+
+void pubnub_set_uuid(pubnub_t *pb, const char *uuid)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    pubnub_mutex_lock(pb->monitor);
+    pbcc_set_uuid(&pb->core, uuid);
+    pubnub_mutex_unlock(pb->monitor);
+}
+
+
+char const *pubnub_uuid_get(pubnub_t *pb)
+{
+    char const *result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    result = pb->core.uuid;
+    pubnub_mutex_unlock(pb->monitor);
+
+    return result;
+}
+
+
+void pubnub_set_auth(pubnub_t *pb, const char *auth)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    pubnub_mutex_lock(pb->monitor);
+    pbcc_set_auth(&pb->core, auth);
+    pubnub_mutex_unlock(pb->monitor);
+}
+
+
+char const *pubnub_auth_get(pubnub_t *pb)
+{
+    char const *result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    result = pb->core.auth;
+    pubnub_mutex_unlock(pb->monitor);
+
+    return result;
+}
+
+
+int pubnub_last_http_code(pubnub_t *pb)
+{
+    int result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    pubnub_mutex_lock(pb->monitor);
+    result = pb->http_code;
+    pubnub_mutex_unlock(pb->monitor);
+    return result;
+}
+
+
+char const *pubnub_last_time_token(pubnub_t *pb)
+{
+    char const *result;
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    result = pb->core.timetoken;
+    pubnub_mutex_unlock(pb->monitor);
+
+    return result;
+}
+
+
+char const *pubnub_last_publish_result(pubnub_t *pb)
+{
+    char *end;
+
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+
+    pubnub_mutex_lock(pb->monitor);
+    if (PUBNUB_DYNAMIC_REPLY_BUFFER && (NULL == pb->core.http_reply)) {
+        pubnub_mutex_unlock(pb->monitor);
+        return "";
+    }
+    if ((pb->trans != PBTT_PUBLISH) || (pb->core.http_reply[0] == '\0')) {
+        pubnub_mutex_unlock(pb->monitor);
+        return "";
+    }
+    for (end = pb->core.http_reply + 1; isdigit((unsigned)*end); ++end) {
+        continue;
+    }
+    pubnub_mutex_unlock(pb->monitor);
+
+    return end + 1;
+}
+
+
+char const *pubnub_get_origin(pubnub_t *pb)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    if (PUBNUB_ORIGIN_SETTABLE) {
+        char const *result;
+
+        pubnub_mutex_lock(pb->monitor);
+        result = pb->origin;
+        pubnub_mutex_unlock(pb->monitor);
+
+        return result;
+    }
+    return PUBNUB_ORIGIN;
+}
+
+
+int pubnub_origin_set(pubnub_t *pb, char const *origin)
+{
+    PUBNUB_ASSERT(pb_valid_ctx_ptr(pb));
+    if (PUBNUB_ORIGIN_SETTABLE) {
+        if (NULL == origin) {
+            origin = PUBNUB_ORIGIN;
+        }
+
+        pubnub_mutex_lock(pb->monitor);
+        pb->origin = origin;
+        pubnub_mutex_unlock(pb->monitor);
+
+        return 0;
+    }
+    return -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_coreapi.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,640 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_COREAPI
+#define INC_PUBNUB_COREAPI
+
+
+#include "pubnub_api_types.h"
+
+#include <stdbool.h>
+
+
+/** @file pubnub_coreapi.h 
+    This is the "Core" API of the Pubnub client library.
+    It has the functions that are present in all variants and
+    platforms and have the same interface in all of them.
+    For the most part, they have the same implementation in
+    all of them, too.
+*/
+
+
+/** Append this to a name of the channel to form the name of its
+    "presence" channel. A "presence" channel is a pseudo-channel on
+    which notifications of presence changes of a channel are announced
+    (sent by the Pubnub network). These notifications are JSON objects
+    with the following keys:
+
+    - "action": the event that happened, can be "leave", "join", "timeout",
+    "state-change"
+    - "timestamp": the timestamp of the moment the event happened
+    - "uuid": ID of the user that the event pertains to
+    - "occupancy": current number of present users in the channel
+
+    There is no special support for these (pseudo) channels in our
+    Pubnub client. If you wish to receive presence events, simply
+    append this suffix to the name of the channel and subscribe to
+    that "combined" name. For example, to receive presence events on
+    channel "my_channel", subscribe to "my_channel-pnpres".
+
+    Actually, you can subscribe to both a "regular" channel and a
+    "presence" channel at the same time, and you'll receive both the
+    presence events (on the "presence channel") and the published
+    messages (on the "regular" channel).
+ */
+#define PUBNUB_PRESENCE_SUFFIX "-pnpres"
+
+/** Initialize a given pubnub context @p p to the @p publish_key and @p
+    subscribe_key. You can customize other parameters of the context by
+    the configuration function calls below.  
+
+    @note The @p publish_key and @p subscribe key are expected to be
+    valid (ASCIIZ string) pointers throughout the use of context @p p,
+    that is, until either you call pubnub_done(), or the otherwise
+    stop using it (like when the whole software/ firmware stops
+    working). So, the contents of these keys are not copied to the
+    Pubnub context @p p.
+
+    @pre Call this after TCP initialization.
+    @pre @p subscribe_key can't be NULL
+    @param p The Context to initialize (use pubnub_alloc() to
+    obtain it)
+    @param publish_key The string of the key to use when publishing
+    messages (if you don't want to publish, you can pass NULL)
+    @param subscribe_key The string of the key to use when subscribing
+    to messages
+    @return Returns the @p p context
+*/
+pubnub_t* pubnub_init(pubnub_t *p, const char *publish_key, const char *subscribe_key);
+
+/** Set the UUID identification of PubNub client context @p p to @p
+    uuid. Pass NULL to unset.
+
+    @note The @p uuid is expected to be valid (ASCIIZ string) pointers
+    throughout the use of context @p p, that is, until either you call
+    pubnub_done() on @p p, or the otherwise stop using it (like when
+    the whole software/ firmware stops working). So, the contents of
+    the @p uuid string is not copied to the Pubnub context @p p.  */
+void pubnub_set_uuid(pubnub_t *p, const char *uuid);
+
+/** Get the UUID identification of PubNub client context @p p.
+    After pubnub_init(), it will return `NULL` until you change it
+    to non-`NULL` via pubnub_set_uuid().
+    */
+char const *pubnub_uuid_get(pubnub_t *p);
+
+/** Set the authentication information of PubNub client context @p
+    p. Pass NULL to unset.
+
+    @note The @p auth is expected to be valid (ASCIIZ string) pointers
+    throughout the use of context @p p, that is, until either you call
+    pubnub_done() on @p p, or the otherwise stop using it (like when
+    the whole software/ firmware stops working). So, the contents of
+    the auth string is not copied to the Pubnub context @p p.  */
+void pubnub_set_auth(pubnub_t *p, const char *auth);
+
+/** Returns the current authentication information for the
+    context @p p.
+    After pubnub_init(), it will return `NULL` until you change it
+    to non-`NULL` via pubnub_set_auth().
+*/
+char const *pubnub_auth_get(pubnub_t *p);
+
+/** Cancel an ongoing API transaction. The outcome of the transaction
+    in progress, if any, will be #PNR_CANCELLED. */
+void pubnub_cancel(pubnub_t *p);
+
+/** Publish the @p message (in JSON format) on @p p channel, using the
+    @p p context. This actually means "initiate a publish
+    transaction". 
+
+    You can't publish if a transaction is in progress in @p p context.
+
+    If transaction is not successful (@c PNR_PUBLISH_FAILED), you can 
+    get the string describing the reason for failure by calling
+    pubnub_last_publish_result().
+
+    Keep in mind that the time token from the publish operation
+    response is _not_ parsed by the library, just relayed to the
+    user. Only time-tokens from the subscribe operation are parsed
+    by the library.
+
+    Also, for all error codes known at the time of this writing, the
+    HTTP error will be set also, so the result of the Pubnub operation
+    will not be @c PNR_OK (but you will still be able to get the
+    result code and the description).
+
+    @param p The pubnub context. Can't be NULL
+    @param channel The string with the channel (or comma-delimited list
+    of channels) to publish to.
+    @param message The message to publish, expected to be in JSON format
+
+    @return #PNR_STARTED on success, an error otherwise
+ */
+enum pubnub_res pubnub_publish(pubnub_t *p, const char *channel, const char *message);
+
+/** Publish the @p message (in JSON format) on @p p channel, using the
+    @p p context, utilizing the v2 API. This actually means "initiate
+    a publish transaction".
+
+    Basically, this is an extension to the pubnub_publish() (v1),
+    with some additional options.
+
+    You can't publish if a transaction is in progress in @p p context.
+
+    @param p The pubnub context. Can't be NULL
+    @param channel The string with the channel (or comma-delimited list
+    of channels) to publish to.
+    @param message The message to publish, expected to be in JSON format
+    @param store_in_history If `false`, message will not be stored in
+    history of the channel
+    @param eat_after_reading If `true`, message will not be stored for
+    delayed or repeated retrieval or display
+
+    @return #PNR_STARTED on success, an error otherwise
+ */
+enum pubnub_res pubnub_publishv2(pubnub_t *p, const char *channel, const char *message, bool store_in_history, bool eat_after_reading);
+
+/** Returns a pointer to an arrived message or other element of the
+    response to an operation/transaction. Message(s) arrive on finish
+    of a subscribe operation or history operation, while for some
+    other operations this will give access to the whole response,
+    or the next element of the response. That is documented in
+    the function that starts the operation.
+
+    Subsequent call to this function will return the next message (if
+    any). All messages are from the channel(s) the last operation was
+    for.
+
+    @note Context doesn't keep track of the channel(s) you subscribed
+    to. This is a memory saving design decision, as most users won't
+    change the channel(s) they subscribe too.
+
+    @param p The Pubnub context. Can't be NULL.
+
+    @return Pointer to the message, NULL on error
+    @see pubnub_subscribe
+ */
+char const* pubnub_get(pubnub_t *p);
+
+/** Returns a pointer to an fetched subscribe operation/transaction's
+    next channel.  Each transaction may hold a list of channels, and
+    this functions provides a way to read them.  Subsequent call to
+    this function will return the next channel (if any).
+
+    @note You don't have to read all (or any) of the channels before
+    you start a new transaction.
+
+    @param pb The Pubnub context. Can't be NULL.
+
+    @return Pointer to the channel, NULL on error
+    @see pubnub_subscribe
+    @see pubnub_get
+ */
+char const *pubnub_get_channel(pubnub_t *pb);
+
+/** Subscribe to @p channel and/or @p channel_group. This actually
+    means "initiate a subscribe operation/transaction". The outcome
+    will be retrieved by the "notification" API, which is different
+    for different platforms. There are two APIs that are widely
+    available - "sync" and "callback".
+
+    Messages published on @p channel and/or @p channel_group since the
+    last subscribe transaction will be fetched, unless this is the
+    first subscribe on this context after initialization or a serious
+    error. In that "first" case, this will just retrieve the current
+    time token, and that event is called "connect" in many pubnub
+    SDKs, but in C-core we don't treat it any different. For that
+    "first" case, you will receive a notification that subscribe has
+    finished OK, but there will be no messages in the reply.
+
+    The @p channel and @p channel_group strings may contain multiple
+    comma-separated channel (channel group) names, so only one call is
+    needed to fetch messages from multiple channels (channel groups).
+
+    If @p channel is NULL, then @p channel_group cannot be NULL and
+    you will subscribe only to the channel group(s). It goes both
+    ways: if @p channel_group is NULL, then @p channel cannot be NULL
+    and you will subscribe only to the channel(s).
+
+    You can't subscribe if a transaction is in progress on the context.
+
+    Also, you can't subscribe if there are unread messages in the
+    context (you read messages with pubnub_get()).
+
+    @param p The pubnub context. Can't be NULL
+    @param channel The string with the channel name (or comma-delimited list
+    of channel names) to subscribe to.
+    @param channel_group The string with the channel group name (or
+    comma-delimited list of channel group names) to subscribe to.
+
+    @return #PNR_STARTED on success, an error otherwise
+    
+    @see pubnub_get
+ */
+enum pubnub_res pubnub_subscribe(pubnub_t *p, const char *channel, const char *channel_group);
+
+/** Leave the @p channel. This actually means "initiate a leave
+    transaction".  You should leave channel(s) when you want to
+    subscribe to another in the same context to avoid loosing
+    messages. Also, it is useful for tracking presence.
+
+    You can't leave if a transaction is in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL.  
+    @param channel The string with the channel name (or
+    comma-delimited list of channel names) to leave from.
+    @param channel_group The string with the channel group name (or
+    comma-delimited list of channel group names) to leave from.
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_leave(pubnub_t *p, const char *channel, const char *channel_group);
+
+/** Get the current Pubnub time token . This actually means "initiate
+    a time transaction". Since time token is in the response to most
+    Pubnub REST API calls, this is reserved mostly when you want to
+    get a high-quality seed for a random number generator, or some
+    such thing.
+
+    If transaction is successful, the gotten time will be the only
+    message you can get with pubnub_get(). It will be a (large) JSON
+    integer.
+    
+    You can't get time if a transaction is in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL.  
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_time(pubnub_t *p);
+
+/** Get the message history for the @p channel. This actually
+    means "initiate a history transaction/operation".
+
+    If transaction is successful, the gotten messages will be
+    available via the pubnub_get().  Using pubnub_get() will give
+    you exactly three messages (or, rather, elements).  The first will
+    be a JSON array of gotten messages, and the second and third will be
+    the timestamps of the first and the last message from that array.
+
+    Also, if you select to @c include_token, then the JSON array
+    you get will not be a simple array of gotten messages, but
+    rather an array of JSON objects, having keys `message` with
+    value the actual message, and `timetoken` with the time token
+    of that particular message.
+    
+    You can't get history if a transaction is in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The string with the channel name to get message 
+    history for. This _can't_ be a comma separated list of channels.
+    @param count Maximum number of messages to get. If there are less
+    than this available on the @c channel, you'll get less, but you
+    can't get more.
+    @param include_token If true, include the time token for every
+    gotten message
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_history(pubnub_t *p, const char *channel, unsigned count, bool include_token);
+
+/** Inform Pubnub that we're still working on @p channel and/or @p
+    channel_group.  This actually means "initiate a heartbeat
+    transaction". It can be thought of as an update against the
+    "presence database".
+
+    If transaction is successful, the response will be a available
+    via pubnub_get() as one message, a JSON object. Following keys
+    are always present:
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "message": the string/message describing the status ("OK"...)
+    - "service": should be "Presence"
+
+    If @p channel is NULL, then @p channel_group cannot be NULL and
+    you will subscribe only to the channel group(s). It goes both ways:
+    if @p channel_group is NULL, then @p channel cannot be NULL and
+    you will subscribe only to the channel(s).
+    
+    You can't get list of currently present users if a transaction is
+    in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The string with the channel name (or
+    comma-delimited list of channel names) to get presence info for.
+    @param channel_group The string with the channel name (or
+    comma-delimited list of channel group names) to get presence info for.
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_heartbeat(pubnub_t *p, const char* channel, const char* channel_group);
+
+/** Get the currently present users on a @p channel and/or @p
+    channel_group. This actually means "initiate a here_now
+    transaction". It can be thought of as a query against the
+    "presence database".
+
+    If transaction is successful, the response will be a available
+    via pubnub_get() as one message, a JSON object. Following keys
+    are always present:
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "message": the string/message describing the status ("OK"...)
+    - "service": should be "Presence"
+
+    If doing a query on a single channel, following keys are present:
+    - "uuids": an array of UUIDs of currently present users
+    - "occupancy": the number of currently present users in the channel
+
+    If doing a query on more channels, a key "payload" is present,
+    which is a JSON object whose keys are:
+
+    - "channels": a JSON object with keys being the names of the
+    channels and their values JSON objects with keys "uuids" and
+    "occupancy" with the meaning the same as for query on a single
+    channel
+    - "total_channels": the number of channels for which the
+    presence is given (in "payload")
+    - "total_occupancy": total number of users present in all channels
+
+    If @p channel is NULL, then @p channel_group cannot be NULL and
+    you will subscribe only to the channel group(s). It goes both ways:
+    if @p channel_group is NULL, then @p channel cannot be NULL and
+    you will subscribe only to the channel(s).
+    
+    You can't get list of currently present users if a transaction is
+    in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The string with the channel name (or
+    comma-delimited list of channel names) to get presence info for.
+    @param channel_group The string with the channel name (or
+    comma-delimited list of channel group names) to get presence info for.
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_here_now(pubnub_t *p, const char *channel, const char *channel_group);
+
+
+/** Get the currently present users on all channel. This actually
+    means "initiate a global here_now transaction". It can be thought
+    of as a query against the "presence database".
+
+    If transaction is successful, the response will be the same
+    as for "multi-channel" response for pubnub_here_now(), if
+    we queried against all currently available channels.
+    
+    You can't get list of currently present users if a transaction is
+    in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_global_here_now(pubnub_t *p);
+
+/** Get the currently present users on a @p channel and/or @p
+    channel_group. This actually means "initiate a here_now
+    transaction". It can be thought of as a query against the
+    "presence database".
+
+    If transaction is successful, the response will be a available
+    via pubnub_get() as one message, a JSON object with keys:
+
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "message": the string/message describing the status ("OK"...)
+    - "service": should be "Presence"
+    - "payload": JSON object with a key "channels" which is an
+    array of channels this user is present in
+
+    You can't get channel presence for the user if a transaction is
+    in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param uuid The UUID of the user to get the channel presence.
+    If NULL, the current UUID of the @c p context will be used.
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_where_now(pubnub_t *p, const char *uuid);
+
+/** Sets some state for the @p channel and/or @channel_group for a
+    user, identified by @p uuid. This actually means "initiate a set
+    state transaction". It can be thought of as an update against the
+    "presence database".
+
+    "State" has to be a JSON object (IOW, several "key-value" pairs).
+
+    If transaction is successful, the response will be a available
+    via pubnub_get() as one message, a JSON object with following keys:
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "message": the string/message describing the status ("OK"...)
+    - "service": should be "Presence"
+    - "payload" the state
+
+    This will set the same state to all channels identified by
+    @p channel and @p channel_group.
+
+    If @p channel is NULL, then @p channel_group cannot be NULL and
+    you will set state only to the channel group(s). It goes both
+    ways: if @p channel_group is NULL, then @p channel cannot be NULL
+    and you will set state only to the channel(s).
+
+    You can't set state of channels if a transaction is in progress on
+    the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The string with the channel name (or
+    comma-delimited list of channel names) to set state for.
+    @param channel_group The string with the channel name (or
+    comma-delimited list of channel group names) to set state for.
+    @param uuid The UUID of the user for which to set state for.
+    If NULL, the current UUID of the @c p context will be used.
+    @param state Has to be a JSON object
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_set_state(pubnub_t *p, char const *channel, char const *channel_group, const char *uuid, char const *state);
+
+
+/** Gets some state for the @p channel and/or @p channel_group for a
+    user, identified by @p uuid. This actually means "initiate a get
+    state transaction". It can be thought of as a query against the
+    "presence database".
+
+    If transaction is successful, the response will be a available
+    via pubnub_get() as one message, a JSON object with following keys:
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "message": the string/message describing the status ("OK"...)
+    - "service": should be "Presence"
+    - "payload": if querying against one channel the gotten state 
+    (a JSON object), otherwise a JSON object with the key "channels"
+    whose value is a JSON object with keys the name of the channels
+    and their respective values JSON objects of the gotten state
+
+    If @p channel is NULL, then @p channel_group cannot be NULL and
+    you will get state only for the channel group(s). It goes both
+    ways: if @p channel_group is NULL, then @p channel cannot be NULL
+    and you will get state only for the channel(s).
+
+    You can't get state of channel(s) if a transaction is in progress
+    on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The string with the channel name (or
+    comma-delimited list of channel names) to get state from.
+    @param channel_group The string with the channel name (or
+    comma-delimited list of channel group names) to get state from.
+    @param uuid The UUID of the user for which to get state for.
+    If NULL, the current UUID of the @p p context will be used.
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_state_get(pubnub_t *p, char const *channel, char const *channel_group, const char *uuid);
+
+/** Removes a @p channel_group and all its channels. This actually
+    means "initiate a remove_channel_group transaction". It can be
+    thought of as an update against the "channel group database".
+
+    If transaction is successful, the response will be a available via
+    pubnub_get_channel() as one "channel", a JSON object with keys:
+
+    - "service": should be "channel-registry"
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "error": true on error, false on success
+    - "message": the string/message describing the status ("OK"...)
+
+    You can't remove a channel group if a transaction is in progress
+    on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel_group The channel group to remove
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_remove_channel_group(pubnub_t *p, char const *channel_group);
+
+/** Removes a @p channel from the @p channel_group . This actually
+    means "initiate a remove_channel_from_channel_group
+    transaction". It can be thought of as an update against the
+    "channel group database".
+
+    You can't remove the last channel from a channel group. To do
+    that, remove the channel group itself.
+
+    If transaction is successful, the response will be a available via
+    pubnub_get_channel() as one "channel", a JSON object with keys:
+
+    - "service": should be "channel-registry"
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "error": true on error, false on success
+    - "message": the string/message describing the status ("OK"...)
+
+    You can't remove a channel from a channel group if a transaction
+    is in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel_group The channel to remove
+    @param channel_group The channel group to remove from
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_remove_channel_from_group(pubnub_t *p, char const *channel, char const *channel_group);
+
+/** Adds a @p channel to the @p channel_group . This actually means
+    "initiate a add_channel_to_channel_group transaction". It can be
+    thought of as an update against the "channel group database".
+
+    If the channel group doesn't exist, this implicitly adds (creates)
+    it.
+
+    If transaction is successful, the response will be a available
+    via pubnub_get_channel() as one "channel", a JSON object with keys:
+
+    - "service": should be "channel-registry"
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "error": true on error, false on success
+    - "message": the string/message describing the status ("OK"...)
+
+    You can't add a channel to a channel group if a transaction
+    is in progress on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel The channel to add
+    @param channel_group The channel group to add to
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_add_channel_to_group(pubnub_t *p, char const *channel, char const *channel_group);
+
+/** Lists all channels of a @p channel_group. This actually
+    means "initiate a list_channel_group transaction". It can be
+    thought of as a query against the "channel group database".
+
+    If transaction is successful, the response will be a available via
+    pubnub_get_channel() as one "channel", a JSON object with keys:
+
+    - "service": should be "channel-registry"
+    - "status": the HTTP status of the operation (200 OK, 40x error, etc.)
+    - "error": true on error, false on success
+    - "payload": JSON object with keys "group" with value the string
+    of the channel group name and "channels" with value a JSON array
+    of strings with names of the channels that belong to the group
+
+    You can't remove a channel group if a transaction is in progress
+    on the context.
+
+    @param p The Pubnub context. Can't be NULL. 
+    @param channel_group The channel group to list
+
+    @return #PNR_STARTED on success, an error otherwise
+*/
+enum pubnub_res pubnub_list_channel_group(pubnub_t *p, char const *channel_group);
+
+/** Returns the result of the last transaction in the @p p context.
+    This _may_ block if using blocking I/O. It will _not_ block if using
+    non-blocking I/O.  
+
+    @see pubnub_set_blocking_io
+    @see pubnub_set_non_blocking_io
+*/
+enum pubnub_res pubnub_last_result(pubnub_t *p);
+
+/** Returns the HTTP reply code of the last transaction in the @p p
+ * context. */
+int pubnub_last_http_code(pubnub_t *p);
+
+/** Returns the string of the result of the last `publish` transaction,
+    as returned from Pubnub. If the last transaction is not a publish,
+    or there is some other error, it returns NULL. If the Publish
+    was successfull, it will return "Sent", otherwise a description
+    of the error.
+ */
+char const *pubnub_last_publish_result(pubnub_t *p);
+
+/** Returns the string of the last received time token on the
+    @c p context. After pubnub_init() this should be "0".
+    @param p Pubnub context to get the last received time token from
+    @return A read only string of the last received time token
+ */
+char const *pubnub_last_time_token(pubnub_t *p);
+
+/** Gets the origin to be used for the context @p p.
+    If setting of the origin is not enabled, this will return
+    the default origin.
+    @param p Pubnub context to get the origin from
+    @return A read only string of origin used for context @p p
+ */
+char const *pubnub_get_origin(pubnub_t *p);
+
+/** Sets the origin to be used for the context @p p.  If setting of
+    the origin is not enabled, this will fail.  It may also fail if it
+    detects an invalid origin, but NULL is not an invalid origin - it
+    resets the origin to default.
+
+    @param p Pubnub context to set the origin for
+    @param origin The origin to use for context @p p. If NULL,
+    the default origin will be set
+    @return 0: success, -1: fail
+*/
+int pubnub_origin_set(pubnub_t *p, char const *origin);
+
+
+#endif /* defined INC_PUBNUB_COREAPI */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_internal_common.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,220 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_INTERNAL_COMMON
+#define      INC_PUBNUB_INTERNAL_COMMON
+
+#include "pubnub_config.h"
+#include "pubnub_ccore.h"
+#include "pubnub_netcore.h"
+#include "pubnub_mutex.h"
+
+#if defined(PUBNUB_CALLBACK_API)
+#include "pubnub_ntf_callback.h"
+#endif
+
+#if !defined PUBNUB_PROXY_API
+#define PUBNUB_PROXY_API 0
+#elif PUBNUB_PROXY_API
+#include "pubnub_proxy.h"
+#endif
+
+#include <stdint.h>
+
+
+#if !defined PUBNUB_USE_ADNS
+#define PUBNUB_USE_ADNS 0
+#endif
+
+
+/** State of a Pubnub socket. Some states are specific to some
+    PALs.
+ */
+enum PBSocketState {
+    /** Socket idle - unused */
+    STATE_NONE = 0,
+    /** ACK received on the socket */
+    STATE_ACKED = 1,
+    /** Reading a number of octets */
+    STATE_READ = 2,
+    /** Block reading of new data, even though we have an indicator
+        that there is data to read.
+    */
+    STATE_BLOCKED_NEWDATA = 3,
+    /** All of the data that has arrived has been read */
+    STATE_NEWDATA_EXHAUSTED = 4,
+    /** All of the data that was to be sent has been sent. */
+    STATE_DATA_SENT = 6,
+    /** Reading a line */
+    STATE_READ_LINE = 7
+};
+
+
+/** The Pubnub context 
+
+    @note Don't declare any members as `bool`, as there may be
+    alignment issues when this is included from both C and C++
+    compilers, especially pre-C99 C compilers (like MSVC (at least
+    until MSVC 2013)).
+*/
+struct pubnub_ {
+    struct pbcc_context core;
+
+    /** Network communication state */
+    enum pubnub_state state;
+    /** Type of current transaction */
+    enum pubnub_trans trans;
+
+    /** Pointer to the next data to be sent. */
+    uint8_t const *sendptr;   
+
+    /** The number of bytes left to be sent. */
+    uint16_t sendlen;         
+
+    /** The number of bytes left to be read. */
+    uint16_t readlen;         
+
+    /** Pointer to next free byte in the read buffer*/
+    uint8_t *ptr;          
+
+    /** Number of bytes left (empty) in the read buffer */
+    uint16_t left;   
+
+    /** The state of the socket. */
+    enum PBSocketState sock_state;   
+
+    /** Number of bytes to read - given by the user */
+    unsigned len;          
+
+    /** Indicates whether we are receiving chunked or regular HTTP
+     * response
+     */
+    uint16_t http_chunked;
+
+    /** Last received HTTP (result) code */
+    uint16_t http_code;
+
+#if defined PUBNUB_ORIGIN_SETTABLE
+    char const *origin;
+#endif
+
+#if 0
+    /** Process that started last transaction */
+    struct process *initiator;
+
+    uint8_t *readptr;         /* Pointer to the next data to be read. */
+#endif
+
+    struct pubnub_pal pal;
+
+    struct pubnub_options {
+        /** Indicates whether to use blocking I/O. Ignored if 
+            choosing between blocking and non-blocking is not supported
+            on a platform. Would be ifdef-ed out, but then it would be
+            possible for this struct to have no members which is 
+            prohibited by the ISO C standard.
+        */
+        bool use_blocking_io : 1;
+
+#if PUBNUB_USE_SSL
+        /** Should the PubNub client establish the connection to
+         * PubNub using SSL? */
+        bool useSSL : 1;
+        /** When SSL is enabled, should PubNub client ignore all SSL
+         * certificate-handshake issues and still continue in SSL mode
+         * if it experiences issues handshaking across local proxies,
+         * firewalls, etc?
+          */
+        bool ignoreSSL : 1;
+        /** When SSL is enabled, should the client fallback to a
+
+         * non-SSL connection if it experiences issues handshaking
+         * across local proxies, firewalls, etc?
+         */
+        bool fallbackSSL : 1;
+#endif
+    } options;
+
+#if PUBNUB_THREADSAFE
+    pubnub_mutex_t monitor;
+#endif
+
+#if PUBNUB_TIMERS_API
+    /** Duration of the transaction timeout, in milliseconds */
+    int transaction_timeout_ms;
+
+#if defined(PUBNUB_CALLBACK_API)
+    struct pubnub_ *previous;
+    struct pubnub_ *next;
+    int timeout_left_ms;
+#endif
+
+#endif
+
+#if defined(PUBNUB_CALLBACK_API)
+    pubnub_callback_t cb;
+    void *user_data;
+#endif
+
+#if PUBNUB_PROXY_API
+
+    /** The type (protocol) of the proxy to use */
+    enum pubnub_proxy_type proxy_type;
+
+    /** Hostname (address) of the proxy server to use */
+    char proxy_hostname[PUBNUB_MAX_PROXY_HOSTNAME_LENGTH + 1];
+
+    /** The (TCP) port to use on the proxy. */
+    uint16_t proxy_port;
+
+    /** Indicates whether this is the "first" HTTP request - that is,
+        the `CONNECT` one. The first is sent to the proxy, while the
+        second (if the first succeeds) is sent to the "real" HTTP
+        server (to which the proxy established a "tunnel".
+    */
+    bool proxy_tunnel_established;
+
+    /** The saved path part of the URL for the Pubnub transaction.
+     */
+    char proxy_saved_path[PUBNUB_BUF_MAXLEN];
+
+#endif
+};
+
+
+
+/** Internal function, to be called when the outcome of a
+    REST call / transaction has been reached.
+*/
+void pbntf_trans_outcome(pubnub_t *pb);
+
+int pbntf_init(void);
+
+int pbntf_got_socket(pubnub_t *pb, pb_socket_t socket);
+
+void pbntf_update_socket(pubnub_t *pb, pb_socket_t socket);
+
+void pbntf_lost_socket(pubnub_t *pb, pb_socket_t socket);
+
+int pbntf_enqueue_for_processing(pubnub_t *pb);
+
+int pbntf_requeue_for_processing(pubnub_t *pb);
+
+int pbntf_watch_in_events(pubnub_t *pb);
+int pbntf_watch_out_events(pubnub_t *pb);
+
+
+/** Internal function. Checks if the given pubnub context pointer
+    is valid. 
+*/
+bool pb_valid_ctx_ptr(pubnub_t const *pb);
+
+/** Internal function, only available in the "static" context
+    allocator. Gives a context with the given index.
+*/
+pubnub_t *pballoc_get_ctx(unsigned idx);
+
+/** Internal function, the "bottom half" of pubnub_free(), which is 
+    done asynchronously in the callback mode. */
+void pballoc_free_at_last(pubnub_t *pb);
+
+
+#endif /* !defined INC_PUBNUB_INTERNAL_COMMON */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_json_parse.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,223 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_json_parse.h"
+
+#include <string.h>
+
+
+char const* pbjson_skip_whitespace(char const *start, char const *end)
+{
+    for (; start < end; ++start) {
+        switch (*start) {
+        case ' ':
+        case '\t':
+        case '\r':
+        case '\n':
+            break;
+        default:
+            return start;
+        }
+    }
+    return start;
+}
+
+
+char const* pbjson_find_end_string(char const *start, char const *end)
+{
+    bool in_escape = false;
+
+    for (; start < end; ++start) {
+        switch (*start) {
+        case '"':
+            if (!in_escape) {
+                return start;
+            }
+            break;
+        case '\\':
+            in_escape = !in_escape;
+            break;
+        case '\0':
+            return start;
+        default:
+            in_escape = false;
+            break;
+        }
+    }
+
+    return start;
+}
+
+
+char const *pbjson_find_end_primitive(char const *start, char const *end)
+{
+    for (; start < end; ++start) {
+        switch (*start) {
+        case ' ':
+        case '\t':
+        case '\r':
+        case '\n':
+        case ',':
+        case '}':
+        case ']':
+            return start-1;
+        case '\0':
+            return start;
+        default:
+            break;
+        }
+    }
+    return start;
+}
+
+
+char const *pbjson_find_end_complex(char const *start, char const *end)
+{
+    bool in_string = false, in_escape = false;
+    int bracket_level = 0, brace_level = 0;
+    char c;
+    char const *s;
+
+    for (s = start, c = *s; (c != '\0') && (s < end); ++s, c = *s) {
+        if (!in_string) {
+            switch (c) {
+            case '{':
+                ++brace_level;
+                break;
+            case '}':
+                if ((--brace_level == 0) && (0 == bracket_level)) {
+                    return s;
+                }
+                break;
+            case '[':
+                ++bracket_level;
+                break;
+            case ']':
+                if ((--bracket_level == 0) && (0 == brace_level)) {
+                    return s;
+                }
+                break;
+            case '"':
+                in_string = true;
+                in_escape = false;
+                break;
+            default:
+                break;
+            }
+        }
+        else {
+            switch (c) {
+            case '"':
+                if (!in_escape) {
+                    in_string = false;
+                }
+                break;
+            case '\\':
+                in_escape = !in_escape;
+                break;
+            default:
+                in_escape = false;
+                break;
+            }
+        }
+    }
+    return s;
+}
+
+
+char const *pbjson_find_end_element(char const *start, char const *end)
+{
+    switch (*start) {
+    case '"':
+        return pbjson_find_end_string(start+1, end);
+    case '{':
+    case '[':
+        return pbjson_find_end_complex(start, end);
+    default:
+        return pbjson_find_end_primitive(start+1, end);
+    }
+}
+
+
+enum pbjson_object_name_parse_result pbjson_get_object_value(struct pbjson_elem const *p, char const *name, struct pbjson_elem *parsed)
+{
+    char const *s = pbjson_skip_whitespace(p->start, p->end);
+    unsigned name_len = strlen(name);
+    bool found = false;
+    char const *end;
+
+    if (0 == name_len) {
+        return jonmpInvalidKeyName;
+    }
+    if (*s != '{') {
+        return jonmpNoStartCurly;
+    }
+    while (s < p->end) {
+        s = pbjson_skip_whitespace(s+1, p->end);
+        if (s == p->end) {
+            return jonmpKeyMissing;
+        }
+        if (*s != '"') {
+            return jonmpKeyNotString;
+        }
+        end = pbjson_find_end_string(s+1, p->end);
+        if (end == p->end) {
+            return jonmpStringNotTerminated;
+        }
+        if (*end != '"') {
+            return jonmpStringNotTerminated;
+        }
+        found = (end-s-1 == name_len) && (0 == memcmp(s+1, name, name_len));
+        s = pbjson_skip_whitespace(end+1, p->end);
+        if (s == p->end) {
+            return jonmpMissingColon;
+        }
+        if (*s != ':') {
+            return jonmpMissingColon;
+        }
+        s = pbjson_skip_whitespace(s+1, p->end);
+        end = pbjson_find_end_element(s, p->end);
+        if (found) {
+            parsed->start = s;
+            parsed->end = end+1;
+            return jonmpOK;
+        }
+        s = pbjson_skip_whitespace(end+1, p->end);
+        if (*s != ',') {
+            if (*s == '}') {
+                break;
+            }
+            return jonmpMissingValueSeparator;
+        }
+    }
+
+    return (s < p->end) ? jonmpKeyNotFound : jonmpObjectIncomplete;
+}
+
+
+bool pbjson_elem_equals_string(struct pbjson_elem const *e, char const *s)
+{
+    char const *p = e->start;
+    for (p = e->start; p != e->end; ++p, ++s) {
+        if (*p != *s) {
+            return false;
+        }
+    }
+    return *s == '\0';
+}
+
+
+char const *pbjson_object_name_parse_result_2_string(enum pbjson_object_name_parse_result e)
+{
+    switch (e) {
+    case jonmpNoStartCurly: return "No Start Curly";
+    case jonmpKeyMissing: return "Key Missing";
+    case jonmpKeyNotString: return "Key Not String";
+    case jonmpStringNotTerminated: return "String Not Terminated";
+    case jonmpMissingColon: return "Missing Colon";
+    case jonmpObjectIncomplete: return "Object Incomplete";
+    case jonmpMissingValueSeparator: return "Missing Value Separator";
+    case jonmpKeyNotFound: return "Key Not Found";
+    case jonmpInvalidKeyName: return "Invalid Key Name";
+    case jonmpOK: return "OK";
+    default: return "?!?";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_json_parse.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,143 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_JSON_PARSE
+#define      INC_PUBNUB_JSON_PARSE
+
+#include <stdbool.h>
+
+
+/** @file pubnub_json_parse.h 
+
+    A bunch of functions for parsing JSON. These are designed for
+    internal use by the Pubnub client, but, since they are rather
+    generic, the user can use them, too, though their availability and
+    interface are not guaranteed to survive Pubnub client version
+    changes.
+ */
+
+/** A representation of a JSON element. */
+struct pbjson_elem {
+    /** The start of the element - pointer to the first
+        character. */
+    char const *start;
+    /** The end of the element - pointer to the character
+        _after_ the last character */
+    char const *end;
+};
+
+
+/** Results of parsing a JSON (object) definition */
+enum pbjson_object_name_parse_result {
+    /** No starting curly brace `{` */
+    jonmpNoStartCurly,
+    /** Key is missing from a JSON object definition */
+    jonmpKeyMissing,
+    /** Key is not a string (this forbidden by JSON) */
+    jonmpKeyNotString,
+    /** String not terminated (no end `"`) */
+    jonmpStringNotTerminated,
+    /** Colon (`:`) is missing in JSON object definition */
+    jonmpMissingColon,
+    /** Ending curly brace `}` is missing */
+    jonmpObjectIncomplete,
+    /** A comma `,`, delimiting key-value pairs in a JSON
+        object, is missing.*/
+    jonmpMissingValueSeparator,
+    /** The key was not found in the JSON object definition */
+    jonmpKeyNotFound,
+    /** The name of the key is empty or otherwise not valid */
+    jonmpInvalidKeyName,
+    /** Parsed OK, JSON (object) is valid */
+    jonmpOK
+};
+
+
+/** Skips whitespace starting from @p start, until @p end.
+    Interprets whitespace as JSON does - that should be
+    compatible with a lot of other specifications.
+
+    @return Pointer to the first character that is not whitespace.
+    It is == @p end if the whole input is skipped.
+ */
+char const* pbjson_skip_whitespace(char const *start, char const *end);
+
+
+/** Finds the end of the string starting from @p start, until @p end.
+    Interprets string as JSON does (starting and ending with
+    double-quotation and allowing escape characters with backslash) -
+    that should be compatible with a lot of other specifications.
+
+    Assumes that @p start points to the first character of the string
+    (that is, one past the opening double-quote).
+
+    @return Pointer to the double-quotation character that is the end
+    of string from input.  It is == @p end if the end of the string
+    was not found in input.
+*/
+    
+char const* pbjson_find_end_string(char const *start, char const *end);
+
+
+/** Finds the end of the "primitive" value starting from @p start,
+    until @p end.  Interprets "primitive value" a little more broadly
+    than JSON does (basically, we allow anything that is not a JSON
+    string, array or object) - that should be compatible with a lot of
+    other specifications.
+
+    Assumes that @p start points to the first character of the
+    primitive.
+
+    @return Pointer to the character that is the end of the primitive.
+    It is == @p end if the end of the primitive was not found in
+    input.
+ */
+char const *pbjson_find_end_primitive(char const *start, char const *end);
+
+
+/** Finds the end of the "complex" value starting from @p start, until
+    @p end.  Interprets "complex value" as a JSON object or array -
+    that should be compatible with a lot of other specifications.
+
+    Assumes that @p start points to the first character of the
+    complex object (opening curly brace or square bracket).
+
+    @return Pointer to the character that is the end of the complex.
+    It is == @p end if the end of the complex was not found in
+    input.
+ */
+char const *pbjson_find_end_complex(char const *start, char const *end);
+
+
+/** Finds the end of the JSON element starting from @p start, until
+    @p end.  Interprets element as JSON does (primitive, object or array) -
+    that should be compatible with a lot of other specifications.
+
+    Assumes that @p start points to the first character of the
+    element.
+
+    @return Pointer to the character that is the end of the element.
+    It is == @p end if the end of the element was not found in
+    input.
+ */
+char const *pbjson_find_end_element(char const *start, char const *end);
+
+
+/** Gets the value from a JSON object from @p p, with the key @p name
+    and puts it to @p parsed o success, returning jonmpOK. On failure,
+    returns the error code and the effects on @p parsed are not
+    defined.
+*/
+enum pbjson_object_name_parse_result pbjson_get_object_value(struct pbjson_elem const *p, char const *name, struct pbjson_elem *parsed);
+
+
+/** Helper function, returns whether string @p s is equal to the
+    contents of the JSON element @p e.
+*/
+bool pbjson_elem_equals_string(struct pbjson_elem const *e, char const *s);
+
+/** Helper function, returns a string describing an enum for
+    the JSON (object) parse result.
+ */
+char const *pbjson_object_name_parse_result_2_string(enum pbjson_object_name_parse_result e);
+
+
+#endif /* !defined INC_PUBNUB_JSON_PARSE */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_log.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,105 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_LOG
+#define	INC_PUBNUB_LOG
+
+
+/** @file pubnub_log.h 
+    This is the "Log" API of the Pubnub client library.
+    Designed for C-core's own use, but not restricted to it.
+    Provides a reasonably-well-featured, yet small and efficient
+    API for logging.
+*/
+
+/** Available Log levels */
+enum pubnub_log_level {
+    /** Nothing is logged */
+    PUBNUB_LOG_LEVEL_NONE,
+    /** Only Errors are logged */
+    PUBNUB_LOG_LEVEL_ERROR,
+    /** Warnings (and errors) are logged */
+    PUBNUB_LOG_LEVEL_WARNING,
+    /** Informational messages (and warnings and errors) are logged */
+    PUBNUB_LOG_LEVEL_INFO,
+    /** Debugging messages (and info and warning and errors) are logged */
+    PUBNUB_LOG_LEVEL_DEBUG,
+    /** Tracing messages (and debug and info and warning and errors)
+     * are logged */
+    PUBNUB_LOG_LEVEL_TRACE
+};
+
+
+#if !defined PUBNUB_LOG_LEVEL
+/** User should define the log level to use, by defining the
+    `PUBNUB_LOG_LEVEL` symbol. If PUBNUB_LOG_LEVEL is not defined,
+    this default is used */
+#define PUBNUB_LOG_LEVEL PUBNUB_LOG_LEVEL_INFO
+#endif
+
+#if !defined PUBNUB_LOG_PRINTF
+#include <stdio.h>
+/** User should define a printf-like function that will do the actual
+    logging. If it is not defined, we'll use printf().
+*/
+#define PUBNUB_LOG_PRINTF(...) printf(__VA_ARGS__)
+#endif
+
+/** Generic logging macro, logs to given @a LVL using a printf-like
+    interface
+*/
+#define PUBNUB_LOG(LVL, ...) do { if (LVL <= PUBNUB_LOG_LEVEL) PUBNUB_LOG_PRINTF(__VA_ARGS__); } while(0)
+
+/** Helper macro to log an error message */
+#define PUBNUB_LOG_ERROR(...) PUBNUB_LOG(PUBNUB_LOG_LEVEL_ERROR, __VA_ARGS__)
+
+/** Helper macro to log a warning message */
+#define PUBNUB_LOG_WARNING(...) PUBNUB_LOG(PUBNUB_LOG_LEVEL_WARNING, __VA_ARGS__)
+
+/** Helper macro to log an informational message */
+#define PUBNUB_LOG_INFO(...) PUBNUB_LOG(PUBNUB_LOG_LEVEL_INFO, __VA_ARGS__)
+
+/** Helper macro to log a debug message */
+#define PUBNUB_LOG_DEBUG(...) PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, __VA_ARGS__)
+
+/** Helper macro to log a tracing message */
+#define PUBNUB_LOG_TRACE(...) PUBNUB_LOG(PUBNUB_LOG_LEVEL_TRACE, __VA_ARGS__)
+
+/** Generic macro to print a value of a (scalar) variable.
+ */
+#define WATCH_VAL(X,T,FMT) do { (void)(1?&X:(T*)0); PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, __FILE__ "(%d) in %s: `" #X "` = " FMT "\n", __LINE__, __FUNCTION__, X); } while (0)
+
+/** Helper macro to "watch" an integer */
+#define WATCH_INT(X) WATCH_VAL(X, int, "%d")
+
+/** Helper macro to "watch" an unsigned integer */
+#define WATCH_UINT(X) WATCH_VAL(X, unsigned int, "%ud")
+
+/** Helper macro to "watch" a long */
+#define WATCH_LONG(X) WATCH_VAL(X, long, "%l")
+
+/** Helper macro to "watch" an unsigned long */
+#define WATCH_ULONG(X) WATCH_VAL(X, unsigned long, "%ul")
+
+/** Helper macro to "watch" a short */
+#define WATCH_SHORT(X) WATCH_VAL(X, short, "%d")
+
+/** Helper macro to "watch" an unsigned short */
+#define WATCH_USHORT(X) WATCH_VAL(X, unsigned short, "%ud")
+
+/** Helper macro to "watch" a char */
+#define WATCH_CHAR(X) WATCH_VAL(X, char, "%c")
+
+/** Helper macro to "watch" an unsigned char */
+#define WATCH_UCHAR(X) WATCH_VAL(X, unsigned char, "%uc")
+
+/** Helper macro to "watch" an enum like an integer.*/
+#define WATCH_ENUM(X) do { int x_ = (X); PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, __FILE__ "(%d) in %s: `" #X "` = %d\n", __LINE__, __FUNCTION__, x_); } while (0)
+    
+/** Helper macro to "watch" a string (char pointer) */
+#define WATCH_STR(X) do { char const*s_ = (X); PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, __FILE__ "(%d) in %s: `" #X "` = '%s'\n", __LINE__, __FUNCTION__, s_); } while (0)
+
+/** Helper macro to "watch" an array of bytes */
+#define WATCH_BYTEARR(X) do { unsigned char const*ba_ = (X); PUBNUB_LOG(PUBNUB_LOG_LEVEL_DEBUG, __FILE__ "(%d) in %s: `" #X "` = '%s'\n", __LINE__, __FUNCTION__, s_); } while (0)
+
+
+
+#endif /*!defined INC_PUBNUB_LOG*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_memory_block.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,42 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_MEMORY_BLOCK
+#define	INC_PUBNUB_MEMORY_BLOCK
+
+#include <stdint.h>
+#include <stdlib.h>
+
+
+/** @file pubnub_memory_block.h
+
+    Memory block module.
+
+    Support module for having a standard way to represent a 
+    "block" of memory - that is, a pointer and the size of
+    memory allocated (in whatever way) to said pointer.
+ */
+
+
+/** A block of memory whose pointer is pointing to a
+    bytes. This is most often the preferred one to use.
+*/
+struct pubnub_byte_mem_block {
+    uint8_t *ptr;
+    size_t size;
+};
+
+/** A block of memory whose pointer is void pointer.
+    Not nice, but useful at times.
+ */
+struct pubnub_mem_block {
+    void *ptr;
+    size_t size;
+};
+
+/** Helper typedef for a general (void) memory block */
+typedef struct pubnub_mem_block pubnub_mebl_t;
+
+/** Helper typedef for a byte memory block */
+typedef struct pubnub_byte_mem_block pubnub_bymebl_t;
+
+
+#endif /* !defined INC_PUBNUB_MEMORY_BLOCK */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_mutex.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,32 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_MUTEX
+#define      INC_PUBNUB_MUTEX
+
+
+#if PUBNUB_THREADSAFE
+#include "pbpal_mutex.h"
+
+
+#define pubnub_mutex_t pbpal_mutex_t
+#define pubnub_mutex_init(m) pbpal_mutex_init(m)
+#define pubnub_mutex_lock(m) pbpal_mutex_lock(m)
+#define pubnub_mutex_unlock(m) pbpal_mutex_unlock(m)
+#define pubnub_mutex_destroy(m) pbpal_mutex_destroy(m)
+#define pubnub_mutex_decl_and_init(m) pbpal_mutex_decl_and_init(m)
+#define pubnub_mutex_static_decl_and_init(m) pbpal_mutex_static_decl_and_init(m)
+#define pubnub_mutex_init_static(m) pbpal_mutex_init_static(m)
+
+#else
+
+typedef struct { int dummy; } pubnub_mutex_t;
+#define pubnub_mutex_init(m)
+#define pubnub_mutex_lock(m)
+#define pubnub_mutex_unlock(m)
+#define pubnub_mutex_destroy(m)
+#define pubnub_mutex_decl_and_init(m)
+#define pubnub_mutex_static_decl_and_init(m)
+#define pubnub_mutex_init_static(m)
+
+#endif
+
+#endif /* !defined  INC_PUBNUB_MUTEX */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_netcore.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,622 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_internal.h"
+
+#include "pubnub_assert.h"
+#include "pubnub_log.h"
+#include "pubnub_ccore.h"
+#include "pbpal.h"
+
+#include <string.h>
+
+
+static void outcome_detected(struct pubnub_ *pb, enum pubnub_res rslt)
+{
+    pb->core.last_result = rslt;
+    if (pbpal_close(pb) <= 0) {
+        pbpal_forget(pb);
+        pbntf_trans_outcome(pb);
+    }
+    else {
+        pb->state = PBS_WAIT_CLOSE;
+    }
+}
+
+
+static enum pubnub_res parse_pubnub_result(struct pubnub_ *pb)
+{
+    enum pubnub_res pbres = PNR_OK;
+    switch (pb->trans) {
+    case PBTT_SUBSCRIBE:
+        if (pbcc_parse_subscribe_response(&pb->core) != 0) {
+            PUBNUB_LOG_WARNING("parse_subscribe failed\n");
+            pbres = PNR_FORMAT_ERROR;
+        }
+        break;
+    case PBTT_PUBLISH:
+        pbres = pbcc_parse_publish_response(&pb->core);
+        if (pbres != PNR_OK) {
+            PUBNUB_LOG_WARNING("parse_publish failed\n");
+        }
+        break;
+    case PBTT_TIME:
+        if (pbcc_parse_time_response(&pb->core) != 0) {
+            PUBNUB_LOG_WARNING("parse_time failed\n");
+            pbres = PNR_FORMAT_ERROR;
+        }
+        break;
+    case PBTT_HISTORY:
+        if (pbcc_parse_history_response(&pb->core) != 0) {
+            PUBNUB_LOG_WARNING("parse_history failed\n");
+            pbres = PNR_FORMAT_ERROR;
+        }
+        break;
+    case PBTT_LEAVE:
+    case PBTT_HERENOW:
+    case PBTT_GLOBAL_HERENOW:
+    case PBTT_WHERENOW:
+    case PBTT_SET_STATE:
+    case PBTT_STATE_GET:
+    case PBTT_HEARTBEAT:
+        if (pbcc_parse_presence_response(&pb->core) != 0) {
+            PUBNUB_LOG_WARNING("parse_presence failed\n");
+            pbres = PNR_FORMAT_ERROR;
+        }
+        break;
+    case PBTT_REMOVE_CHANNEL_GROUP:
+    case PBTT_REMOVE_CHANNEL_FROM_GROUP:
+    case PBTT_ADD_CHANNEL_TO_GROUP:
+    case PBTT_LIST_CHANNEL_GROUP:
+        pbres = pbcc_parse_channel_registry_response(&pb->core);
+        if (pbres != PNR_OK) {
+            PUBNUB_LOG_WARNING("parse_channel_registry failed\n");
+        }
+        break;
+    default:
+        break;
+    }
+
+    return pbres;
+}
+
+
+static void finish(struct pubnub_ *pb)
+{
+    enum pubnub_res pbres;
+
+#if PUBNUB_PROXY_API
+    if (pb->proxy_type == pbproxyHTTP_CONNECT) {
+        if (!pb->proxy_tunnel_established) {
+            if ((pb->http_code / 100) != 2) {
+                outcome_detected(pb, PNR_HTTP_ERROR);
+            }
+            else {
+                pb->proxy_tunnel_established = true;
+                pb->state = PBS_CONNECTED;
+            }
+
+            return;
+        }
+        else {
+            pb->proxy_tunnel_established = false;
+        }
+    }
+#endif
+
+    pb->core.http_reply[pb->core.http_buf_len] = '\0';
+    PUBNUB_LOG_TRACE("finish('%s')\n", pb->core.http_reply);
+
+    pbres = parse_pubnub_result(pb);
+    if ((PNR_OK == pbres) && ((pb->http_code / 100) != 2)) {
+        pbres = PNR_HTTP_ERROR;
+    }
+    
+    outcome_detected(pb, pbres);
+}
+
+
+int pbnc_fsm(struct pubnub_ *pb)
+{
+    enum pubnub_res pbrslt;
+    int i;
+
+    PUBNUB_LOG_TRACE("pbnc_fsm()\t");
+
+next_state:
+    WATCH_ENUM(pb->state);
+    switch (pb->state) {
+    case PBS_NULL:
+        break;
+    case PBS_IDLE:
+        pb->state = PBS_READY;
+        switch (pbntf_enqueue_for_processing(pb)) {
+        case -1:
+            pb->core.last_result = PNR_INTERNAL_ERROR;
+            pbntf_trans_outcome(pb);
+            return 0;
+        case 0:
+            goto next_state;
+        case +1:
+            break;
+        }
+        break;
+    case PBS_READY:
+    {
+        enum pbpal_resolv_n_connect_result rslv = pbpal_resolv_and_connect(pb);
+        WATCH_ENUM(rslv);
+        switch (rslv) {
+        case pbpal_resolv_send_wouldblock:
+            pb->state = PBS_WAIT_DNS_SEND;
+            break;
+        case pbpal_resolv_sent:
+        case pbpal_resolv_rcv_wouldblock:
+            pb->state = PBS_WAIT_DNS_RCV;
+            pbntf_watch_in_events(pb);
+            break;
+        case pbpal_connect_wouldblock:
+            pb->state = PBS_WAIT_CONNECT;
+            break;
+        case pbpal_connect_success:
+            pb->state = PBS_CONNECTED;
+            break;
+        default:
+            pb->core.last_result = PNR_ADDR_RESOLUTION_FAILED;
+            pbntf_trans_outcome(pb);
+            return 0;
+        }
+        i = pbntf_got_socket(pb, pb->pal.socket);
+        if (0 == i) {
+            goto next_state;
+        }
+        else if (i < 0) {
+            pb->core.last_result = PNR_CONNECT_FAILED;
+            pbntf_trans_outcome(pb);
+        }
+        break;
+    }
+    case PBS_WAIT_DNS_SEND:
+    {
+        enum pbpal_resolv_n_connect_result rslv = pbpal_check_resolv_and_connect(pb);
+        WATCH_ENUM(rslv);
+        switch (rslv) {
+        case pbpal_resolv_send_wouldblock:
+            break;
+        case pbpal_resolv_sent:
+        case pbpal_resolv_rcv_wouldblock:
+            pbntf_update_socket(pb, pb->pal.socket);
+            pb->state = PBS_WAIT_DNS_RCV;
+            pbntf_watch_in_events(pb);
+            break;
+        case pbpal_connect_wouldblock:
+            pbntf_update_socket(pb, pb->pal.socket);
+            pb->state = PBS_WAIT_CONNECT;
+            break;
+        case pbpal_connect_success:
+            pb->state = PBS_CONNECTED;
+            goto next_state;
+        default:
+            outcome_detected(pb, PNR_ADDR_RESOLUTION_FAILED);
+            break;
+        }
+        break;
+    }
+    case PBS_WAIT_DNS_RCV:
+    {
+        enum pbpal_resolv_n_connect_result rslv = pbpal_check_resolv_and_connect(pb);
+        WATCH_ENUM(rslv);
+        switch (rslv) {
+        case pbpal_resolv_send_wouldblock:
+        case pbpal_resolv_sent:
+            outcome_detected(pb, PNR_INTERNAL_ERROR);
+            break;
+        case pbpal_resolv_rcv_wouldblock:
+            break;
+        case pbpal_connect_wouldblock:
+            pbntf_update_socket(pb, pb->pal.socket);
+            pb->state = PBS_WAIT_CONNECT;
+            pbntf_watch_out_events(pb);
+            break;
+        case pbpal_connect_success:
+            pb->state = PBS_CONNECTED;
+            pbntf_watch_out_events(pb);
+            goto next_state;
+        default:
+            outcome_detected(pb, PNR_ADDR_RESOLUTION_FAILED);
+            break;
+        }
+        break;
+    }
+    case PBS_WAIT_CONNECT:
+    {
+        enum pbpal_resolv_n_connect_result rslv = pbpal_check_connect(pb);
+        WATCH_ENUM(rslv);
+        switch (rslv) {
+        case pbpal_resolv_send_wouldblock:
+        case pbpal_resolv_sent:
+        case pbpal_resolv_rcv_wouldblock:
+            pb->core.last_result = PNR_INTERNAL_ERROR;
+            pbntf_trans_outcome(pb);
+            break;
+        case pbpal_connect_wouldblock:
+            break;
+        case pbpal_connect_success:
+            pb->state = PBS_CONNECTED;
+            goto next_state;
+        default:
+            outcome_detected(pb, PNR_CONNECT_FAILED);
+            break;
+        }
+        break;
+    }
+    case PBS_CONNECTED:
+#if PUBNUB_PROXY_API
+        if ((pb->proxy_type == pbproxyHTTP_CONNECT) && (!pb->proxy_tunnel_established)) {
+            pbpal_send_literal_str(pb, "CONNECT ");
+        }
+        else {
+            pbpal_send_literal_str(pb, "GET ");
+        }
+#else
+        pbpal_send_literal_str(pb, "GET ");
+#endif
+        pb->state = PBS_TX_GET;
+        goto next_state;
+    case PBS_TX_GET:
+        i = pbpal_send_status(pb);
+        if (i <= 0) {
+#if PUBNUB_PROXY_API
+            switch (pb->proxy_type) {
+            case pbproxyHTTP_GET:
+                pb->state = PBS_TX_SCHEME;
+                if (i < 0) {
+                    outcome_detected(pb, PNR_IO_ERROR);
+                    break;
+                }
+                pbpal_send_literal_str(pb, "http://");
+                break;
+            case pbproxyHTTP_CONNECT:
+                pb->state = PBS_TX_SCHEME;
+                if (i < 0) {
+                    outcome_detected(pb, PNR_IO_ERROR);
+                    break;
+                }
+                if (!pb->proxy_tunnel_established) {
+                    strcpy(pb->proxy_saved_path, pb->core.http_buf);
+                }
+                else {
+                    strcpy(pb->core.http_buf, pb->proxy_saved_path);
+                }
+                break;
+            case pbproxyNONE:
+                pb->state = PBS_TX_PATH;
+                if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
+                    outcome_detected(pb, PNR_IO_ERROR);
+                }
+                break;
+            default:
+                outcome_detected(pb, PNR_INTERNAL_ERROR);
+                break;
+            }
+#else
+            pb->state = PBS_TX_PATH;
+            if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
+                outcome_detected(pb, PNR_IO_ERROR);
+                break;
+            }
+#endif /* PUBNUB_PROXY_API */
+            goto next_state;
+        }
+        break;
+#if PUBNUB_PROXY_API
+    case PBS_TX_SCHEME:
+        i = pbpal_send_status(pb);
+        if (i <= 0) {
+            if ((pb->proxy_type == pbproxyHTTP_CONNECT) && pb->proxy_tunnel_established) {
+                pb->state = PBS_TX_HOST;
+            }
+            else {
+                char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
+                pb->state = PBS_TX_HOST;
+                if ((i < 0) || (-1 == pbpal_send_str(pb, o))) {
+                    outcome_detected(pb, PNR_IO_ERROR);
+                    break;
+                }
+            }
+            goto next_state;
+        }
+        break;
+    case PBS_TX_HOST:
+        i = pbpal_send_status(pb);
+        if (i <= 0) {
+            if ((pb->proxy_type == pbproxyHTTP_CONNECT) && !pb->proxy_tunnel_established) {
+                char port_num[20];
+                snprintf(port_num, sizeof port_num, ":%d", 80);
+                pbpal_send_str(pb, port_num);
+                pb->state = PBS_TX_PORT_NUM;
+                goto next_state;
+            }
+            else {
+                pb->state = PBS_TX_PATH;
+                if ((i < 0) || (-1 == pbpal_send_str(pb, pb->core.http_buf))) {
+                    outcome_detected(pb, PNR_IO_ERROR);
+                    break;
+                }
+            }
+            goto next_state;
+        }
+        break;
+    case PBS_TX_PORT_NUM:
+        i = pbpal_send_status(pb);
+        if (i <= 0) {
+            pb->state = PBS_TX_PATH;
+            if (i < 0) {
+                outcome_detected(pb, PNR_IO_ERROR);
+                break;
+            }
+            goto next_state;
+        }
+        break;
+#endif /* PUBNUB_PROXY_API */
+    case PBS_TX_PATH:
+        i = pbpal_send_status(pb);
+        if (i < 0) {
+            outcome_detected(pb, PNR_IO_ERROR);
+        }
+        else if (0 == i) {
+            pbpal_send_literal_str(pb, " HTTP/1.1\r\nHost: ");
+            pb->state = PBS_TX_VER;
+            goto next_state;
+        }
+        break;
+    case PBS_TX_VER:
+        i = pbpal_send_status(pb);
+        if (i <= 0) {
+            char const* o = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN;
+            pb->state = PBS_TX_ORIGIN;
+            if ((i < 0) || (-1 == pbpal_send_str(pb, o))) {
+                outcome_detected(pb, PNR_IO_ERROR);
+                break;
+            }
+            goto next_state;
+        }
+        break;
+    case PBS_TX_ORIGIN:
+        i = pbpal_send_status(pb);
+        if (i < 0) {
+            outcome_detected(pb, PNR_IO_ERROR);
+        }
+        else if (0 == i) {
+            pbpal_send_literal_str(pb, "\r\nUser-Agent: PubNub-C-core/2.1\r\nConnection: Keep-Alive\r\n\r\n");
+            pb->state = PBS_TX_FIN_HEAD;
+            goto next_state;
+        }
+        break;
+    case PBS_TX_FIN_HEAD:
+        i = pbpal_send_status(pb);
+        if (i < 0) {
+            outcome_detected(pb, PNR_IO_ERROR);
+        }
+        else if (0 == i) {
+            pbpal_start_read_line(pb);
+            pb->state = PBS_RX_HTTP_VER;
+            pbntf_watch_in_events(pb);
+            goto next_state;
+        }
+        break;
+    case PBS_RX_HTTP_VER:
+        pbrslt = pbpal_line_read_status(pb);
+        switch (pbrslt) {
+        case PNR_IN_PROGRESS:
+            break;
+        case PNR_OK:
+            if (strncmp(pb->core.http_buf, "HTTP/1.", 7) != 0) {
+                outcome_detected(pb, PNR_IO_ERROR);
+                break;
+            }
+            pb->http_code = atoi(pb->core.http_buf + 9);
+            WATCH_USHORT(pb->http_code);
+            pb->core.http_content_len = 0;
+            pb->http_chunked = false;
+            pb->state = PBS_RX_HEADERS;
+            goto next_state;
+        default:
+            outcome_detected(pb, pbrslt);
+            break;
+        }
+        break;
+    case PBS_RX_HEADERS:
+        PUBNUB_LOG_TRACE("PBS_RX_HEADERS\n");
+        pbpal_start_read_line(pb);
+        pb->state = PBS_RX_HEADER_LINE;
+        goto next_state;
+    case PBS_RX_HEADER_LINE:
+        PUBNUB_LOG_TRACE("PBS_RX_HEADER_LINE\n");
+        pbrslt = pbpal_line_read_status(pb);
+        switch (pbrslt) {
+        case PNR_IN_PROGRESS:
+            break;
+        case PNR_OK:
+        {
+            char h_chunked[] = "Transfer-Encoding: chunked";
+            char h_length[] = "Content-Length: ";
+            int read_len = pbpal_read_len(pb);
+            PUBNUB_LOG_TRACE("header line was read: %.*s\n", read_len, pb->core.http_buf);
+            WATCH_INT(read_len);
+            if (read_len <= 2) {
+                pb->core.http_buf_len = 0;
+                if (!pb->http_chunked) {
+                    if (0 == pb->core.http_content_len) {
+#if PUBNUB_PROXY_API
+                        if ((pb->proxy_type == pbproxyHTTP_CONNECT) && !pb->proxy_tunnel_established) {
+                            finish(pb);
+                            break;
+                        }
+#endif
+                        outcome_detected(pb, PNR_IO_ERROR);
+                        break;
+                    }
+                    pb->state = PBS_RX_BODY;
+                }
+                else {
+                    pb->state = PBS_RX_CHUNK_LEN;
+                }
+                goto next_state;
+            }
+            if (strncmp(pb->core.http_buf, h_chunked, sizeof h_chunked - 1) == 0) {
+                pb->http_chunked = true;
+            }
+            else if (strncmp(pb->core.http_buf, h_length, sizeof h_length - 1) == 0) {
+                size_t len = atoi(pb->core.http_buf + sizeof h_length - 1);
+                if (0 != pbcc_realloc_reply_buffer(&pb->core, len)) {
+                    outcome_detected(pb, PNR_REPLY_TOO_BIG);
+                    break;
+                }
+                pb->core.http_content_len = len;
+            }
+            pb->state = PBS_RX_HEADERS;
+            goto next_state;
+        }
+        default:
+            outcome_detected(pb, pbrslt);
+            break;
+        }
+        break;
+    case PBS_RX_BODY:
+        PUBNUB_LOG_TRACE("PBS_RX_BODY\n");
+        if (pb->core.http_buf_len < pb->core.http_content_len) {
+            pbpal_start_read(pb, pb->core.http_content_len - pb->core.http_buf_len);
+            pb->state = PBS_RX_BODY_WAIT;
+            goto next_state;
+        }
+        else {
+            finish(pb);
+        }
+        break;
+    case PBS_RX_BODY_WAIT:
+        PUBNUB_LOG_TRACE("PBS_RX_BODY_WAIT\n");
+        if (pbpal_read_over(pb)) {
+            unsigned len = pbpal_read_len(pb);
+            WATCH_UINT(len);
+            WATCH_UINT(pb->core.http_buf_len);
+            memcpy(
+                pb->core.http_reply + pb->core.http_buf_len,
+                pb->core.http_buf,
+                len
+                );
+            pb->core.http_buf_len += len;
+            pb->state = PBS_RX_BODY;
+            goto next_state;
+        }
+        break;
+    case PBS_RX_CHUNK_LEN:
+        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN\n");
+        pbpal_start_read_line(pb);
+        pb->state = PBS_RX_CHUNK_LEN_LINE;
+        goto next_state;
+    case PBS_RX_CHUNK_LEN_LINE:
+        pbrslt = pbpal_line_read_status(pb);
+        PUBNUB_LOG_TRACE("PBS_RX_CHUNK_LEN_LINE: pbrslt=%d\n", pbrslt);
+        switch (pbrslt) {
+        case PNR_IN_PROGRESS:
+            break;
+        case PNR_OK:
+        {
+            unsigned chunk_length = strtoul(pb->core.http_buf, NULL, 16);
+
+            PUBNUB_LOG_TRACE("About to read a chunk w/length: %d\n", chunk_length);
+            if (chunk_length == 0) {
+                finish(pb);
+            }
+            else if (chunk_length > sizeof pb->core.http_buf) {
+                outcome_detected(pb, PNR_IO_ERROR);
+            }
+            else if (0 != pbcc_realloc_reply_buffer(&pb->core, pb->core.http_buf_len + chunk_length)) {
+                outcome_detected(pb, PNR_REPLY_TOO_BIG);
+            }
+            else {
+                pb->core.http_content_len = chunk_length + 2;
+                pb->state = PBS_RX_BODY_CHUNK;
+                goto next_state;
+            }
+            break;
+        }
+        default:
+            outcome_detected(pb, pbrslt);
+            break;
+        }
+        break;
+    case PBS_RX_BODY_CHUNK:
+        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK\n");
+        if (pb->core.http_content_len > 0) {
+            pbpal_start_read(pb, pb->core.http_content_len);
+            pb->state = PBS_RX_BODY_CHUNK_WAIT;
+        }
+        else {
+            pb->state = PBS_RX_CHUNK_LEN;
+        }
+        goto next_state;
+    case PBS_RX_BODY_CHUNK_WAIT:
+        PUBNUB_LOG_TRACE("PBS_RX_BODY_CHUNK_WAIT\n");
+        if (pbpal_read_over(pb)) {
+            unsigned len = pbpal_read_len(pb);
+
+            PUBNUB_ASSERT_OPT(pb->core.http_content_len >= len);
+            PUBNUB_ASSERT_OPT(len > 0);
+
+            if (pb->core.http_content_len > 2) {
+                unsigned to_copy = pb->core.http_content_len - 2;
+                if (len < to_copy) {
+                    to_copy = len;
+                }
+                memcpy(
+                    pb->core.http_reply + pb->core.http_buf_len,
+                    pb->core.http_buf,
+                    to_copy
+                    );
+                pb->core.http_buf_len += to_copy;
+            }
+            pb->core.http_content_len -= len;
+            pb->state = PBS_RX_BODY_CHUNK;
+            goto next_state;
+        }
+        break;
+    case PBS_WAIT_CLOSE:
+        if (pbpal_closed(pb)) {
+            pbpal_forget(pb);
+            pbntf_trans_outcome(pb);
+        }
+        break;
+    case PBS_WAIT_CANCEL:
+        pb->state = PBS_WAIT_CANCEL_CLOSE;
+        if (pbpal_close(pb) <= 0) {
+            goto next_state;
+        }
+        break;
+    case PBS_WAIT_CANCEL_CLOSE:
+        if (pbpal_closed(pb)) {
+            pbpal_forget(pb);
+            pb->core.msg_ofs = pb->core.msg_end = 0;
+            pbntf_trans_outcome(pb);
+        }
+        break;
+    }
+    return 0;
+}
+
+
+void pbnc_stop(struct pubnub_ *pb, enum pubnub_res outcome_to_report)
+{
+    pb->core.last_result = outcome_to_report;
+    switch (pb->state) {
+    case PBS_WAIT_CANCEL:
+    case PBS_WAIT_CANCEL_CLOSE:
+        break;
+    case PBS_IDLE:
+    case PBS_NULL:
+        pbntf_trans_outcome(pb);
+        break;
+    default:
+        pb->state = PBS_WAIT_CANCEL;
+        pbntf_requeue_for_processing(pb);
+        break;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_netcore.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,97 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_NETCORE
+#define      INC_PUBNUB_NETCORE
+
+/** @file pubnub_netcore.h
+
+    This is the interface of the Pubnub C-core network module.
+    It is internal, users should not include this or use any
+    of its definitions.
+ */
+
+
+/** States of a context */
+enum pubnub_state {
+    /** Context not allocated */
+    PBS_NULL,
+    /** No transaction ongoing */
+    PBS_IDLE,
+    /** Ready to start a transaction */
+    PBS_READY,
+    /** Waiting for sending a DNS request */
+    PBS_WAIT_DNS_SEND,
+    /** Waiting for DNS resolution (response)  */
+    PBS_WAIT_DNS_RCV,
+    /** Waiting for TCP connection establishment */
+    PBS_WAIT_CONNECT,
+    /** TCP connected, can start */
+    PBS_CONNECTED,
+    /** Sending HTTP "GET" */
+    PBS_TX_GET,
+    /** Sending the path (part of the URL) */
+    PBS_TX_PATH,
+    /** Sending the scheme (part of the URL - the `http:` part) -
+     utilized when using the "HTTP GET" proxy */
+    PBS_TX_SCHEME,
+    /** Sending the host (part of the URL - the `www.pubnub.com` part)
+     - utilized when using the "HTTP GET" proxy */
+    PBS_TX_HOST,
+    /** Sending the "port number" (part of the URL - the `:80` part) -
+     utilized when using the "HTTP CONNECT" proxy */
+    PBS_TX_PORT_NUM,
+    /** Sending the HTTP version */
+    PBS_TX_VER,
+    /** Sending the DNS name (part of the URL) */
+    PBS_TX_ORIGIN,
+    /** Sending the rest of the HTTP headers */
+    PBS_TX_FIN_HEAD,
+    /** Waiting for HTTP version in response */
+    PBS_RX_HTTP_VER,
+    /** Reading the HTTP response headers */
+    PBS_RX_HEADERS,
+    /** Reading one HTTP response header line */
+    PBS_RX_HEADER_LINE,
+    /** Reading the HTTP response body */
+    PBS_RX_BODY,
+    /** Waiting for new data in HTTP response body */
+    PBS_RX_BODY_WAIT,
+    /** Reading the length of the chunk in HTTP chunked response body */
+    PBS_RX_CHUNK_LEN,
+    /** Waiting to receive whole line of the chunk length*/
+    PBS_RX_CHUNK_LEN_LINE,
+    /** Reading the chunk in HTTP chunked response body */
+    PBS_RX_BODY_CHUNK,
+    /** Waiting for new data in HTTP chunked response body */
+    PBS_RX_BODY_CHUNK_WAIT,
+    /** Waiting for the TCP connection to close */
+    PBS_WAIT_CLOSE,
+    /** Waiting to cancel (close before the end of transaction) the
+     * TCP connection (in some TCP/IP implementations, we don't want to
+     * cancel right away, but wait).
+     */
+    PBS_WAIT_CANCEL,
+    /* Waiting for a cancelled TCP connection to close */
+    PBS_WAIT_CANCEL_CLOSE
+};
+
+
+struct pubnub_;
+
+/** The Finite State Machine (FSM) of the net-core module.  It's
+    "universal", in the sense that it is portable to all platforms,
+    with the porting being done by implementing some functions it
+    needs in the PAL (Platform Abstraction Layer).
+ */
+int pbnc_fsm(struct pubnub_ *pb);
+
+
+/** Issues a stop command to the FSM of the net-core module. The
+	FSM will close a connection (if it is already open) and then
+	inform the user of the outcome (as specified) when it is called.
+
+	@note This function will _not_ call pbnc_fsm().
+*/
+void pbnc_stop(struct pubnub_ *pb, enum pubnub_res outcome_to_report);
+
+
+#endif  /* !defined INC_PUBNUB_NETCORE */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_timers.cpp	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,22 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#include "pubnub_timers.h"
+
+#include "pubnub_assert.h"
+#include "pubnub_internal.h"
+
+
+int pubnub_set_transaction_timeout(pubnub_t *p, int duration_ms)
+{
+    PUBNUB_ASSERT_OPT(p != NULL);
+    PUBNUB_ASSERT_OPT(duration_ms > 0);
+    p->transaction_timeout_ms = duration_ms;
+    return 0;
+}
+
+
+int pubnub_transaction_timeout_get(pubnub_t *p)
+{
+    PUBNUB_ASSERT_OPT(p != NULL);
+    return p->transaction_timeout_ms;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_timers.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,60 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_TIMERS_IO
+#define	INC_PUBNUB_TIMERS_IO
+
+
+#include "pubnub_api_types.h"
+
+
+/** @file pubnub_timers.h 
+    This is the "Timer" API of the Pubnub client library.
+    Functions here influence the way that Pubnub client library
+    works with lower levels (the TCP/IP stack) with respect to
+    how much will an operation be waited upon to complete.
+
+    It is available in most platforms, but the exact way it behaves
+    may have noticable differences. In general, the actual timeout
+    will be _at_ _least_ as the specified by the user, but, it may
+    actually be more.
+*/
+
+
+/** The Pubnub default timeout on subscribe transaction, in milliseconds */
+#define PUBNUB_DEFAULT_SUBSCRIBE_TIMEOUT 310000
+
+/** The Pubnub default timeout on non-subscribe transactions, in milliseconds */
+#define PUBNUB_DEFAULT_NON_SUBSCRIBE_TIMEOUT 10000
+
+
+/** Sets the transaction timeout for the context. This will be
+    used for all subsequent transactions. If a transactions is ongoing
+    and its timeout can be changed, it will be, but if it can't, that 
+    would not be reported as an error.
+    
+    Pubnub SDKs, in general, distinguish the "subscribe" timeout and
+    other transactions, but, C-core doesn't, to save space, as most 
+    contexts are either used for subscribe or for other transactions.
+    
+    If timer support is available, pubnub_init() will set a default timeout,
+    which is configurable at compile time. So, if the default timeout is
+    fine with you, you don't have to call this function.
+
+    @pre Call this after pubnub_init() on the context
+    @pre duration_ms > 0
+    @param p The Context to set transaction timeout for
+    @param duration_ms Duration of the timeout, in milliseconds
+
+    @return 0: OK, otherwise: error, timers not supported  
+*/
+int pubnub_set_transaction_timeout(pubnub_t *p, int duration_ms);
+
+/** Returns the current transaction timeout for the context.
+
+    @pre Call this after pubnub_init() on the context
+    @param p The Context for which to get the transaction timeout
+    @return Current transaction timeout, in milliseconds (should
+    always be > 0)
+*/
+int pubnub_transaction_timeout_get(pubnub_t *p);
+
+#endif /* defined INC_PUBNUB_TIMERS_IO */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pubnub_version.h	Thu Nov 10 22:20:11 2016 +0000
@@ -0,0 +1,29 @@
+/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */
+#if !defined INC_PUBNUB_VERSION
+#define	INC_PUBNUB_VERSION
+
+
+/** @file pubnub_version.h 
+    This is the name / version API of the Pubnub client library.
+    It has the functions that are present in all variants and
+    platforms and have the same interface in all of them.
+*/
+
+
+/** Returns a string with the name of the Pubnub SDK client you
+    are using.
+*/
+char const *pubnub_sdk_name(void);
+
+/** Returns a string with the version of the Pubnub SDK client you are
+    using.
+*/
+char const *pubnub_version(void);
+
+/** Returns an URL encoded string with the full identification of the
+    SDK - name, version, possible something more.
+*/
+char const *pubnub_uname(void);
+
+
+#endif /* !defined INC_PUBNUB_VERSION */