publish final code
Fork of Pubnub_c_core by
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 */