Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: Pubnub_c_core_mbed2_pal Pubnub_c_core_mbed2_pal Pubnub_c_core_mbed2_pal2
Revision 0:d13755cfb705, committed 2016-11-10
- 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
--- /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 */