char const *pubnub_last_publish_result(pubnub_t const *pb) { char *end; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); if (PUBNUB_DYNAMIC_REPLY_BUFFER && (NULL == pb->core.http_reply)) { return ""; } if ((pb->trans != PBTT_PUBLISH) || (pb->core.http_reply[0] == '\0')) { return ""; } for (end = pb->core.http_reply + 1; isdigit((unsigned)*end); ++end) { continue; } return end + 1; }
pubnub_t* pubnub_init(pubnub_t *p, const char *publish_key, const char *subscribe_key) { PUBNUB_ASSERT(pb_valid_ctx_ptr(p)); pbcc_init(&p->core, publish_key, subscribe_key); if (PUBNUB_TIMERS_API) { p->transaction_timeout_ms = PUBNUB_DEFAULT_TRANSACTION_TIMER; } if (PUBNUB_ORIGIN_SETTABLE) { p->origin = PUBNUB_ORIGIN; } p->state = PBS_IDLE; p->trans = PBTT_NONE; pbpal_init(p); return p; }
enum pubnub_res pbpal_resolv_and_connect(pubnub_t *pb) { struct addrinfo *result; struct addrinfo *it; struct addrinfo hint; int error; char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT((pb->state == PBS_IDLE) || (pb->state == PBS_WAIT_DNS)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; hint.ai_protocol = hint.ai_flags = hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; error = getaddrinfo(origin, HTTP_PORT_STRING, &hint, &result); if (error != 0) { return PNR_ADDR_RESOLUTION_FAILED; } for (it = result; it != NULL; it = it->ai_next) { pb->pal.socket = socket(it->ai_family, it->ai_socktype, it->ai_protocol); if (pb->pal.socket == SOCKET_INVALID) { continue; } if (connect(pb->pal.socket, it->ai_addr, it->ai_addrlen) == -1) { socket_close(pb->pal.socket); pb->pal.socket = -1; continue; } break; } freeaddrinfo(result); if (NULL == it) { return PNR_CONNECT_FAILED; } socket_set_rcv_timeout(pb->pal.socket, pb->transaction_timeout_ms); return PNR_OK; }
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)); if (pb->state != PBS_IDLE) { 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; } return rslt; }
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)); if (p->state != PBS_IDLE) { return PNR_IN_PROGRESS; } rslt = pbcc_subscribe_prep(&p->core, channel, channel_group); if (PNR_STARTED == rslt) { p->trans = PBTT_SUBSCRIBE; p->core.last_result = PNR_STARTED; pbnc_fsm(p); rslt = p->core.last_result; } 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)); if (pb->state != PBS_IDLE) { 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; } return rslt; }
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)); if (pb->state != PBS_IDLE) { 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; } 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)); if (pb->state != PBS_IDLE) { 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; } 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)); if (pb->state != PBS_IDLE) { 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; } return rslt; }
enum pubnub_res pubnub_global_here_now(pubnub_t *pb) { enum pubnub_res rslt; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); if (pb->state != PBS_IDLE) { return PNR_IN_PROGRESS; } rslt = pbcc_here_now_prep(&pb->core, NULL, NULL); if (PNR_STARTED == rslt) { pb->trans = PBTT_GLOBAL_HERENOW; pb->core.last_result = PNR_STARTED; pbnc_fsm(pb); rslt = pb->core.last_result; } 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)); if (pb->state != PBS_IDLE) { 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; } return rslt; }
enum pubnub_res pubnub_time(pubnub_t *p) { enum pubnub_res rslt; PUBNUB_ASSERT(pb_valid_ctx_ptr(p)); if (p->state != PBS_IDLE) { 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; } return rslt; }
bool pnfntst_got_messages(pubnub_t *p, ...) { char const *aMsgs[16]; uint16_t missing; size_t count = 0; va_list vl; PUBNUB_ASSERT(pb_valid_ctx_ptr(p)); va_start(vl, p); while (count < 16) { char const *msg = va_arg(vl, char*); if (NULL == msg) { break; } aMsgs[count++] = msg; } va_end(vl); if ((0 == count) || (count > 16)) { return false; } missing = (0x01 << count) - 1; while (missing) { size_t i; char const *msg = pubnub_get(p); if (NULL == msg) { break; } for (i = 0; i < count; ++i) { if ((missing & (0x01 << i)) && (strcmp(msg, aMsgs[i]) == 0)) { missing &= ~(0x01 << i); break; } } } return !missing; }
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_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 pbpal_resolv_n_connect_result pbpal_check_connect(pubnub_t *pb) { fd_set write_set; int rslt; struct timeval timev = { 0, 300000 }; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT(pb->state == PBS_WAIT_CONNECT); FD_ZERO(&write_set); FD_SET(pb->pal.socket, &write_set); rslt = select(pb->pal.socket + 1, NULL, &write_set, NULL, &timev); if (SOCKET_ERROR == rslt) { PUBNUB_LOG_ERROR("pbpal_connected(): select() Error!\n"); return pbpal_connect_resource_failure; } else if (rslt > 0) { PUBNUB_LOG_TRACE("pbpal_connected(): select() event\n"); return pbpal_connect_success; } PUBNUB_LOG_TRACE("pbpal_connected(): no select() events\n"); return pbpal_connect_wouldblock; }
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_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; }
void pubnub_set_auth(pubnub_t *pb, const char *auth) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); pbcc_set_auth(&pb->core, auth); }
void pubnub_cancel(pubnub_t *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); pbnc_stop(pb, PNR_CANCELLED); }
void pubnub_set_uuid(pubnub_t *pb, const char *uuid) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); pbcc_set_uuid(&pb->core, uuid); }
char const *pubnub_get_channel(pubnub_t *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pbcc_get_channel(&pb->core); }
char const *pubnub_uuid_get(pubnub_t const *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->core.uuid; }
void *pubnub_get_user_data(pubnub_t *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->user_data; }
enum pubnub_res pubnub_last_result(pubnub_t *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->core.last_result; }
char const *pubnub_auth_get(pubnub_t const *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->core.auth; }
int pubnub_last_http_code(pubnub_t const *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->http_code; }
char const *pubnub_last_time_token(pubnub_t const *pb) { PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); return pb->core.timetoken; }
bool pnfntst_subscribe_and_check(pubnub_t *p, char const *channel, char const*chgroup, unsigned ms, ...) { char const *aMsgs[16]; char const *aChan[16]; uint16_t missing; size_t count = 0; pnfntst_timer_t *tmr; va_list vl; PUBNUB_ASSERT(pb_valid_ctx_ptr(p)); va_start(vl, ms); while (count < 16) { char const *msg = va_arg(vl, char*); if (NULL == msg) { break; } aMsgs[count] = msg; msg = va_arg(vl, char*); if (NULL == msg) { return false; } aChan[count] = msg; ++count; } va_end(vl); if ((0 == count) || (count > 16)) { return false; } missing = (0x01 << count) - 1; tmr = pnfntst_alloc_timer(); if (NULL == tmr) { puts("subscribe and check: timer alloc failed"); return false; } pnfntst_start_timer(tmr, ms); while (pnfntst_timer_is_running(tmr) && missing) { enum pubnub_res pbres; if (PNR_STARTED != pubnub_subscribe(p, channel, chgroup)) { puts("subscribe and check: subscribe failed"); break; } while (pnfntst_timer_is_running(tmr)) { pbres = pubnub_last_result(p); if (pbres != PNR_STARTED) { break; } } if (pbres != PNR_OK) { printf("subscribe and check: subscribe error %d\n", pbres); break; } do { size_t i; char const *msg = pubnub_get(p); char const *chan = pubnub_get_channel(p); if ((NULL == msg) || (NULL == chan)) { break; } for (i = 0; i < count; ++i) { if ((missing & (0x01 << i)) && (strcmp(msg, aMsgs[i]) == 0) && (strcmp(chan, aChan[i]) == 0) ) { missing &= ~(0x01 << i); break; } } } while (missing); } pnfntst_free_timer(tmr); return !missing; }
enum pbpal_resolv_n_connect_result pbpal_resolv_and_connect(pubnub_t *pb) { SSL *ssl = NULL; int rslt; char const* origin = PUBNUB_ORIGIN_SETTABLE ? pb->origin : PUBNUB_ORIGIN; PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); PUBNUB_ASSERT_OPT((pb->state == PBS_READY) || (pb->state == PBS_WAIT_CONNECT)); if (!pb->options.useSSL) { return resolv_and_connect_wout_SSL(pb); } if (NULL == pb->pal.ctx) { PUBNUB_LOG_TRACE("pb=%p: Don't have SSL_CTX\n", pb); pb->pal.ctx = SSL_CTX_new(SSLv23_client_method()); if (NULL == pb->pal.ctx) { ERR_print_errors_cb(print_to_pubnub_log, NULL); PUBNUB_LOG_ERROR("pb=%p SSL_CTX_new failed\n", pb); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Got SSL_CTX\n", pb); add_pubnub_cert(pb->pal.ctx); } if (NULL == pb->pal.socket) { PUBNUB_LOG_TRACE("pb=%p: Don't have BIO\n", pb); pb->pal.socket = BIO_new_ssl_connect(pb->pal.ctx); if (PUBNUB_TIMERS_API) { pb->pal.connect_timeout = time(NULL) + pb->transaction_timeout_ms/1000; } } else { BIO_get_ssl(pb->pal.socket, &ssl); if (NULL == ssl) { return resolv_and_connect_wout_SSL(pb); } ssl = NULL; } if (NULL == pb->pal.socket) { ERR_print_errors_cb(print_to_pubnub_log, NULL); return pbpal_resolv_resource_failure; } PUBNUB_LOG_TRACE("pb=%p: Using BIO == %p\n", pb, pb->pal.socket); BIO_get_ssl(pb->pal.socket, &ssl); PUBNUB_ASSERT(NULL != ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* maybe not auto_retry? */ if (pb->pal.session != NULL) { SSL_set_session(ssl, pb->pal.session); } BIO_set_conn_hostname(pb->pal.socket, origin); BIO_set_conn_port(pb->pal.socket, "https"); if (pb->pal.ip_timeout != 0) { if (pb->pal.ip_timeout < time(NULL)) { pb->pal.ip_timeout = 0; } else { PUBNUB_LOG_TRACE("SSL re-connect to: %d.%d.%d.%d\n", pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); BIO_set_conn_ip(pb->pal.socket, pb->pal.ip); } } BIO_set_nbio(pb->pal.socket, !pb->options.use_blocking_io); WATCH_ENUM(pb->options.use_blocking_io); if (BIO_do_connect(pb->pal.socket) <= 0) { if (BIO_should_retry(pb->pal.socket) && PUBNUB_TIMERS_API && (pb->pal.connect_timeout > time(NULL))) { PUBNUB_LOG_TRACE("pb=%p: BIO_should_retry\n", pb); return pbpal_connect_wouldblock; } /* Expire the IP for the next connect */ pb->pal.ip_timeout = 0; ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); pb->pal.session = NULL; } PUBNUB_LOG_ERROR("pb=%p: BIO_do_connect failed\n", pb); return pbpal_connect_failed; } PUBNUB_LOG_TRACE("pb=%p: BIO connected\n", pb); { int fd = BIO_get_fd(pb->pal.socket, NULL); socket_set_rcv_timeout(fd, pb->transaction_timeout_ms); } rslt = SSL_get_verify_result(ssl); if (rslt != X509_V_OK) { PUBNUB_LOG_WARNING("pb=%p: SSL_get_verify_result() failed == %d(%s)\n", pb, rslt, X509_verify_cert_error_string(rslt)); ERR_print_errors_cb(print_to_pubnub_log, NULL); if (pb->options.fallbackSSL) { BIO_free_all(pb->pal.socket); pb->pal.socket = NULL; return resolv_and_connect_wout_SSL(pb); } return pbpal_connect_failed; } PUBNUB_LOG_INFO("pb=%p: SSL session reused: %s\n", pb, SSL_session_reused(ssl) ? "yes" : "no"); if (pb->pal.session != NULL) { SSL_SESSION_free(pb->pal.session); } pb->pal.session = SSL_get1_session(ssl); if (0 == pb->pal.ip_timeout) { pb->pal.ip_timeout = SSL_SESSION_get_time(pb->pal.session) + SSL_SESSION_get_timeout(pb->pal.session); memcpy(pb->pal.ip, BIO_get_conn_ip(pb->pal.socket), 4); } PUBNUB_LOG_TRACE("pb=%p: SSL connected to IP: %d.%d.%d.%d\n", pb, pb->pal.ip[0], pb->pal.ip[1], pb->pal.ip[2], pb->pal.ip[3]); return pbpal_connect_success; }