diff --git a/.pubnub.yml b/.pubnub.yml index cf06a923..75da8cd7 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,7 +1,16 @@ name: c-core -version: 2.8.4 +version: 2.9.0 scm: github.com/pubnub/c-core changelog: + - version: v2.9.0 + date: Aug 9, 2019 + changes: + - type: enhancement + text: Introduce Signals support + - type: enhancement + text: Support subscribe V2 in C++ & Qt + - type: enhancement + text: Add pubnub_stop() for graceful shutdown when using callback interface - version: v2.8.4 date: Jun 12, 2019 changes: @@ -318,6 +327,7 @@ features: - PUBLISH-GET - PUBLISH-SYNC - PUBLISH-ASYNC + - SIGNAL-SEND storage: - STORAGE-INCLUDE-TIMETOKEN - STORAGE-COUNT @@ -332,6 +342,7 @@ features: - SUBSCRIBE-PRESENCE-CHANNELS - SUBSCRIBE-PRESENCE-CHANNELS-GROUPS - SUBSCRIBE-WILDCARD + - SUBSCRIBE-SIGNAL-LISTENER supported-platforms: - version: PubNub POSIX C SDK diff --git a/core/Makefile b/core/Makefile index 01b5203b..7c36bb38 100644 --- a/core/Makefile +++ b/core/Makefile @@ -1,4 +1,4 @@ -PROJECT_SOURCEFILES = pubnub_pubsubapi.c pubnub_coreapi.c pubnub_ccore_pubsub.c pubnub_ccore.c pubnub_netcore.c pubnub_alloc_static.c pubnub_assert_std.c pubnub_json_parse.c pubnub_keep_alive.c pubnub_helper.c pubnub_url_encode.c +PROJECT_SOURCEFILES = pubnub_pubsubapi.c pubnub_coreapi.c pubnub_ccore_pubsub.c pubnub_ccore.c pubnub_netcore.c pubnub_alloc_static.c pubnub_assert_std.c pubnub_json_parse.c pubnub_keep_alive.c pubnub_helper.c pubnub_url_encode.c ../lib/pb_strnlen_s.c all: pubnub_proxy_unittest pubnub_timer_list_unittest unittest diff --git a/core/W_makefile.mk b/core/W_makefile.mk index bbfc2880..f512b96f 100644 --- a/core/W_makefile.mk +++ b/core/W_makefile.mk @@ -1,4 +1,4 @@ -PROJECT_SOURCEFILES = pubnub_pubsubapi.c pubnub_coreapi.c pubnub_ccore_pubsub.c pubnub_ccore.c pubnub_url_encode.c pubnub_netcore.c pubnub_alloc_static.c pubnub_assert_std.c pubnub_json_parse.c pubnub_keep_alive.c ..\core\pubnub_helper.c ..\core\c99\snprintf.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c +PROJECT_SOURCEFILES = pubnub_pubsubapi.c pubnub_coreapi.c pubnub_ccore_pubsub.c pubnub_ccore.c pubnub_url_encode.c pubnub_netcore.c pubnub_alloc_static.c pubnub_assert_std.c pubnub_json_parse.c pubnub_keep_alive.c ..\core\pubnub_helper.c ..\core\c99\snprintf.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c ..\lib\pb_strnlen_s.c all: pubnub_proxy_NTLM_test.exe diff --git a/core/pbcc_objects_api.c b/core/pbcc_objects_api.c new file mode 100644 index 00000000..24cbd809 --- /dev/null +++ b/core/pbcc_objects_api.c @@ -0,0 +1,687 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#include "pubnub_internal.h" +#include "pubnub_version.h" +#include "pubnub_assert.h" +#include "pubnub_json_parse.h" +#include "pubnub_log.h" +#include "pubnub_url_encode.h" +#include "lib/pb_strnlen_s.h" +#include "pbcc_objects_api.h" + +#include +#include + +/* Maximum number of objects to return in response */ +#define MAX_OBJECTS_LIMIT 100 +/** Maximum include string element length */ +#define MAX_INCLUDE_ELEM_LENGTH 30 + +enum pubnub_res append_url_param_include(struct pbcc_context* pb, + char const** include, + size_t include_count) +{ + unsigned i; + if ((NULL == include) && (include_count != 0)) { + PUBNUB_LOG_ERROR("append_url_param_include(pbcc=%p) - Invalid params: " + "include=NULL, include_count=%lu\n", + pb, + (unsigned long)include_count); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + for (i = 0; i < include_count; i++) { + size_t param_val_len; + if (NULL == include[i]) { + PUBNUB_LOG_ERROR("append_url_param_include(pbcc=%p) - Invalid params: " + "include[%u]=NULL, include_count=%lu\n", + pb, + i, + (unsigned long)include_count); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + param_val_len = pb_strnlen_s(include[i], MAX_INCLUDE_ELEM_LENGTH); + if ((pb->http_buf_len + 1 + param_val_len + 1) > sizeof pb->http_buf) { + PUBNUB_LOG_ERROR("append_url_param_include(pbcc=%p) - Ran out of buffer while appending " + "include params : " + "include[%u]='%s', include_count=%lu\n", + pb, + i, + include[i], + (unsigned long)include_count); + + return PNR_TX_BUFF_TOO_SMALL; + } + if (0 == i) { + APPEND_URL_PARAM_M(pb, "include", include[0], '&'); + } + else { + pb->http_buf_len += snprintf(pb->http_buf + pb->http_buf_len, + sizeof pb->http_buf - pb->http_buf_len, + "​,%s", + include[i]); + } + } + + return PNR_OK; +} + + +enum pubnub_res pbcc_fetch_all_users_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(limit > MAX_OBJECTS_LIMIT); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users", + pb->subscribe_key); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + if (limit > 0) { + APPEND_URL_PARAM_UNSIGNED_M(pb, "limit", (unsigned)limit, '&'); + } + APPEND_URL_PARAM_M(pb, "start", start, '&'); + if (NULL == start) { + APPEND_URL_PARAM_M(pb, "end", end, '&'); + } + APPEND_URL_PARAM_TRIBOOL_M(pb, "count", count, '&'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_create_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + const char* user_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(user_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "/v1​/objects​/%s/users", + pb->subscribe_key); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, user_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_fetch_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* user_id) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + if (NULL == user_id) { + PUBNUB_LOG_ERROR("pbcc_fetch_user_prep(pbcc=%p) - Invalid param: " + "user_id=NULL\n", + pb); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_update_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* user_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + struct pbjson_elem elem; + struct pbjson_elem parsed_id; + enum pbjson_object_name_parse_result json_rslt; + + PUBNUB_ASSERT_OPT(user_obj != NULL); + + elem.end = pbjson_find_end_element(user_obj, + user_obj + pb_strnlen_s(user_obj, PUBNUB_MAX_OBJECT_LENGTH)); + if ((*user_obj != '{') || (*elem.end != '}')) { + PUBNUB_LOG_ERROR("pbcc_update_user_prep(pbcc=%p) - Invalid param: User object is not JSON - " + "user_obj='%s'\n", + pb, + user_obj); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + elem.start = user_obj; + json_rslt = pbjson_get_object_value(&elem, "id", &parsed_id); + if (json_rslt != jonmpOK) { + PUBNUB_LOG_ERROR("pbcc_update_user_prep(pbcc=%p) - Invalid param: " + "pbjson_get_object_value(\"id\") reported an error: %s\n", + pb, + pbjson_object_name_parse_result_2_string(json_rslt)); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + if ((*parsed_id.start != '"') || (*parsed_id.end != '"')) { + PUBNUB_LOG_ERROR("pbcc_update_user_prep(pbcc=%p) - Invalid param: " + "'id' key value is not a string - id=%.*s\n", + pb, + (int)(parsed_id.end - parsed_id.start + 1), + parsed_id.start); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%.*s", + pb->subscribe_key, + (int)(parsed_id.end - parsed_id.start - 2), + parsed_id.start + 1); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, user_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_delete_user_prep(struct pbcc_context* pb, char const* user_id) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + + if (NULL == user_id) { + PUBNUB_LOG_ERROR("pbcc_delete_user_prep(pbcc=%p) - Invalid param: " + "user_id=NULL\n", + pb); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + return PNR_STARTED; +} + + +enum pubnub_res pbcc_fetch_all_spaces_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(limit > MAX_OBJECTS_LIMIT); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces", + pb->subscribe_key); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + if (limit > 0) { + APPEND_URL_PARAM_UNSIGNED_M(pb, "limit", (unsigned)limit, '&'); + } + APPEND_URL_PARAM_M(pb, "start", start, '&'); + if (NULL == start) { + APPEND_URL_PARAM_M(pb, "end", end, '&'); + } + APPEND_URL_PARAM_TRIBOOL_M(pb, "count", count, '&'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_create_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + const char* space_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(space_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "/v1​/objects​/%s/spaces", + pb->subscribe_key); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, space_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_fetch_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* space_id) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + if (NULL == space_id) { + PUBNUB_LOG_ERROR("pbcc_fetch_space_prep(pbcc=%p) - Invalid param: " + "space_id=NULL\n", + pb); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces/%s", + pb->subscribe_key, + space_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_update_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* space_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + struct pbjson_elem elem; + struct pbjson_elem parsed_id; + enum pbjson_object_name_parse_result json_rslt; + + PUBNUB_ASSERT_OPT(space_obj != NULL); + + elem.end = pbjson_find_end_element(space_obj, + space_obj + pb_strnlen_s(space_obj, PUBNUB_MAX_OBJECT_LENGTH)); + if ((*space_obj != '{') || (*elem.end != '}')) { + PUBNUB_LOG_ERROR("pbcc_update_space_prep(pbcc=%p) - " + "Invalid param: Space object is not JSON - " + "space_obj='%s'\n", + pb, + space_obj); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + elem.start = space_obj; + json_rslt = pbjson_get_object_value(&elem, "id", &parsed_id); + if (json_rslt != jonmpOK) { + PUBNUB_LOG_ERROR("pbcc_update_space_prep(pbcc=%p) - Invalid param: " + "pbjson_get_object_value(\"id\") reported an error: %s\n", + pb, + pbjson_object_name_parse_result_2_string(json_rslt)); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + if ((*parsed_id.start != '"') || (*parsed_id.end != '"')) { + PUBNUB_LOG_ERROR("pbcc_update_space_prep(pbcc=%p) - Invalid param: " + "'id' key value is not a string - id=%.*s\n", + pb, + (int)(parsed_id.end - parsed_id.start + 1), + parsed_id.start); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces/%.*s", + pb->subscribe_key, + (int)(parsed_id.end - parsed_id.start - 2), + parsed_id.start + 1); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, space_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_delete_space_prep(struct pbcc_context* pb, char const* space_id) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + + if (NULL == space_id) { + PUBNUB_LOG_ERROR("pbcc_delete_space_prep(pbcc=%p) - Invalid param: " + "space_id=NULL\n", + pb); + + return PNR_OBJECTS_API_INVALID_PARAM; + } + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces/%s", + pb->subscribe_key, + space_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + return PNR_STARTED; +} + + +enum pubnub_res pbcc_fetch_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(limit > MAX_OBJECTS_LIMIT); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s/spaces", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + if (limit > 0) { + APPEND_URL_PARAM_UNSIGNED_M(pb, "limit", (unsigned)limit, '&'); + } + APPEND_URL_PARAM_M(pb, "start", start, '&'); + if (NULL == start) { + APPEND_URL_PARAM_M(pb, "end", end, '&'); + } + APPEND_URL_PARAM_TRIBOOL_M(pb, "count", count, '&'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_add_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(update_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s/spaces", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, update_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_update_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(update_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s/spaces", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, update_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_remove_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(update_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/users/%s/spaces", + pb->subscribe_key, + user_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, update_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_fetch_members_in_space_prep(struct pbcc_context* pb, + char const* space_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(limit > MAX_OBJECTS_LIMIT); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces/%s/users", + pb->subscribe_key, + space_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + if (limit > 0) { + APPEND_URL_PARAM_UNSIGNED_M(pb, "limit", (unsigned)limit, '&'); + } + APPEND_URL_PARAM_M(pb, "start", start, '&'); + if (NULL == start) { + APPEND_URL_PARAM_M(pb, "end", end, '&'); + } + APPEND_URL_PARAM_TRIBOOL_M(pb, "count", count, '&'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + rslt = append_url_param_include(pb, include, include_count); + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_update_members_in_space_prep(struct pbcc_context* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + enum pubnub_res rslt; + + PUBNUB_ASSERT_OPT(update_obj != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "​/v1​/objects​/%s/spaces/%s/users", + pb->subscribe_key, + space_id); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + rslt = append_url_param_include(pb, include, include_count); + if (PNR_OK == rslt) { + APPEND_MESSAGE_BODY_M(pb, update_obj); + } + + return (rslt != PNR_OK) ? rslt : PNR_STARTED; +} + + +enum pubnub_res pbcc_parse_objects_api_response(struct pbcc_context* pb) +{ + char* reply = pb->http_reply; + int replylen = pb->http_buf_len; + struct pbjson_elem elem; + struct pbjson_elem parsed_status; + enum pbjson_object_name_parse_result json_rslt; + + if ((replylen < 2) || (reply[0] != '{')) { + return PNR_FORMAT_ERROR; + } + + pb->chan_ofs = pb->chan_end = 0; + pb->msg_ofs = pb->msg_end = 0; + + elem.end = pbjson_find_end_element(reply, reply + replylen); + if ((*reply != '{') || (*elem.end != '}')) { + PUBNUB_LOG_ERROR("pbcc_parse_objects_api_response(pbcc=%p) - Invalid: " + "response from server is not JSON - response='%s'\n", + pb, + reply); + + return PNR_FORMAT_ERROR; + } + elem.start = reply; + json_rslt = pbjson_get_object_value(&elem, "status", &parsed_status); + if (json_rslt != jonmpOK) { + PUBNUB_LOG_ERROR("pbcc_parse_objects_api_response(pbcc=%p) - Invalid response: " + "pbjson_get_object_value(\"status\") reported an error: %s\n", + pb, + pbjson_object_name_parse_result_2_string(json_rslt)); + + return PNR_FORMAT_ERROR; + } + if (strncmp("\"ok\"", parsed_status.start, parsed_status.end - parsed_status.start) != 0) { + return PNR_OBJECTS_API_ERROR; + } + + return PNR_OBJECTS_API_OK; +} diff --git a/core/pbcc_objects_api.h b/core/pbcc_objects_api.h new file mode 100644 index 00000000..a33ffd9c --- /dev/null +++ b/core/pbcc_objects_api.h @@ -0,0 +1,160 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PBCC_OBJECTS_API +#define INC_PBCC_OBJECTS_API + +#include "pubnub_api_types.h" + +struct pbcc_context; + + +/** @file pbcc_objects_api.h + + This has the functions for formating and parsing the + requests and responses for 'Objects API' transactions + */ + +/** Prepares the 'fetch_all_users' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_all_users_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + +/** Prepares the 'create_user' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_create_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + const char* user_obj); + +/** Prepares the 'fetch_user' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* user_id); + +/** Prepares the 'update_user' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_update_user_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* user_obj); + + + +/** Prepares the 'delete_user' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_delete_user_prep(struct pbcc_context* pb, char const* user_id); + + +/** Prepares the 'fetch_all_spaces' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_all_spaces_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + + +/** Prepares the 'create_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_create_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + const char* space_obj); + + +/** Prepares the 'fetch_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* space_id); + + +/** Prepares the 'update_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_update_space_prep(struct pbcc_context* pb, + char const** include, + size_t include_count, + char const* space_obj); + + +/** Prepares the 'delete_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_delete_space_prep(struct pbcc_context* pb, char const* space_id); + + +/** Prepares the 'fetch_users_space_memberships' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + + +/** Prepares the 'update_users_space_memberships' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_update_users_space_memberships_prep(struct pbcc_context* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj); + +/** Prepares the 'fetch_members_in_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_fetch_members_in_space_prep(struct pbcc_context* pb, + char const* space_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + +/** Prepares the 'update_members_in_space' transaction, mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_update_members_in_space_prep(struct pbcc_context* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Parses server response simply on any 'Objects API' transaction request. + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @retval PNR_OBJECTS_API_OK on success + @retval PNR_OBJECTS_API_ERROR on error + */ +enum pubnub_res pbcc_parse_objects_api_response(struct pbcc_context* pb); + + +#endif /* !defined INC_PBCC_OBJECTS_API */ diff --git a/core/pbcc_subscribe_v2.c b/core/pbcc_subscribe_v2.c new file mode 100644 index 00000000..8d425db9 --- /dev/null +++ b/core/pbcc_subscribe_v2.c @@ -0,0 +1,259 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#include "pubnub_internal.h" + +#include "pubnub_version.h" +#include "pubnub_assert.h" +#include "pubnub_json_parse.h" +#include "pubnub_log.h" +#include "pubnub_url_encode.h" +#include "lib/pb_strnlen_s.h" +#include "pbcc_subscribe_v2.h" + + +#include +#include + + +/** It has to contain the `t` field, with another `t` for the + timetoken (as string) and `tr` for the region (integer) and the + `m` field for the message(s) array. +*/ +#define MIN_SUBSCRIBE_V2_RESPONSE_LENGTH 40 + +enum pubnub_res pbcc_subscribe_v2_prep(struct pbcc_context* p, + char const* channel, + char const* channel_group, + unsigned* heartbeat, + char const* filter_expr) +{ + char region_str[20]; + char const* tr; + + 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; + } + + if ('\0' == p->timetoken[0]) { + p->timetoken[0] = '0'; + p->timetoken[1] = '\0'; + tr = NULL; + } + else { + snprintf(region_str, sizeof region_str, "%d", p->region); + tr = region_str; + } + p->http_content_len = 0; + p->msg_ofs = p->msg_end = 0; + + p->http_buf_len = snprintf( + p->http_buf, sizeof p->http_buf, "/v2/subscribe/%s/", p->subscribe_key); + APPEND_URL_ENCODED_M(p, channel); + p->http_buf_len += snprintf(p->http_buf + p->http_buf_len, + sizeof p->http_buf - p->http_buf_len, + "/0?tt=%s&pnsdk=%s", + p->timetoken, + pubnub_uname()); + APPEND_URL_PARAM_M(p, "tr", tr, '&'); + 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_PARAM_ENCODED_M(p, "filter-expr", filter_expr, '&'); + APPEND_URL_OPT_PARAM_UNSIGNED_M(p, "heartbeat", heartbeat, '&'); + + return PNR_STARTED; +} + + +enum pubnub_res pbcc_parse_subscribe_v2_response(struct pbcc_context* p) +{ + enum pbjson_object_name_parse_result jpresult; + struct pbjson_elem el; + struct pbjson_elem found; + char* reply = p->http_reply; + + if (p->http_buf_len < MIN_SUBSCRIBE_V2_RESPONSE_LENGTH) { + return PNR_FORMAT_ERROR; + } + if ((reply[0] != '{') || (reply[p->http_buf_len - 1] != '}')) { + return PNR_FORMAT_ERROR; + } + + el.start = p->http_reply; + el.end = p->http_reply + p->http_buf_len; + jpresult = pbjson_get_object_value(&el, "t", &found); + if (jonmpOK == jpresult) { + struct pbjson_elem titel; + if (jonmpOK == pbjson_get_object_value(&found, "t", &titel)) { + size_t len = titel.end - titel.start - 2; + if ((*titel.start != '"') || (titel.end[-1] != '"')) { + PUBNUB_LOG_ERROR("Time token in response is not a string\n"); + return PNR_FORMAT_ERROR; + } + if (len >= sizeof p->timetoken) { + PUBNUB_LOG_ERROR( + "Time token in response, length %lu, longer than max %lu\n", + (unsigned long)len, + (unsigned long)(sizeof p->timetoken - 1)); + return PNR_FORMAT_ERROR; + } + + memcpy(p->timetoken, titel.start + 1, len); + p->timetoken[len] = '\0'; + } + else { + PUBNUB_LOG_ERROR( + "No timetoken value in subscribe V2 response found\n"); + return PNR_FORMAT_ERROR; + } + if (jonmpOK == pbjson_get_object_value(&found, "r", &titel)) { + p->region = strtol(titel.start, NULL, 0); + } + else { + PUBNUB_LOG_ERROR( + "No region value in subscribe V2 response found\n"); + return PNR_FORMAT_ERROR; + } + } + else { + PUBNUB_LOG_ERROR( + "No timetoken in subscribe V2 response found, error=%d\n", jpresult); + return PNR_FORMAT_ERROR; + } + + p->chan_ofs = p->chan_end = 0; + + /* We should optimize this to not look from the start again. + */ + jpresult = pbjson_get_object_value(&el, "m", &found); + if (jonmpOK == jpresult) { + p->msg_ofs = (unsigned)(found.start - reply + 1); + p->msg_end = (unsigned)(found.end - reply - 1); + } + else { + PUBNUB_LOG_ERROR( + "No message array subscribe V2 response found, error=%d\n", jpresult); + return PNR_FORMAT_ERROR; + } + + return PNR_OK; +} + + +struct pubnub_v2_message pbcc_get_msg_v2(struct pbcc_context* p) +{ + enum pbjson_object_name_parse_result jpresult; + struct pbjson_elem el; + struct pbjson_elem found; + struct pubnub_v2_message rslt; + char const* start; + char const* end; + char const* seeker; + + memset(&rslt, 0, sizeof rslt); + + if (p->msg_ofs >= p->msg_end) { + return rslt; + } + start = p->http_reply + p->msg_ofs; + if (*start != '{') { + PUBNUB_LOG_ERROR( + "Message subscribe V2 response is not a JSON object\n"); + return rslt; + } + end = p->http_reply + p->msg_end; + seeker = pbjson_find_end_complex(start, end); + if (seeker == end) { + PUBNUB_LOG_ERROR( + "Message subscribe V2 response has no end of JSON object\n"); + return rslt; + } + + p->msg_ofs = (unsigned)(seeker - p->http_reply + 2); + el.start = start; + el.end = seeker; + + /* @todo We should iterate over elements of JSON message object, + instead of looking from the start, again and again. + */ + + jpresult = pbjson_get_object_value(&el, "d", &found); + if (jonmpOK == jpresult) { + rslt.payload.ptr = (char*)found.start; + rslt.payload.size = found.end - found.start; + } + else { + PUBNUB_LOG_ERROR("pbcc=%p: No message payload in subscribe V2 response " + "found, error=%d\n", + p, + jpresult); + return rslt; + } + + jpresult = pbjson_get_object_value(&el, "c", &found); + if (jonmpOK == jpresult) { + rslt.channel.ptr = (char*)found.start + 1; + rslt.channel.size = found.end - found.start - 2; + } + else { + PUBNUB_LOG_ERROR("pbcc=%p: No message channel in subscribe V2 response " + "found, error=%d\n", + p, + jpresult); + return rslt; + } + + jpresult = pbjson_get_object_value(&el, "e", &found); + if (jonmpOK == jpresult) { + if (pbjson_elem_equals_string(&found, "1")) { + rslt.message_type = pbsbSignal; + } + else { + rslt.message_type = pbsbPublished; + } + } + else { + rslt.message_type = pbsbPublished; + } + + jpresult = pbjson_get_object_value(&el, "p", &found); + if (jonmpOK == jpresult) { + struct pbjson_elem titel; + if (jonmpOK == pbjson_get_object_value(&found, "t", &titel)) { + if ((*titel.start != '"') || (titel.end[-1] != '"')) { + PUBNUB_LOG_ERROR("Time token in response is not a string\n"); + return rslt; + } + rslt.tt.ptr = (char*)titel.start + 1; + rslt.tt.size = titel.end - titel.start - 2; + } + else { + PUBNUB_LOG_ERROR( + "No timetoken value in subscribe V2 response found\n"); + return rslt; + } + } + else { + PUBNUB_LOG_ERROR("No message publish timetoken in subscribe V2 " + "response found, error=%d\n", + jpresult); + return rslt; + } + + if (jonmpOK == pbjson_get_object_value(&el, "b", &found)) { + rslt.match_or_group.ptr = (char*)found.start; + rslt.match_or_group.size = found.end - found.start; + } + + if (jonmpOK == pbjson_get_object_value(&el, "u", &found)) { + rslt.metadata.ptr = (char*)found.start; + rslt.metadata.size = found.end - found.start; + } + + return rslt; +} diff --git a/core/pbcc_subscribe_v2.h b/core/pbcc_subscribe_v2.h new file mode 100644 index 00000000..63800dda --- /dev/null +++ b/core/pbcc_subscribe_v2.h @@ -0,0 +1,37 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PBCC_SUBSCRIBE_V2 +#define INC_PBCC_SUBSCRIBE_V2 + +#include "pubnub_subscribe_v2_message.h" + +struct pbcc_context; + +/** Prepares the Subscribe_v2 operation (transaction), mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_subscribe_v2_prep(struct pbcc_context* p, + char const* channel, + char const* channel_group, + unsigned* heartbeat, + char const* filter_expr); + + +/** Parses the string received as a response for a subscribe_v2 operation + (transaction). This checks if the response is valid, and, if it + is, prepares for giving the v2 messages that are received in the + response to the user (via pbcc_get_msg_v2()). + + @param p The Pubnub C core context to parse the response "in" + @return PNR_OK: OK, PNR_FORMAT_ERROR: error (invalid response) + */ +enum pubnub_res pbcc_parse_subscribe_v2_response(struct pbcc_context* p); + + +/** Returns the next v2 message from the Pubnub C Core context. + Empty structure if there are no (more) v2 messages(checked through presence + of valid timetoken, or payload) + */ +struct pubnub_v2_message pbcc_get_msg_v2(struct pbcc_context* p); + + +#endif /* !defined INC_PBCC_SUBSCRIBE_V2 */ diff --git a/core/pbntf_trans_outcome_common.h b/core/pbntf_trans_outcome_common.h index daf80abb..4a4df640 100644 --- a/core/pbntf_trans_outcome_common.h +++ b/core/pbntf_trans_outcome_common.h @@ -1,4 +1,6 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PBNTF_TRANS_OUTCOME_COMMON +#define INC_PBNTF_TRANS_OUTCOME_COMMON /** This macro does the common "stuff to do" on the outcome of a transaction. Should be used by all `pbntf_trans_outcome()` @@ -23,6 +25,8 @@ default: \ break; \ } \ - M_pb_->flags.is_publish_via_post = false; \ + M_pb_->method = pubnubSendViaGET; \ M_pb_->state = state; \ } while (0) + +#endif /* INC_PBNTF_TRANS_OUTCOME_COMMON */ diff --git a/core/pbpal.h b/core/pbpal.h index 4648a4b2..3779e116 100644 --- a/core/pbpal.h +++ b/core/pbpal.h @@ -227,6 +227,7 @@ int pbpal_set_blocking_io(pubnub_t *pb); void pbpal_free(pubnub_t *pb); #if PUBNUB_USE_MULTIPLE_ADDRESSES +struct pubnub_multi_addresses; void pbpal_multiple_addresses_reset_counters(struct pubnub_multi_addresses* spare_addresses); #endif /* PUBNUB_USE_MULTIPLE_ADDRESSES */ #endif /* !defined INC_PBPAL */ diff --git a/core/pubnub_api_types.h b/core/pubnub_api_types.h index 08a7b97c..30702c85 100644 --- a/core/pubnub_api_types.h +++ b/core/pubnub_api_types.h @@ -8,7 +8,9 @@ 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. - */ + */ + +#include "pubnub_config.h" struct pubnub_; @@ -97,7 +99,13 @@ enum pubnub_res { /** Server reports an error in the response */ PNR_ERROR_ON_SERVER, /** Proxy authentication failed */ - PNR_AUTHENTICATION_FAILED + PNR_AUTHENTICATION_FAILED, + /** Objects API invalid parameter */ + PNR_OBJECTS_API_INVALID_PARAM, + /** Objects API transaction has finished successfully */ + PNR_OBJECTS_API_OK, + /** Objects API transaction reported an error */ + PNR_OBJECTS_API_ERROR }; /** 'pubnub_cancel()' return value */ @@ -105,7 +113,7 @@ enum pubnub_cancel_res { /** 'cancel' finished */ PN_CANCEL_FINISHED, /** 'cancel' started */ - PN_CANCEL_STARTED, + PN_CANCEL_STARTED }; /** Type of Pubnub operation/transaction */ @@ -116,6 +124,8 @@ enum pubnub_trans { PBTT_SUBSCRIBE, /** Publish operation/transaction */ PBTT_PUBLISH, + /** Signal operation/transaction */ + PBTT_SIGNAL, /** Leave (channel(s)) operation/transaction */ PBTT_LEAVE, /** Time (get from Pubnub server) operation/transaction */ @@ -153,15 +163,69 @@ enum pubnub_trans { /** Inform Pubnub that we're still working on channel and/or channel_group operation/transaction */ PBTT_HEARTBEAT, +#if PUBNUB_USE_SUBSCRIBE_V2 /** Subscribe V2 operation/transaction */ PBTT_SUBSCRIBE_V2, +#endif +#if PUBNUB_USE_ADVANCED_HISTORY /** Message counts(get counters of unread messages for a user, identified by UUID, for the list of channels specified) starting from given timetoken, or(exclusive or) list of timetokens per channel. If neither timetoken, nor channel_timetokens are specified, gets entire message history counts for channels listed. - */ + */ PBTT_MESSAGE_COUNTS, +#endif +#if PUBNUB_USE_OBJECTS_API + /** Objects API transaction. Returns a paginated list of users associated with the + subscription key. + */ + PBTT_FETCH_ALL_USERS, + /** Objects API transaction. Creates a user with the attributes specified. */ + PBTT_CREATE_USER, + /** Objects API transaction. Returns the user object specified with user_id */ + PBTT_FETCH_USER, + /** Objects API transaction. Updates users data( on pubnub server) specified with user_id */ + PBTT_UPDATE_USER, + /** Objects API transaction. Deletes user data( on pubnub server) specified with user_id */ + PBTT_DELETE_USER, + /** Objects API transaction. Returns a paginated list of spaces associated with the + subscription key. + */ + PBTT_FETCH_ALL_SPACES, + /** Objects API transaction. Creates a space with the attributes specified. */ + PBTT_CREATE_SPACE, + /** Objects API transaction. Returns the space object specified with space_id */ + PBTT_FETCH_SPACE, + /** Objects API transaction. Updates space data( on pubnub server) specified with space_id */ + PBTT_UPDATE_SPACE, + /** Objects API transaction. Deletes space data( on pubnub server) specified with space_id */ + PBTT_DELETE_SPACE, + /** Objects API transaction. Returns the space memberships of the user specified with user_id. + */ + PBTT_FETCH_USERS_SPACE_MEMBERSHIPS, + /** Objects API transaction. Adds the users space memberships specified with user_id. + */ + PBTT_ADD_USERS_SPACE_MEMBERSHIPS, + /** Objects API transaction. Updates the users space memberships specified with user_id. + */ + PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS, + /** Objects API transaction. Removes the users space memberships specified with user_id. + */ + PBTT_REMOVE_USERS_SPACE_MEMBERSHIPS, + /** Objects API transaction. Returns all users in the space specified by space_id. + */ + PBTT_FETCH_MEMBERS_IN_SPACE, + /** Objects API transaction. Adds the list of members of the space specified with space_id. + */ + PBTT_ADD_MEMBERS_IN_SPACE, + /** Objects API transaction. Updates the list of members of the space specified with space_id. + */ + PBTT_UPDATE_MEMBERS_IN_SPACE, + /** Objects API transaction. Removes the list of members of the space specified with space_id. + */ + PBTT_REMOVE_MEMBERS_IN_SPACE, +#endif /* PUBNUB_USE_OBJECTS_API */ /** Count the number of transaction types */ PBTT_MAX }; @@ -180,10 +244,13 @@ enum pubnub_tribool { pbccNotSet }; -enum pubnub_publish_method { - pubnubPublishViaPOST, - pubnubPublishViaPOSTwithGZIP, - pubnubPublishViaGET +enum pubnub_method { + pubnubSendViaGET, + pubnubSendViaPOST, + pubnubUsePATCH, + pubnubSendViaPOSTwithGZIP, + pubnubUsePATCHwithGZIP, + pubnubUseDELETE }; /** Enum that describes an error when checking parameters passed to a function */ diff --git a/core/pubnub_ccore_pubsub.c b/core/pubnub_ccore_pubsub.c index f3d66ec6..e4f9d53c 100644 --- a/core/pubnub_ccore_pubsub.c +++ b/core/pubnub_ccore_pubsub.c @@ -1,11 +1,13 @@ /* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ -#include "pubnub_ccore_pubsub.h" +#include "pubnub_internal.h" #include "pubnub_version.h" #include "pubnub_assert.h" -#include "pubnub_internal.h" #include "pubnub_json_parse.h" #include "pubnub_log.h" #include "pubnub_url_encode.h" +#include "lib/pb_strnlen_s.h" +#include "pubnub_ccore_pubsub.h" + #include #include @@ -27,7 +29,7 @@ void pbcc_init(struct pbcc_context* p, const char* publish_key, const char* subs p->decomp_http_reply = NULL; #endif /* PUBNUB_RECEIVE_GZIP_RESPONSE */ #endif /* PUBNUB_DYNAMIC_REPLY_BUFFER */ - p->message_to_publish = NULL; + p->message_to_send = NULL; #if PUBNUB_CRYPTO_API p->secret_key = NULL; @@ -345,15 +347,15 @@ enum pubnub_res pbcc_append_url_param(struct pbcc_context* pb, } -void pbcc_headers_for_publish_via_post(struct pbcc_context* pb, - char* header, - size_t max_length) +void pbcc_via_post_headers(struct pbcc_context* pb, + char* header, + size_t max_length) { char lines[] = "Content-Type: application/json\r\nContent-Length: "; unsigned length; PUBNUB_ASSERT_OPT(pb != NULL); - PUBNUB_ASSERT_OPT(pb->message_to_publish != NULL); + PUBNUB_ASSERT_OPT(pb->message_to_send != NULL); PUBNUB_ASSERT_OPT(header != NULL); PUBNUB_ASSERT_OPT(max_length > sizeof lines); memcpy(header, lines, sizeof lines - 1); @@ -368,8 +370,10 @@ void pbcc_headers_for_publish_via_post(struct pbcc_context* pb, return; } #endif - length = snprintf( - header, max_length, "%lu", (unsigned long)strlen(pb->message_to_publish)); + length = snprintf(header, + max_length, + "%lu", + (unsigned long)pb_strnlen_s(pb->message_to_send, PUBNUB_MAX_OBJECT_LENGTH)); PUBNUB_ASSERT_OPT(max_length > length); } @@ -409,13 +413,13 @@ enum pubnub_res pbcc_append_url_param_encoded(struct pbcc_context* pb, } -enum pubnub_res pbcc_publish_prep(struct pbcc_context* pb, - const char* channel, - const char* message, - bool store_in_history, - bool norep, - char const* meta, - enum pubnub_publish_method method) +enum pubnub_res pbcc_publish_prep(struct pbcc_context* pb, + const char* channel, + const char* message, + bool store_in_history, + bool norep, + char const* meta, + enum pubnub_method method) { char const* const uname = pubnub_uname(); char const* uuid = pbcc_uuid_get(pb); @@ -431,13 +435,10 @@ enum pubnub_res pbcc_publish_prep(struct pbcc_context* pb, pb->subscribe_key); APPEND_URL_ENCODED_M(pb, channel); APPEND_URL_LITERAL_M(pb, "/0"); - if (pubnubPublishViaGET == method) { + if (pubnubSendViaGET == method) { pb->http_buf[pb->http_buf_len++] = '/'; APPEND_URL_ENCODED_M(pb, message); } - else { - pb->message_to_publish = message; - } APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); @@ -458,11 +459,40 @@ enum pubnub_res pbcc_publish_prep(struct pbcc_context* pb, pb->http_buf[pb->http_buf_len++] = '='; rslt = pbcc_url_encode(pb, meta); } + if ((PNR_OK == rslt) && (method != pubnubSendViaGET)) { + APPEND_MESSAGE_BODY_M(pb, message); + } return (rslt != PNR_OK) ? rslt : PNR_STARTED; } +enum pubnub_res pbcc_signal_prep(struct pbcc_context* pb, + const char* channel, + const char* message) +{ + char const* const uname = pubnub_uname(); + char const* uuid = pbcc_uuid_get(pb); + + PUBNUB_ASSERT_OPT(message != NULL); + + pb->http_content_len = 0; + pb->http_buf_len = snprintf(pb->http_buf, + sizeof pb->http_buf, + "/signal/%s/%s/0/", + pb->publish_key, + pb->subscribe_key); + APPEND_URL_ENCODED_M(pb, channel); + APPEND_URL_LITERAL_M(pb, "/0/"); + APPEND_URL_ENCODED_M(pb, message); + APPEND_URL_PARAM_M(pb, "pnsdk", uname, '?'); + APPEND_URL_PARAM_M(pb, "uuid", uuid, '&'); + APPEND_URL_PARAM_M(pb, "auth", pb->auth, '&'); + + return PNR_STARTED; +} + + enum pubnub_res pbcc_subscribe_prep(struct pbcc_context* p, char const* channel, char const* channel_group, diff --git a/core/pubnub_ccore_pubsub.h b/core/pubnub_ccore_pubsub.h index 4c80c453..bace64c5 100644 --- a/core/pubnub_ccore_pubsub.h +++ b/core/pubnub_ccore_pubsub.h @@ -31,8 +31,8 @@ struct pbcc_context { /** The `auth` parameter to be sent to server. If NULL, don't send * any */ char const* auth; - /** Pointer to the message to publish via POST */ - char const* message_to_publish; + /** Pointer to the message to send via POST method */ + char const* message_to_send; /** The last recived subscribe time token. */ char timetoken[20]; @@ -121,16 +121,22 @@ struct pbcc_context { } \ } -#define APPEND_URL_LITERAL_M(pbc, string_literal) \ +#define APPEND_URL_LITERAL_M_IMP(pbc, string_literal) \ { \ - PUBNUB_ASSERT_OPT((string_literal) != NULL); \ if ((pbc)->http_buf_len + sizeof(string_literal) > sizeof (pbc)->http_buf) {\ + PUBNUB_LOG_ERROR("Error: Request buffer too small - cannot append url literal:\n"\ + "current_buffer_size = %lu\n" \ + "required_buffer_size = %lu\n", \ + (unsigned long)(sizeof (pbc)->http_buf), \ + (unsigned long)((pbc)->http_buf_len + 1 + sizeof(string_literal)));\ return PNR_TX_BUFF_TOO_SMALL; \ } \ strcpy((pbc)->http_buf + (pbc)->http_buf_len, (string_literal)); \ (pbc)->http_buf_len += sizeof(string_literal) - 1; \ } - + +#define APPEND_URL_LITERAL_M(pbc, string_literal) APPEND_URL_LITERAL_M_IMP(pbc, "" string_literal) + #define APPEND_URL_ENCODED_M(pbc, what) \ if ((what) != NULL) { \ enum pubnub_res rslt_ = pbcc_url_encode((pbc), (what)); \ @@ -185,6 +191,37 @@ struct pbcc_context { APPEND_URL_PARAM_M(pbc, name, v_, separator); \ } +#if PUBNUB_USE_GZIP_COMPRESSION +#define CHECK_IF_GZIP_COMPRESSED(pbc, message) \ + if ((pbc)->gzip_msg_len != 0) { \ + (pbc)->message_to_send = (message); \ + } \ + else { \ + (pbc)->message_to_send = strcpy((pbc)->http_buf + (pbc)->http_buf_len + 1,\ + (message)); \ + } +#else +#define CHECK_IF_GZIP_COMPRESSED(pbc, message) \ + (pbc)->message_to_send = strcpy((pbc)->http_buf + (pbc)->http_buf_len + 1, \ + (message)) +#endif /* PUBNUB_USE_GZIP_COMPRESSION */ + +#define APPEND_MESSAGE_BODY_M(pbc, message) \ + if ((message) != NULL) { \ + if (pb_strnlen_s(message, PUBNUB_MAX_OBJECT_LENGTH) > \ + sizeof (pbc)->http_buf - (pbc)->http_buf_len - 2) { \ + PUBNUB_LOG_ERROR("Error: Request buffer too small - cannot pack the message body:\n"\ + "current_buffer_size = %lu\n" \ + "required_buffer_size = %lu\n", \ + (unsigned long)(sizeof (pbc)->http_buf), \ + (unsigned long)((pbc)->http_buf_len + 2 + pb_strnlen_s(message,\ + PUBNUB_MAX_OBJECT_LENGTH)));\ + return PNR_TX_BUFF_TOO_SMALL; \ + } \ + (pbc)->http_buf[(pbc)->http_buf_len] = '\0'; \ + CHECK_IF_GZIP_COMPRESSED((pbc), (message)); \ + } + /** Initializes the Pubnub C core context */ void pbcc_init(struct pbcc_context* pbcc, @@ -236,7 +273,7 @@ typedef enum pubnub_res (*PFpbcc_parse_response_T)(struct pbcc_context*); pbcc_get_channel()). @param p The Pubnub C core context to parse the response "in" - @return 0: OK, -1: error (invalid response) + @return PNR_OK: OK, PNR_FORMAT_ERROR: error (invalid response) */ enum pubnub_res pbcc_parse_subscribe_response(struct pbcc_context* p); @@ -251,23 +288,30 @@ enum pubnub_res pbcc_parse_subscribe_response(struct pbcc_context* p); */ enum pubnub_res pbcc_parse_publish_response(struct pbcc_context* p); -/** Prepares HTTP header lines specific for publish 'via POST' +/** Prepares HTTP header lines specific for sending message 'via POST' method. @param p The Pubnub C core context with all necessary information @param header pointer to char array provided for placing these headers @param max_length maximum size of array provided */ -void pbcc_headers_for_publish_via_post(struct pbcc_context* p, char* header, size_t max_length); +void pbcc_via_post_headers(struct pbcc_context* p, char* header, size_t max_length); /** 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 norep, - char const* meta, - enum pubnub_publish_method method); +enum pubnub_res pbcc_publish_prep(struct pbcc_context* pb, + const char* channel, + const char* message, + bool store_in_history, + bool norep, + char const* meta, + enum pubnub_method method); + +/** Prepares the Signal operation (transaction), mostly by + formatting the URI of the HTTP request. + */ +enum pubnub_res pbcc_signal_prep(struct pbcc_context* pb, + const char* channel, + const char* message); /** Prepares the Subscribe operation (transaction), mostly by formatting the URI of the HTTP request. diff --git a/core/pubnub_coreapi_ex.c b/core/pubnub_coreapi_ex.c index fd5db47c..af4611df 100644 --- a/core/pubnub_coreapi_ex.c +++ b/core/pubnub_coreapi_ex.c @@ -22,7 +22,7 @@ struct pubnub_publish_options pubnub_publish_defopts(void) result.cipher_key = NULL; result.replicate = true; result.meta = NULL; - result.method = pubnubPublishViaGET; + result.method = pubnubSendViaGET; return result; } @@ -60,7 +60,7 @@ enum pubnub_res pubnub_publish_ex(pubnub_t* pb, #endif #if PUBNUB_USE_GZIP_COMPRESSION pb->core.gzip_msg_len = 0; - if (pubnubPublishViaPOSTwithGZIP == opts.method) { + if (pubnubSendViaPOSTwithGZIP == opts.method) { if (pbgzip_compress(pb, message) == PNR_OK) { message = pb->core.gzip_msg_buf; } @@ -71,7 +71,7 @@ enum pubnub_res pubnub_publish_ex(pubnub_t* pb, if (PNR_STARTED == rslt) { pb->trans = PBTT_PUBLISH; pb->core.last_result = PNR_STARTED; - pb->flags.is_publish_via_post = (opts.method != pubnubPublishViaGET); + pb->method = opts.method; pbnc_fsm(pb); rslt = pb->core.last_result; } diff --git a/core/pubnub_coreapi_ex.h b/core/pubnub_coreapi_ex.h index 4e3d4541..88f1f2fa 100644 --- a/core/pubnub_coreapi_ex.h +++ b/core/pubnub_coreapi_ex.h @@ -58,7 +58,7 @@ struct pubnub_publish_options { */ char const* meta; /** Defines the method by which publish transaction will be performed */ - enum pubnub_publish_method method; + enum pubnub_method method; }; /** This returns the default options for publish V1 transactions. diff --git a/core/pubnub_crypto.c b/core/pubnub_crypto.c index 87a5368a..957ccb37 100644 --- a/core/pubnub_crypto.c +++ b/core/pubnub_crypto.c @@ -15,6 +15,9 @@ int pbcrypto_signature(struct pbcc_context *pbcc, char const *channel, char const* msg, char *signature, size_t n) { +#if !PUBNUB_CRYPTO_API + return -1; +#else PBMD5_CTX md5; char s[2] = { '/', '\0' }; uint8_t digest[16]; @@ -24,7 +27,7 @@ int pbcrypto_signature(struct pbcc_context *pbcc, char const *channel, char cons PUBNUB_ASSERT_OPT(signature != NULL); PUBNUB_ASSERT_OPT(n > 32); - if (!PUBNUB_CRYPTO_API || (NULL == pbcc->secret_key)) { + if (NULL == pbcc->secret_key) { return -1; } @@ -52,6 +55,7 @@ int pbcrypto_signature(struct pbcc_context *pbcc, char const *channel, char cons ); return 0; +#endif /* !PUBNUB_CRYPTO_API */ } diff --git a/core/pubnub_helper.c b/core/pubnub_helper.c index d334b366..08b5bb3c 100644 --- a/core/pubnub_helper.c +++ b/core/pubnub_helper.c @@ -2,6 +2,9 @@ #include "pubnub_helper.h" #include "pubnub_assert.h" +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "pubnub_subscribe_v2_message.h" +#endif #include @@ -65,11 +68,26 @@ char const* pubnub_res_2_string(enum pubnub_res e) case PNR_INVALID_PARAMETERS: return "Invalid function parameters"; case PNR_ERROR_ON_SERVER: return "Server reported an error"; case PNR_AUTHENTICATION_FAILED: return "Proxy authentication failed"; + case PNR_OBJECTS_API_INVALID_PARAM: return "Objects API invalid parameter"; + case PNR_OBJECTS_API_OK: return "Objects API transaction successfully finished"; + case PNR_OBJECTS_API_ERROR: return "Objects API transaction reported an error"; + default: return "!?!?!"; } - return "!?!?!"; } +#if PUBNUB_USE_SUBSCRIBE_V2 +char const* pubnub_msg_type_to_str(enum pubnub_message_type type) +{ + switch (type) { + case pbsbSignal: return "signal"; + case pbsbPublished: return "published"; + default: return "!?!?!"; + } +} +#endif + + enum pubnub_tribool pubnub_should_retry(enum pubnub_res e) { switch (e) { @@ -97,6 +115,9 @@ enum pubnub_tribool pubnub_should_retry(enum pubnub_res e) case PNR_INVALID_PARAMETERS: return pbccFalse; /* Check and fix invalid parameters */ case PNR_ERROR_ON_SERVER: return pbccFalse; /* Fix the error reported */ case PNR_AUTHENTICATION_FAILED: return pbccFalse; /* Check and fix the error reported */ + case PNR_OBJECTS_API_INVALID_PARAM: return pbccFalse; /* Check and fix the error reported */ + case PNR_OBJECTS_API_OK: return pbccFalse; + case PNR_OBJECTS_API_ERROR: return pbccFalse; /* Check the error reported */ } return pbccFalse; } diff --git a/core/pubnub_helper.h b/core/pubnub_helper.h index 74faa896..42e5e9ad 100644 --- a/core/pubnub_helper.h +++ b/core/pubnub_helper.h @@ -50,6 +50,14 @@ enum pubnub_publish_res pubnub_parse_publish_result(char const *result); */ char const* pubnub_res_2_string(enum pubnub_res e); +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "pubnub_subscribe_v2_message.h" +/** Returns a string literal describing enum value @p type + Used when looking at pubnub V2 message received by pubnub_subscribe_v2(). + */ +char const* pubnub_msg_type_to_str(enum pubnub_message_type type); +#endif + /** Returns whether retrying a Pubnub transaction makes sense. This is mostly interesting for publishing, but is useful in general. It is least useful for subscribing, because you will most probably diff --git a/core/pubnub_internal_common.h b/core/pubnub_internal_common.h index 3eab778c..48b81efd 100644 --- a/core/pubnub_internal_common.h +++ b/core/pubnub_internal_common.h @@ -43,6 +43,10 @@ #define PUBNUB_USE_ADVANCED_HISTORY 0 #endif +#if !defined(PUBNUB_USE_OBJECTS_API) +#define PUBNUB_USE_OBJECTS_API 0 +#endif + #if !defined(PUBNUB_PROXY_API) #define PUBNUB_PROXY_API 0 #elif PUBNUB_PROXY_API @@ -71,6 +75,8 @@ #include #endif +/* Maximum object length that will be sent via PATCH, or POST methods */ +#define PUBNUB_MAX_OBJECT_LENGTH 30000 /** State of a Pubnub socket. Some states are specific to some PALs. @@ -203,11 +209,6 @@ struct pubnub_flags { renewed without losing transaction at hand. */ bool started_while_kept_alive : 1; - - /** Indicates whether to send the message in http message body, or if not, - encoded 'via GET'(, or maybe some third method). - */ - bool is_publish_via_post : 1; }; #if PUBNUB_CHANGE_DNS_SERVERS @@ -247,6 +248,7 @@ struct pubnub_multi_addresses { }; #endif /* PUBNUB_USE_MULTIPLE_ADDRESSES */ + /** The Pubnub context @note Don't declare any members as `bool`, as there may be @@ -310,6 +312,12 @@ struct pubnub_ { struct pubnub_flags flags; + /** Indicates whether to send the message in http message body(POST, or PATCH), + or if not, encoded 'via GET'(, or maybe some other method). + Takes values from enum 'pubnub_method' defined in 'pubnub_api_types.h'. + */ + uint8_t method; + #if PUBNUB_ADVANCED_KEEP_ALIVE struct pubnub_keep_alive_data { time_t timeout; @@ -485,12 +493,4 @@ pubnub_t* pballoc_get_ctx(unsigned idx); void pballoc_free_at_last(pubnub_t* pb); -/** Parses subscribe V2 response from Pubnub. - - @todo Should probably find a better place for this - declaration... -*/ -enum pubnub_res pbcc_parse_subscribe_v2_response(struct pbcc_context* p); - - #endif /* !defined INC_PUBNUB_INTERNAL_COMMON */ diff --git a/core/pubnub_netcore.c b/core/pubnub_netcore.c index 5b27eb7f..ab69133c 100644 --- a/core/pubnub_netcore.c +++ b/core/pubnub_netcore.c @@ -9,9 +9,15 @@ #include "core/pubnub_version.h" #include "core/pubnub_version_internal.h" #include "core/pubnub_helper.h" +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "core/pbcc_subscribe_v2.h" +#endif #if PUBNUB_USE_ADVANCED_HISTORY #include "core/pbcc_advanced_history.h" #endif +#if PUBNUB_USE_OBJECTS_API +#include "core/pbcc_objects_api.h" +#endif #include "core/pubnub_proxy_core.h" #include @@ -42,6 +48,50 @@ #define possible_gzip_response(pb) #endif /* PUBNUB_RECEIVE_GZIP_RESPONSE */ +bool HTTP_request_has_body(uint8_t method) +{ + switch(method) { + case pubnubSendViaGET: + case pubnubUseDELETE: + return false; + case pubnubSendViaPOST: + case pubnubUsePATCH: +#if PUBNUB_USE_GZIP_COMPRESSION + case pubnubSendViaPOSTwithGZIP: + case pubnubUsePATCHwithGZIP: +#endif + return true; + default: + PUBNUB_LOG_ERROR("Error: HTTP_message_has_body(method): unhandled method: %u\n", + method); + return false; + } +} + + +static char const* get_method_verb_string(uint8_t method) +{ + switch(method) { + case pubnubSendViaGET: + return "GET "; + case pubnubSendViaPOST: +#if PUBNUB_USE_GZIP_COMPRESSION + case pubnubSendViaPOSTwithGZIP: +#endif + return "POST "; + case pubnubUsePATCH: +#if PUBNUB_USE_GZIP_COMPRESSION + case pubnubUsePATCHwithGZIP: +#endif + return "PATCH "; + case pubnubUseDELETE: + return "DELETE "; + default: + PUBNUB_LOG_ERROR("Error: get_method_verb_string(method): unhandled method: %u\n", + method); + return "UNKOWN "; + } +} static int send_fin_head(struct pubnub_* pb) { @@ -166,6 +216,7 @@ static enum pubnub_res dont_parse(struct pbcc_context* p) static PFpbcc_parse_response_T m_aParseResponse[] = { dont_parse, pbcc_parse_subscribe_response, pbcc_parse_publish_response, + pbcc_parse_publish_response, /* PBTT_SIGNAL */ #if PUBNUB_ONLY_PUBSUB_API dont_parse, dont_parse, @@ -198,14 +249,30 @@ static PFpbcc_parse_response_T m_aParseResponse[] = { dont_parse, pbcc_parse_presence_response /* PBTT_HEARTBEAT */ #if PUBNUB_USE_SUBSCRIBE_V2 , pbcc_parse_subscribe_v2_response /* PBTT_SUBSCRIBE_V2 */ -#else - , dont_parse /* PBTT_SUBSCRIBE_V2 */ #endif #if PUBNUB_USE_ADVANCED_HISTORY , pbcc_parse_message_counts_response /* PBTT_MESSAGE_COUNTS */ -#else - , dont_parse /* PBTT_MESSAGE_COUNTS */ #endif +#if PUBNUB_USE_OBJECTS_API + , pbcc_parse_objects_api_response /* PBTT_FETCH_ALL_USERS */ + , pbcc_parse_objects_api_response /* PBTT_CREATE_USER */ + , pbcc_parse_objects_api_response /* PBTT_FETCH_USER */ + , pbcc_parse_objects_api_response /* PBTT_UPDATE_USER */ + , pbcc_parse_objects_api_response /* PBTT_DELETE_USER */ + , pbcc_parse_objects_api_response /* PBTT_FETCH_ALL_SPACES */ + , pbcc_parse_objects_api_response /* PBTT_CREATE_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_FETCH_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_UPDATE_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_DELETE_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_FETCH_USERS_SPACE_MEMBERSHIPS */ + , pbcc_parse_objects_api_response /* PBTT_ADD_USERS_SPACE_MEMBERSHIPS */ + , pbcc_parse_objects_api_response /* PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS */ + , pbcc_parse_objects_api_response /* PBTT_REMOVE_USERS_SPACE_MEMBERSHIPS */ + , pbcc_parse_objects_api_response /* PBTT_FETCH_MEMBERS_IN_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_ADD_MEMBERS_IN_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_UPDATE_MEMBERS_IN_SPACE */ + , pbcc_parse_objects_api_response /* PBTT_REMOVE_MEMBERS_IN_SPACE */ +#endif /* PUBNUB_USE_OBJECTS_API */ #endif /* PUBNUB_ONLY_PUBSUB_API */ }; @@ -593,7 +660,7 @@ int pbnc_fsm(struct pubnub_* pb) } } #endif /* PUBNUB_USE_SSL */ - i = pbpal_send_str(pb, pb->flags.is_publish_via_post ? "POST " : "GET "); + i = pbpal_send_str(pb, get_method_verb_string(pb->method)); if (i < 0) { outcome_detected(pb, PNR_IO_ERROR); break; @@ -605,7 +672,7 @@ int pbnc_fsm(struct pubnub_* pb) enum pbpal_tls_result res = pbpal_check_tls(pb); switch (res) { case pbtlsEstablished: - i = pbpal_send_str(pb, pb->flags.is_publish_via_post ? "POST " : "GET "); + i = pbpal_send_str(pb, get_method_verb_string(pb->method)); if (i < 0) { outcome_detected(pb, PNR_IO_ERROR); break; @@ -818,16 +885,16 @@ int pbnc_fsm(struct pubnub_* pb) } } #endif - if (pb->flags.is_publish_via_post + if (HTTP_request_has_body(pb->method) #if PUBNUB_PROXY_API && (pb->proxy_tunnel_established || (pbproxyNONE == pb->proxy_type)) #endif ) { char hedr[128] = "\r\n"; - pbcc_headers_for_publish_via_post( + pbcc_via_post_headers( &(pb->core), hedr + 2, sizeof hedr - 2); PUBNUB_LOG_TRACE( - "Sending HTTP 'publish via POST' headers: '%s'\n", hedr); + "Sending HTTP 'via POST, or PATCH' headers: '%s'\n", hedr); pb->state = PBS_TX_EXTRA_HEADERS; if (-1 == pbpal_send_str(pb, hedr)) { outcome_detected(pb, PNR_IO_ERROR); @@ -857,12 +924,12 @@ int pbnc_fsm(struct pubnub_* pb) outcome_detected(pb, PNR_IO_ERROR); } else if (0 == i) { - if (pb->flags.is_publish_via_post + if (HTTP_request_has_body(pb->method) #if PUBNUB_PROXY_API && (pb->proxy_tunnel_established || (pbproxyNONE == pb->proxy_type)) #endif ) { - const char* message = pb->core.message_to_publish; + const char* message = pb->core.message_to_send; #if PUBNUB_USE_GZIP_COMPRESSION size_t len = (pb->core.gzip_msg_len != 0) ? pb->core.gzip_msg_len : strlen(message); @@ -1200,7 +1267,7 @@ int pbnc_fsm(struct pubnub_* pb) break; } pb->state = PBS_TX_GET; - i = pbpal_send_str(pb, pb->flags.is_publish_via_post ? "POST " : "GET "); + i = pbpal_send_str(pb, get_method_verb_string(pb->method)); if (i < 0) { pb->state = close_kept_alive_connection(pb); } diff --git a/core/pubnub_ntf_callback.h b/core/pubnub_ntf_callback.h index be1cdd82..ffb6399c 100644 --- a/core/pubnub_ntf_callback.h +++ b/core/pubnub_ntf_callback.h @@ -65,6 +65,18 @@ void *pubnub_get_user_data(pubnub_t *pb); */ pubnub_callback_t pubnub_get_callback(pubnub_t *pb); +/** Enables safe exit from the main() by disabling platform watcher thread. + It exists and is used in callback environment only. + Adequate place for this function call would be the end of main() function. + After pubnub_stop() no other pubnub c-core function call is allowed( not even + pubnub_free() which only enqueues the event into the queue that is supposed + to be processed by the disabled thread). + This function can, also, be the one from the 'atexit() list' on 'full' C + environment. For example we could have just 'atexit(pubnub_stop)' function call + inside the code(You can register your termination function: pubnub_stop() + anywhere you like, but it will be called at the time of the program termination). + */ +void pubnub_stop(void); #endif /* !defined INC_PUBNUB_NTF_CALLBACK */ diff --git a/core/pubnub_objects_api.c b/core/pubnub_objects_api.c new file mode 100644 index 00000000..82234960 --- /dev/null +++ b/core/pubnub_objects_api.c @@ -0,0 +1,710 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#include "pubnub_internal.h" + +#include "core/pubnub_ccore.h" +#include "core/pubnub_netcore.h" +#include "core/pubnub_assert.h" +#include "core/pubnub_timers.h" +#include "core/pubnub_log.h" +#include "lib/pb_strnlen_s.h" +#include "core/pbcc_objects_api.h" +#include "core/pubnub_objects_api.h" + +#include "core/pbpal.h" + +#include +#include + +#define FORM_THE_OBJECT(pb, function_name_literal, obj_buffer, key_literal,json) \ +do { \ + if (sizeof(obj_buffer) < \ + sizeof(key_literal) + pb_strnlen_s(json, PUBNUB_MAX_OBJECT_LENGTH) + 1) { \ + PUBNUB_LOG_ERROR(function_name_literal "(pb=%p) - " \ + "buffer size is too small: " \ + "current_buffer_size = %lu\n" \ + "required_buffer_size = %lu\n", \ + (pb), \ + (unsigned long)sizeof(obj_buffer), \ + (unsigned long)(sizeof(key_literal) + \ + pb_strnlen_s(json, PUBNUB_MAX_OBJECT_LENGTH))); \ + return PNR_TX_BUFF_TOO_SMALL; \ + } \ + snprintf(obj_buffer, sizeof(obj_buffer), "%s%s%c", key_literal, json, '}'); \ + json = (obj_buffer); \ +} while(0) + + +enum pubnub_res pubnub_fetch_all_users(pubnub_t* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_all_users_prep(&pb->core, + include, + include_count, + limit, + start, + end, + count); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_ALL_USERS; + 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_create_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_obj) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, user_obj) == PNR_OK) { + user_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_create_user_prep(&pb->core, include, include_count, user_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_CREATE_USER; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubSendViaPOST; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_fetch_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_id) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_user_prep(&pb->core, include, include_count, user_id); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_USER; + 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_update_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_obj) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, user_obj) == PNR_OK) { + user_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_user_prep(&pb->core, include, include_count, user_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_UPDATE_USER; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_delete_user(pubnub_t* pb, char const* user_id) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_delete_user_prep(&pb->core, user_id); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_DELETE_USER; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUseDELETE; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_fetch_all_spaces(pubnub_t* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_all_spaces_prep(&pb->core, + include, + include_count, + limit, + start, + end, + count); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_ALL_SPACES; + 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_create_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_obj) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, space_obj) == PNR_OK) { + space_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_create_space_prep(&pb->core, include, include_count, space_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_CREATE_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubSendViaPOST; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_fetch_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_id) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_space_prep(&pb->core, include, include_count, space_id); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_SPACE; + 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_update_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_obj) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, space_obj) == PNR_OK) { + space_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_space_prep(&pb->core, include, include_count, space_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_UPDATE_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_delete_space(pubnub_t* pb, char const* space_id) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_delete_space_prep(&pb->core, space_id); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_DELETE_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUseDELETE; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_fetch_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_users_space_memberships_prep(&pb->core, + user_id, + include, + include_count, + limit, + start, + end, + count); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_USERS_SPACE_MEMBERSHIPS; + 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_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + enum pubnub_res rslt; + char obj_buffer[PUBNUB_BUF_MAXLEN]; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_add_users_space_memberships", + obj_buffer, + "{\"add\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_users_space_memberships_prep(&pb->core, + user_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_ADD_USERS_SPACE_MEMBERSHIPS; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_update_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + enum pubnub_res rslt; + char obj_buffer[PUBNUB_BUF_MAXLEN]; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_update_users_space_memberships", + obj_buffer, + "{\"update\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_users_space_memberships_prep(&pb->core, + user_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_remove_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + enum pubnub_res rslt; + char obj_buffer[PUBNUB_BUF_MAXLEN]; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_remove_users_space_memberships", + obj_buffer, + "{\"remove\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_users_space_memberships_prep(&pb->core, + user_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_REMOVE_USERS_SPACE_MEMBERSHIPS; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_fetch_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count) +{ + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_fetch_members_in_space_prep(&pb->core, + space_id, + include, + include_count, + limit, + start, + end, + count); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_FETCH_MEMBERS_IN_SPACE; + 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_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + char obj_buffer[PUBNUB_BUF_MAXLEN]; + enum pubnub_res rslt; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_add_members_in_space", + obj_buffer, + "{\"add\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_members_in_space_prep(&pb->core, + space_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_ADD_MEMBERS_IN_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_update_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + enum pubnub_res rslt; + char obj_buffer[PUBNUB_BUF_MAXLEN]; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_update_members_in_space", + obj_buffer, + "{\"update\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_members_in_space_prep(&pb->core, + space_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_UPDATE_MEMBERS_IN_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} + + +enum pubnub_res pubnub_remove_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj) +{ + enum pubnub_res rslt; + char obj_buffer[PUBNUB_BUF_MAXLEN]; + + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); + + pubnub_mutex_lock(pb->monitor); + if (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + FORM_THE_OBJECT(pb, + "pubnub_remove_members_in_space", + obj_buffer, + "{\"remove\":", + update_obj); +#if PUBNUB_USE_GZIP_COMPRESSION + pb->core.gzip_msg_len = 0; + if (pbgzip_compress(pb, update_obj) == PNR_OK) { + update_obj = pb->core.gzip_msg_buf; + } +#endif + rslt = pbcc_update_members_in_space_prep(&pb->core, + space_id, + include, + include_count, + update_obj); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_REMOVE_MEMBERS_IN_SPACE; + pb->core.last_result = PNR_STARTED; + pb->method = pubnubUsePATCH; + pbnc_fsm(pb); + rslt = pb->core.last_result; + } + pubnub_mutex_unlock(pb->monitor); + + return rslt; +} diff --git a/core/pubnub_objects_api.h b/core/pubnub_objects_api.h new file mode 100644 index 00000000..c8205c0e --- /dev/null +++ b/core/pubnub_objects_api.h @@ -0,0 +1,353 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PUBNUB_OBJECTS_API +#define INC_PUBNUB_OBJECTS_API + + +#include "pubnub_api_types.h" + +#include + +struct pbcc_context; + + +/** Returns a paginated list of users associated with the subscription key of the context @p pbp, + optionally including each record's custom data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex user attributes to include in + response. Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param limit Number of entities to return in response. Regular values 1 - 100. If you set `0`, + that means “use the default”. At the time of this writing, default was 100. + @param start Previously-returned cursor bookmark for fetching the next page. Use NULL if you + don’t want to paginate with a start bookmark. + @param end Previously-returned cursor bookmark for fetching the previous page. Ignored if you + also supply the start parameter. Use NULL if you don’t want to paginate with an + end bookmark. + @param count Request totalCount to be included in paginated response. By default, totalCount + is omitted. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_all_users(pubnub_t* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + +/** Creates a user with the attributes specified in @p user_obj. + Returns the created user object, optionally including the user's custom data object. + @note User ID and name are required properties in the @p user_obj + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex user attributes to include in + response. Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param user_obj The JSON string with the definition of the User + Object to create. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_create_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_obj); + + +/** Returns the user object specified with @p user_id, optionally including the user's + custom data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex user attributes to include in + response. Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param user_id The User ID for which to retrieve the user object. + Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_id); + + +/** Updates the user object specified with the `id` key of the @p user_obj with any new + information you provide. Returns the updated user object, optionally including + the user's custom data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex user attributes to include in + response. Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param user_obj The JSON string with the description of the User + Object to update. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_update_user(pubnub_t* pb, + char const** include, + size_t include_count, + char const* user_obj); + + +/** Deletes the user specified with @p user_id. + @param pb The pubnub context. Can't be NULL + @param user_id The User ID. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_delete_user(pubnub_t* pb, char const* user_id); + + +/** Returns the spaces associated with the subscriber key of the context @p pbp, optionally + including each space's custom data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param limit Number of entities to return in response. Regular values 1 - 100. If you set `0`, + that means “use the default”. At the time of this writing, default was 100. + @param start Previously-returned cursor bookmark for fetching the next page. Use NULL if you + don’t want to paginate with a start bookmark. + @param end Previously-returned cursor bookmark for fetching the previous page. Ignored if + you also supply the start parameter. Use NULL if you don’t want to paginate with + an end bookmark. + @param count Request totalCount to be included in paginated response. By default, totalCount + is omitted. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_all_spaces(pubnub_t* pb, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + + +/** Creates a space with the attributes specified in @p space_obj. + Returns the created space object, optionally including its custom data object. + @note Space ID and name are required properties of @p space_obj + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param space_obj The JSON string with the definition of the Space Object to create. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_create_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_obj); + + +/** Returns the space object specified with @p space_id, optionally including its custom + data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param space_id The Space ID for which to retrieve the space object. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_id); + + +/** Updates the space specified by the `id` property of the @p space_obj. Returns the space object, + optionally including its custom data object. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param space_obj The JSON string with the description of the Space Object to update. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_update_space(pubnub_t* pb, + char const** include, + size_t include_count, + char const* space_obj); + + +/** Deletes the space specified with @p space_id. + @param pb The pubnub context. Can't be NULL + @param space_id The Space ID. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_delete_space(pubnub_t* pb, char const* space_id); + + +/** Returns the space memberships of the user specified by @p user_id, optionally including + the custom data objects for... + @param pb The pubnub context. Can't be NULL + @param user_id The User ID for which to retrieve the space memberships for. + Cannot be NULL. + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param limit Number of entities to return in response. Regular values 1 - 100. + If you set `0`, that means “use the default”. At the time of this writing, + default was 100. + @param start Previously-returned cursor bookmark for fetching the next page. Use NULL if you + don’t want to paginate with a start bookmark. + @param end Previously-returned cursor bookmark for fetching the previous page. Ignored if + you also supply the start parameter. Use NULL if you don’t want to paginate with + an end bookmark. + @param count Request totalCount to be included in paginated response. By default, totalCount + is omitted. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + + +/** Adds the space memberships of the user specified by @p user_id. Uses the `add` property + to perform those operations on one, or more memberships. + + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. + Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_add_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Updates the space memberships of the user specified by @p user_id. Uses the `update` property + to perform those operations on one, or more memberships. + + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. + Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_update_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Removes the space memberships of the user specified by @p user_id. Uses the `remove` property + to perform those operations on one, or more memberships. + + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. + Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_remove_users_space_memberships(pubnub_t* pb, + char const* user_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Returns all users in the space specified with @p space_id, optionally including + the custom data objects for... + @param pb The pubnub context. Can't be NULL + @param space_id The Space ID for which to retrieve the user object. + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param limit Number of entities to return in response. Regular values 1 - 100. + If you set `0`, that means “use the default”. At the time of this writing, + default was 100. + @param start Previously-returned cursor bookmark for fetching the next page. Use NULL if you + don’t want to paginate with a start bookmark. + @param end Previously-returned cursor bookmark for fetching the previous page. Ignored if + you also supply the start parameter. Use NULL if you don’t want to paginate with + an end bookmark. + @param count Request totalCount to be included in paginated response. By default, totalCount + is omitted. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_fetch_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + size_t limit, + char const* start, + char const* end, + enum pubnub_tribool count); + + +/** Adds the list of members of the space specified with @p space_id. Uses the `add` + property to perform the operation on one or more members. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_add_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Updates the list of members of the space specified with @p space_id. Uses the `update` + property to perform the operation on one or more members. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_update_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj); + + +/** Removes the list of members of the space specified with @p space_id. Uses the `remove` + property to perform the operation on one or more members. + @param pb The pubnub context. Can't be NULL + @param include array of (C) strings with additional/complex attributes to include in response. + Use NULL if you don't want to retrieve additional attributes. + @param include_count dimension of @p include. Set 0 if you don’t want to retrieve additional + attributes + @param update_obj The JSON object that defines the updates to perform. Cannot be NULL. + @return #PNR_STARTED on success, an error otherwise + */ +enum pubnub_res pubnub_remove_members_in_space(pubnub_t* pb, + char const* space_id, + char const** include, + size_t include_count, + char const* update_obj); + + +#endif /* !defined INC_PUBNUB_OBJECTS_API */ diff --git a/core/pubnub_pubsubapi.c b/core/pubnub_pubsubapi.c index d8e7bd42..dfb161ed 100644 --- a/core/pubnub_pubsubapi.c +++ b/core/pubnub_pubsubapi.c @@ -50,7 +50,7 @@ pubnub_t* pubnub_init(pubnub_t* p, const char* publish_key, const char* subscrib p->options.ipv6_connectivity = false; #endif p->flags.started_while_kept_alive = false; - p->flags.is_publish_via_post = false; + p->method = pubnubSendViaGET; #if PUBNUB_ADVANCED_KEEP_ALIVE p->keep_alive.max = 1000; p->keep_alive.timeout = 50; @@ -95,7 +95,7 @@ enum pubnub_res pubnub_publish(pubnub_t* pb, const char* channel, const char* me return PNR_IN_PROGRESS; } - rslt = pbcc_publish_prep(&pb->core, channel, message, true, false, NULL, pubnubPublishViaGET); + rslt = pbcc_publish_prep(&pb->core, channel, message, true, false, NULL, pubnubSendViaGET); if (PNR_STARTED == rslt) { pb->trans = PBTT_PUBLISH; pb->core.last_result = PNR_STARTED; @@ -108,6 +108,33 @@ enum pubnub_res pubnub_publish(pubnub_t* pb, const char* channel, const char* me } +enum pubnub_res pubnub_signal(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 (!pbnc_can_start_transaction(pb)) { + pubnub_mutex_unlock(pb->monitor); + return PNR_IN_PROGRESS; + } + + rslt = pbcc_signal_prep(&pb->core, channel, message); + if (PNR_STARTED == rslt) { + pb->trans = PBTT_SIGNAL; + 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; @@ -252,7 +279,8 @@ static char const* do_last_publish_result(pubnub_t* pb) if (PUBNUB_DYNAMIC_REPLY_BUFFER && (NULL == pb->core.http_reply)) { return ""; } - if ((pb->trans != PBTT_PUBLISH) || (pb->core.http_reply[0] == '\0')) { + if (((pb->trans != PBTT_PUBLISH) && (pb->trans != PBTT_SIGNAL)) || + (pb->core.http_reply[0] == '\0')) { return ""; } diff --git a/core/pubnub_pubsubapi.h b/core/pubnub_pubsubapi.h index b6c01630..2bb2a89e 100644 --- a/core/pubnub_pubsubapi.h +++ b/core/pubnub_pubsubapi.h @@ -93,7 +93,7 @@ enum pubnub_cancel_res pubnub_cancel(pubnub_t* p); @p p context. This actually means "initiate a publish transaction". - You can't publish if a transaction is in progress in @p p context. + You can't publish if a transaction is in progress on @p p context. If transaction is not successful (@c PNR_PUBLISH_FAILED), you can get the string describing the reason for failure by calling @@ -110,14 +110,52 @@ enum pubnub_cancel_res pubnub_cancel(pubnub_t* p); 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 channel The string with the channel to publish to. @param message The message to publish, expected to be in JSON format - @return #PNR_STARTED on success, an error otherwise + @return #PNR_STARTED or #PNR_OK on success, an error otherwise */ enum pubnub_res pubnub_publish(pubnub_t* p, const char* channel, const char* message); +/** Sends a signal @p message (in JSON format) on @p channel + using the @p pb context. This actually means + "initiate a signal transaction". + + It has similar behaviour as publish, but unlike publish + transaction, signal erases previous signal message on server(, on + a given channel,) and you can not send any metadata. + + There can be only up to one signal message at the time. If it's + not renewed by another signal, signal message disappears from + channel history after certain amount of time. + + You can't (send a) 'signal' if a transaction is in progress on @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 signal 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 pb The pubnub context. Can't be NULL + @param channel The string with the channel to signal to. + @param message The signal message to send, expected to be in JSON format + + @return #PNR_STARTED or #PNR_OK on success, an error otherwise + */ +enum pubnub_res pubnub_signal(pubnub_t* pb, + const char* channel, + const char* message); + /** 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 @@ -191,7 +229,7 @@ char const* pubnub_get_channel(pubnub_t* pb); @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 + @return #PNR_STARTED or #PNR_OK on success, an error otherwise @see pubnub_get */ diff --git a/core/pubnub_subscribe_v2.c b/core/pubnub_subscribe_v2.c index 6165be9f..749150fe 100644 --- a/core/pubnub_subscribe_v2.c +++ b/core/pubnub_subscribe_v2.c @@ -2,7 +2,7 @@ #include "pubnub_internal.h" #include "pubnub_subscribe_v2.h" - +#include "pbcc_subscribe_v2.h" #include "pubnub_ccore_pubsub.h" #include "pubnub_netcore.h" #include "pubnub_assert.h" @@ -13,12 +13,6 @@ #include "pbpal.h" -/** It has to contain the `t` field, with another `t` for the - timetoken (as string) and `tr` for the region (integer) and the - `m` field for the message(s) array. -*/ -#define MIN_SUBSCRIBE_V2_RESPONSE_LENGTH 40 - /** Minimal presence heartbeat interval supported by Pubnub, in seconds. */ @@ -35,56 +29,6 @@ struct pubnub_subscribe_v2_options pubnub_subscribe_v2_defopts(void) } -static enum pubnub_res subscribe_v2_prep(struct pbcc_context* p, - char const* channel, - char const* channel_group, - unsigned* heartbeat, - char const* filter_expr) -{ - char region_str[20]; - char const* tr; - - 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; - } - - if ('\0' == p->timetoken[0]) { - p->timetoken[0] = '0'; - p->timetoken[1] = '\0'; - tr = NULL; - } - else { - snprintf(region_str, sizeof region_str, "%d", p->region); - tr = region_str; - } - p->http_content_len = 0; - p->msg_ofs = p->msg_end = 0; - - p->http_buf_len = snprintf( - p->http_buf, sizeof p->http_buf, "/v2/subscribe/%s/", p->subscribe_key); - APPEND_URL_ENCODED_M(p, channel); - p->http_buf_len += snprintf(p->http_buf + p->http_buf_len, - sizeof p->http_buf - p->http_buf_len, - "/0?tt=%s&pnsdk=%s", - p->timetoken, - pubnub_uname()); - APPEND_URL_PARAM_M(p, "tr", tr, '&'); - 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_PARAM_ENCODED_M(p, "filter-expr", filter_expr, '&'); - APPEND_URL_OPT_PARAM_UNSIGNED_M(p, "heartbeat", heartbeat, '&'); - - return PNR_STARTED; -} - - enum pubnub_res pubnub_subscribe_v2(pubnub_t* p, const char* channel, struct pubnub_subscribe_v2_options opt) @@ -99,7 +43,7 @@ enum pubnub_res pubnub_subscribe_v2(pubnub_t* p, return PNR_IN_PROGRESS; } - rslt = subscribe_v2_prep( + rslt = pbcc_subscribe_v2_prep( &p->core, channel, opt.channel_group, &opt.heartbeat, opt.filter_expr); if (PNR_STARTED == rslt) { p->trans = PBTT_SUBSCRIBE_V2; @@ -113,180 +57,14 @@ enum pubnub_res pubnub_subscribe_v2(pubnub_t* p, } -enum pubnub_res pbcc_parse_subscribe_v2_response(struct pbcc_context* p) -{ - enum pbjson_object_name_parse_result jpresult; - struct pbjson_elem el; - struct pbjson_elem found; - char* reply = p->http_reply; - - if (p->http_buf_len < MIN_SUBSCRIBE_V2_RESPONSE_LENGTH) { - return PNR_FORMAT_ERROR; - } - if ((reply[0] != '{') || (reply[p->http_buf_len - 1] != '}')) { - return PNR_FORMAT_ERROR; - } - - el.start = p->http_reply; - el.end = p->http_reply + p->http_buf_len; - jpresult = pbjson_get_object_value(&el, "t", &found); - if (jonmpOK == jpresult) { - struct pbjson_elem titel; - if (jonmpOK == pbjson_get_object_value(&found, "t", &titel)) { - size_t len = titel.end - titel.start - 2; - if ((*titel.start != '"') || (titel.end[-1] != '"')) { - PUBNUB_LOG_ERROR("Time token in response is not a string\n"); - return PNR_FORMAT_ERROR; - } - if (len >= sizeof p->timetoken) { - PUBNUB_LOG_ERROR( - "Time token in response, length %zu, longer than max %zu\n", - len, - sizeof p->timetoken - 1); - return PNR_FORMAT_ERROR; - } - - memcpy(p->timetoken, titel.start + 1, len); - p->timetoken[len] = '\0'; - } - else { - PUBNUB_LOG_ERROR( - "No timetoken value in subscribe V2 response found\n"); - return PNR_FORMAT_ERROR; - } - if (jonmpOK == pbjson_get_object_value(&found, "r", &titel)) { - char s[20]; - pbjson_element_strcpy(&titel, s, sizeof s); - p->region = strtol(s, NULL, 0); - } - else { - PUBNUB_LOG_ERROR( - "No region value in subscribe V2 response found\n"); - return PNR_FORMAT_ERROR; - } - } - else { - PUBNUB_LOG_ERROR( - "No timetoken in subscribe V2 response found, error=%d\n", jpresult); - return PNR_FORMAT_ERROR; - } - - p->chan_ofs = p->chan_end = 0; - - /* We should optimize this to not look from the start again. - */ - jpresult = pbjson_get_object_value(&el, "m", &found); - if (jonmpOK == jpresult) { - p->msg_ofs = (unsigned)(found.start - reply + 1); - p->msg_end = (unsigned)(found.end - reply - 1); - } - else { - PUBNUB_LOG_ERROR( - "No message array subscribe V2 response found, error=%d\n", jpresult); - return PNR_FORMAT_ERROR; - } - - return PNR_OK; -} - - -struct pubnub_v2_message pubnub_get_v2(pubnub_t* pbp) +struct pubnub_v2_message pubnub_get_v2(pubnub_t* pb) { - enum pbjson_object_name_parse_result jpresult; - struct pbjson_elem el; - struct pbjson_elem found; - struct pubnub_v2_message rslt; - struct pbcc_context* p = &pbp->core; - char const* start; - char const* end; - char const* seeker; - - memset(&rslt, 0, sizeof rslt); - - if (p->msg_ofs >= p->msg_end) { - return rslt; - } - start = p->http_reply + p->msg_ofs; - if (*start != '{') { - PUBNUB_LOG_ERROR( - "Message subscribe V2 response is not a JSON object\n"); - return rslt; - } - end = p->http_reply + p->msg_end; - seeker = pbjson_find_end_complex(start, end); - if (seeker == end) { - PUBNUB_LOG_ERROR( - "Message subscribe V2 response has no endo of JSON object\n"); - return rslt; - } - - p->msg_ofs = (unsigned)(seeker - p->http_reply + 2); - el.start = start; - el.end = seeker; - - /* @todo We should iterate over elements of JSON message object, - instead of looking from the start, again and again. - */ - - jpresult = pbjson_get_object_value(&el, "d", &found); - if (jonmpOK == jpresult) { - rslt.payload.ptr = (char*)found.start; - rslt.payload.size = found.end - found.start; - } - else { - PUBNUB_LOG_ERROR("pbp=%p: No message payload in subscribe V2 response " - "found, error=%d\n", - pbp, - jpresult); - return rslt; - } + struct pubnub_v2_message result; + PUBNUB_ASSERT(pb_valid_ctx_ptr(pb)); - jpresult = pbjson_get_object_value(&el, "c", &found); - if (jonmpOK == jpresult) { - rslt.channel.ptr = (char*)found.start + 1; - rslt.channel.size = found.end - found.start - 2; - } - else { - PUBNUB_LOG_ERROR("pbp=%p: No message channel in subscribe V2 response " - "found, error=%d\n", - pbp, - jpresult); - return rslt; - } - - jpresult = pbjson_get_object_value(&el, "p", &found); - if (jonmpOK == jpresult) { - struct pbjson_elem titel; - if (jonmpOK == pbjson_get_object_value(&found, "t", &titel)) { - if ((*titel.start != '"') || (titel.end[-1] != '"')) { - PUBNUB_LOG_ERROR("Time token in response is not a string\n"); - return rslt; - } - rslt.tt.ptr = (char*)titel.start + 1; - rslt.tt.size = titel.end - titel.start - 2; - } - else { - PUBNUB_LOG_ERROR( - "No timetoken value in subscribe V2 response found\n"); - return rslt; - } - } - else { - PUBNUB_LOG_ERROR("No message publish timetoken in subscribe V2 " - "response found, error=%d\n", - jpresult); - return rslt; - } + pubnub_mutex_lock(pb->monitor); + result = pbcc_get_msg_v2(&pb->core); + pubnub_mutex_unlock(pb->monitor); - if (jonmpOK == pbjson_get_object_value(&el, "b", &found)) { - rslt.match_or_group.ptr = (char*)found.start; - rslt.match_or_group.size = found.end - found.start; - } - - if (jonmpOK == pbjson_get_object_value(&el, "u", &found)) { - rslt.metadata.ptr = (char*)found.start; - rslt.metadata.size = found.end - found.start; - } - - return rslt; + return result; } diff --git a/core/pubnub_subscribe_v2.h b/core/pubnub_subscribe_v2.h index 80c760f1..b3e5eb9e 100644 --- a/core/pubnub_subscribe_v2.h +++ b/core/pubnub_subscribe_v2.h @@ -5,13 +5,12 @@ #include "pubnub_config.h" #include "pubnub_api_types.h" -#include "pubnub_memory_block.h" - #if !PUBNUB_USE_SUBSCRIBE_V2 #error To use the subscribe V2 API you must define PUBNUB_USE_SUBSCRIBE_V2=1 #endif +#include "pubnub_subscribe_v2_message.h" /** @file pubnub_subscribe_v2.h @@ -41,7 +40,7 @@ struct pubnub_subscribe_v2_options { be received, otherwise, it will be skipped (as if not published). Syntax is not trivial, but can be described as mostly Javascript on the metadata (which is JSON, thus, - "integrates well" wtih Javascript). For example, if your + "integrates well" with Javascript). For example, if your metadata is: `{"zec":3}`, then this filter _would_ match it: `zec==3`, while `zec==4` would _not_. @@ -79,43 +78,14 @@ enum pubnub_res pubnub_subscribe_v2(pubnub_t* p, struct pubnub_subscribe_v2_options opts); -/** Pubnub V2 message has lots of data and here's how we express them - for the pubnub_get_v2(). - - The "string fields" are expressed as "Pascal strings", that is, a - pointer with string length, and _don't_ include a NUL character. - Also, these pointers are actually pointing into the full received - message, so, their lifetime is tied to the message lifetime and - any subsequent transaction on the same context will invalidate - them. - -*/ -struct pubnub_v2_message { - /** The time token of the message - when it was published. */ - struct pubnub_char_mem_block tt; - /** Region of the message - not interesting in most cases */ - int region; - /** Message flags */ - int flags; - /** Channel that message was published to */ - struct pubnub_char_mem_block channel; - /** Subscription match or the channel group */ - struct pubnub_char_mem_block match_or_group; - /** The message itself */ - struct pubnub_char_mem_block payload; - /** The message metadata, as published */ - struct pubnub_char_mem_block metadata; -}; - /** Parse and return the next V2 message, if any. Do keep in mind that you can use pubnub_get() to get the full message in JSON, but, then you'll have to parse it yourself, and pubnub_channel() and pubnub_channel_group() would not work. - If there are no more messages, this will return a struct "memset - to zero". Since message must have the payload, the fail safe - way of checking is: + If there are no more messages, this will return an empty message v2 structure. + Since message must have the payload, the fail-safe way of checking is: v2msg = pubnub_get_v2(pbp); if (NULL == v2msg.payload.ptr) { diff --git a/core/pubnub_subscribe_v2_message.h b/core/pubnub_subscribe_v2_message.h new file mode 100644 index 00000000..176d3b89 --- /dev/null +++ b/core/pubnub_subscribe_v2_message.h @@ -0,0 +1,50 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PUBNUB_SUBSCRIBE_V2_MESSAGE +#define INC_PUBNUB_SUBSCRIBE_V2_MESSAGE + +#if !PUBNUB_USE_SUBSCRIBE_V2 +#error To use the subscribe V2 API you must define PUBNUB_USE_SUBSCRIBE_V2=1 +#endif + +#include +#include "pubnub_memory_block.h" + +/* subscribe_v2 message types */ +enum pubnub_message_type { + /* Indicates that message was received as a signal */ + pbsbSignal, + /* Indicates that message was published */ + pbsbPublished +}; + +/** Pubnub V2 message has lots of data and here's how we express them + for the pubnub_get_v2(). + + The "string fields" are expressed as "Pascal strings", that is, a + pointer with string length, and _don't_ include a NUL character. + Also, these pointers are actually pointing into the full received + message, so, their lifetime is tied to the message lifetime and + any subsequent transaction on the same context will invalidate + them. + */ +struct pubnub_v2_message { + /** The time token of the message - when it was published. */ + struct pubnub_char_mem_block tt; + /** Region of the message - not interesting in most cases */ + int region; + /** Message flags */ + int flags; + /** Channel that message was published to */ + struct pubnub_char_mem_block channel; + /** Subscription match or the channel group */ + struct pubnub_char_mem_block match_or_group; + /** The message itself */ + struct pubnub_char_mem_block payload; + /** The message metadata, as published */ + struct pubnub_char_mem_block metadata; + /** Indicates the message type: a signal, published, or something else */ + enum pubnub_message_type message_type; +}; + + +#endif /* INC_PUBNUB_SUBSCRIBE_V2_MESSAGE */ diff --git a/core/pubnub_version_internal.h b/core/pubnub_version_internal.h index bdbab8fe..8dce0948 100644 --- a/core/pubnub_version_internal.h +++ b/core/pubnub_version_internal.h @@ -3,7 +3,7 @@ #define INC_PUBNUB_VERSION_INTERNAL -#define PUBNUB_SDK_VERSION "2.8.4" +#define PUBNUB_SDK_VERSION "2.9.0" #endif /* !defined INC_PUBNUB_VERSION_INTERNAL */ diff --git a/core/samples/metadata.c b/core/samples/metadata.c index 7b53e7a2..4d8a3c12 100644 --- a/core/samples/metadata.c +++ b/core/samples/metadata.c @@ -29,6 +29,21 @@ static enum pubnub_res publish(pubnub_t* pbp, char const* chan, char const* meta } +static enum pubnub_res signal_trans(pubnub_t* pbp, char const* chan) +{ + enum pubnub_res res; + + puts("------------------"); + puts("Signal"); + puts("------------------"); + res = pubnub_signal(pbp, chan, "\"Signal world - sync w/meta\""); + if (PNR_STARTED == res) { + res = pubnub_await(pbp); + } + return res; +} + + static int printout_subscribe_v2_outcome(pubnub_t* pbp, enum pubnub_res res) { struct pubnub_v2_message msg; @@ -42,6 +57,7 @@ static int printout_subscribe_v2_outcome(pubnub_t* pbp, enum pubnub_res res) printf(" Timetoken = '%.*s'\n", (int)msg.tt.size, msg.tt.ptr); printf(" Metadata = '%.*s'\n", (int)msg.metadata.size, msg.metadata.ptr); printf(" Payload = '%.*s'\n", (int)msg.payload.size, msg.payload.ptr); + printf(" MessageType= '%s'\n", pubnub_msg_type_to_str(msg.message_type)); } return 0; } @@ -49,14 +65,6 @@ static int printout_subscribe_v2_outcome(pubnub_t* pbp, enum pubnub_res res) static void sync_sample_free(pubnub_t* p) { - if (PN_CANCEL_STARTED == pubnub_cancel(p)) { - enum pubnub_res pnru = pubnub_await(p); - if (pnru != PNR_OK) { - printf("Awaiting cancel failed: %d('%s')\n", - pnru, - pubnub_res_2_string(pnru)); - } - } if (pubnub_free(p) != 0) { printf("Failed to free the Pubnub context\n"); } @@ -89,7 +97,7 @@ static int doit(pubnub_t* pbp) printf("publish failed, result=%d('%s')\n", res, pubnub_res_2_string(res)); return -1; } - + puts("------------------------------------"); puts("Subscribe w/filter that's satisfied."); puts("------------------------------------"); @@ -103,6 +111,23 @@ static int doit(pubnub_t* pbp) return -1; } + res = signal_trans(pbp, chan); + if (PNR_OK != res) { + printf("signal failed, result=%d('%s')\n", res, pubnub_res_2_string(res)); + return -1; + } + + puts("----------------------------------------"); + puts("No subscribe w/filter."); + puts("----------------------------------------"); + res = pubnub_subscribe_v2(pbp, chan, pubnub_subscribe_v2_defopts()); + if (PNR_STARTED == res) { + res = pubnub_await(pbp); + } + if (0 != printout_subscribe_v2_outcome(pbp, res)) { + return -1; + } + res = publish(pbp, chan, "{\"pub\": \"sub\"}"); if (PNR_OK != res) { printf("publish failed, result=%d('%s')\n", res, pubnub_res_2_string(res)); diff --git a/core/samples/pubnub_publish_via_post_sample.c b/core/samples/pubnub_publish_via_post_sample.c index f68bfe33..fa51552d 100644 --- a/core/samples/pubnub_publish_via_post_sample.c +++ b/core/samples/pubnub_publish_via_post_sample.c @@ -26,7 +26,7 @@ static void sync_sample_free(pubnub_t* p) } -static struct pubnub_publish_options publish_opts_method(enum pubnub_publish_method method) +static struct pubnub_publish_options publish_opts_method(enum pubnub_method method) { struct pubnub_publish_options opts = pubnub_publish_defopts(); opts.method = method; @@ -66,7 +66,7 @@ static int alloc_and_start_publish_via_post(pubnub_t* pb, *publish_res = pubnub_publish_ex(pb, channel, mem, - publish_opts_method(pubnubPublishViaPOST)); + publish_opts_method(pubnubSendViaPOST)); *allocated = mem; return 0; } @@ -135,7 +135,7 @@ int main() res = pubnub_publish_ex(pbp, chan, message_via_post_for_gzip, - publish_opts_method(pubnubPublishViaPOSTwithGZIP)); + publish_opts_method(pubnubSendViaPOSTwithGZIP)); if (PNR_STARTED == res) { res = pubnub_await(pbp); } diff --git a/cpp/posix.mk b/cpp/posix.mk index 43e3e23b..47483889 100644 --- a/cpp/posix.mk +++ b/cpp/posix.mk @@ -1,4 +1,4 @@ -SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_coreapi_ex.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_sockets.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../core/pubnub_helper.c pubnub_version_posix.cpp ../posix/pubnub_generate_uuid_posix.c ../posix/pbpal_posix_blocking_io.c ../core/pubnub_free_with_timeout_std.c pubnub_subloop.cpp ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c +SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_coreapi_ex.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_sockets.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../lib/pb_strnlen_s.c ../core/pubnub_helper.c pubnub_version_posix.cpp ../posix/pubnub_generate_uuid_posix.c ../posix/pbpal_posix_blocking_io.c ../core/pubnub_free_with_timeout_std.c pubnub_subloop.cpp ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c ifndef ONLY_PUBSUB_API ONLY_PUBSUB_API = 0 @@ -24,6 +24,10 @@ ifndef USE_ADVANCED_HISTORY USE_ADVANCED_HISTORY = 1 endif +ifndef USE_OBJECTS_API +USE_OBJECTS_API = 1 +endif + ifeq ($(USE_PROXY), 1) SOURCEFILES += ../core/pubnub_proxy.c ../core/pubnub_proxy_core.c ../core/pbhttp_digest.c ../core/pbntlm_core.c ../core/pbntlm_packer_std.c endif @@ -39,8 +43,8 @@ OBJFILES += miniz_tinfl.o pbgzip_decompress.o endif ifeq ($(USE_SUBSCRIBE_V2), 1) -SOURCEFILES += ../core/pubnub_subscribe_v2.c -OBJFILES += pubnub_subscribe_v2.o +SOURCEFILES += ../core/pbcc_subscribe_v2.c ../core/pubnub_subscribe_v2.c +OBJFILES += pbcc_subscribe_v2.o pubnub_subscribe_v2.o endif ifeq ($(USE_ADVANCED_HISTORY), 1) @@ -48,6 +52,11 @@ SOURCEFILES += ../core/pbcc_advanced_history.c ../core/pubnub_advanced_history.c OBJFILES += pbcc_advanced_history.o pubnub_advanced_history.o endif +ifeq ($(USE_OBJECTS_API), 1) +SOURCEFILES += ../core/pbcc_objects_api.c ../core/pubnub_objects_api.c +OBJFILES += pbcc_objects_api.o pubnub_objects_api.o +endif + OS := $(shell uname) ifeq ($(OS),Darwin) SOURCEFILES += ../posix/monotonic_clock_get_time_darwin.c @@ -57,7 +66,7 @@ SOURCEFILES += ../posix/monotonic_clock_get_time_posix.c LDLIBS=-lrt -lpthread endif -CFLAGS =-g -I .. -I ../posix -I . -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) +CFLAGS =-g -I .. -I ../posix -I . -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) -D PUBNUB_USE_OBJECTS_API=$(USE_OBJECTS_API) # -g enables debugging, remove to get a smaller executable diff --git a/cpp/posix_openssl.mk b/cpp/posix_openssl.mk index 9f6936c3..347bff6c 100644 --- a/cpp/posix_openssl.mk +++ b/cpp/posix_openssl.mk @@ -1,4 +1,4 @@ -SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../openssl/pbpal_openssl.c ../openssl/pbpal_connect_openssl.c ../openssl/pbpal_add_system_certs_posix.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_free_with_timeout_std.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../core/pubnub_helper.c pubnub_version_posix.cpp ../posix/pubnub_generate_uuid_posix.c ../openssl/pbpal_openssl_blocking_io.c ../core/pubnub_crypto.c ../core/pubnub_coreapi_ex.c ../openssl/pbaes256.c ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c +SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../openssl/pbpal_openssl.c ../openssl/pbpal_connect_openssl.c ../openssl/pbpal_add_system_certs_posix.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_free_with_timeout_std.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../lib/pb_strnlen_s.c ../core/pubnub_helper.c pubnub_version_posix.cpp ../posix/pubnub_generate_uuid_posix.c ../openssl/pbpal_openssl_blocking_io.c ../core/pubnub_crypto.c ../core/pubnub_coreapi_ex.c ../openssl/pbaes256.c ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c ifndef ONLY_PUBSUB_API ONLY_PUBSUB_API = 0 @@ -24,6 +24,10 @@ ifndef USE_ADVANCED_HISTORY USE_ADVANCED_HISTORY = 1 endif +ifndef USE_OBJECTS_API +USE_OBJECTS_API = 1 +endif + ifeq ($(USE_PROXY), 1) SOURCEFILES += ../core/pubnub_proxy.c ../core/pubnub_proxy_core.c ../core/pbhttp_digest.c ../core/pbntlm_core.c ../core/pbntlm_packer_std.c endif @@ -39,8 +43,8 @@ OBJFILES += miniz_tinfl.o pbgzip_decompress.o endif ifeq ($(USE_SUBSCRIBE_V2), 1) -SOURCEFILES += ../core/pubnub_subscribe_v2.c -OBJFILES += pubnub_subscribe_v2.o +SOURCEFILES += ../core/pbcc_subscribe_v2.c ../core/pubnub_subscribe_v2.c +OBJFILES += pbcc_subscribe_v2.o pubnub_subscribe_v2.o endif ifeq ($(USE_ADVANCED_HISTORY), 1) @@ -48,7 +52,12 @@ SOURCEFILES += ../core/pbcc_advanced_history.c ../core/pubnub_advanced_history.c OBJFILES += pbcc_advanced_history.o pubnub_advanced_history.o endif -CFLAGS =-g -I .. -I . -I ../openssl -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) +ifeq ($(USE_OBJECTS_API), 1) +SOURCEFILES += ../core/pbcc_objects_api.c ../core/pubnub_objects_api.c +OBJFILES += pbcc_objects_api.o pubnub_objects_api.o +endif + +CFLAGS =-g -I .. -I . -I ../openssl -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) -D PUBNUB_USE_OBJECTS_API=$(USE_OBJECTS_API) # -g enables debugging, remove to get a smaller executable OS := $(shell uname) diff --git a/cpp/pubnub.hpp b/cpp/pubnub.hpp index ec45fd8c..6b2b6bc8 100644 --- a/cpp/pubnub.hpp +++ b/cpp/pubnub.hpp @@ -23,6 +23,8 @@ extern "C" { } #endif +#include "tribool.hpp" + namespace pubnub { class context; @@ -154,101 +156,6 @@ class caller_keeper { }; #endif -/** A simple C++ tribool type. It has `true`, `false` and - `not_set`. - */ -class tribool { -public: - enum not_set_t { not_set = 2 }; - tribool() - : d_3log(not_set) - { - } - tribool(enum pubnub_tribool v) - : d_3log(v) - { - } - tribool(bool v) - : d_3log(v) - { - } - tribool(not_set_t) - : d_3log(not_set) - { - } - - tribool operator!() const - { - static const tribool rslt[3] = { true, false, not_set }; - return rslt[d_3log]; - } - bool is_set() const { return d_3log != not_set; } - - tribool operator&&(bool t) const { return *this && tribool(t); } - tribool operator&&(tribool t) const - { - static const tribool rslt[3][3] = { { false, false, false }, - { false, true, not_set }, - { false, not_set, not_set } }; - return rslt[d_3log][t.d_3log]; - } - - tribool operator||(bool t) const { return *this || tribool(t); } - tribool operator||(tribool t) const - { - static const tribool rslt[3][3] = { { false, true, not_set }, - { true, true, true }, - { not_set, true, not_set } }; - return rslt[d_3log][t.d_3log]; - } - - tribool operator==(tribool t) const - { - static const tribool rslt[3][3] = { { true, false, not_set }, - { false, true, not_set }, - { not_set, not_set, not_set } }; - return rslt[d_3log][t.d_3log]; - } - tribool operator==(bool t) const - { - return !is_set() ? not_set - : static_cast(static_cast(d_3log) == t); - } - - tribool operator!=(tribool t) const - { - static const tribool rslt[3][3] = { { false, true, not_set }, - { true, false, not_set }, - { not_set, not_set, not_set } }; - return rslt[d_3log][t.d_3log]; - } - tribool operator!=(bool t) const - { - return !is_set() ? not_set - : static_cast(static_cast(d_3log) != t); - } - - operator bool() const { return true == static_cast(d_3log); } - - std::string to_string() const - { - static char const* rslt[3] = { "false", "true", "not-set" }; - return rslt[d_3log]; - } - -private: - int d_3log; -}; - -inline tribool operator==(tribool, tribool::not_set_t) -{ - return tribool::not_set; -} - -inline tribool operator!=(tribool, tribool::not_set_t) -{ - return tribool::not_set; -} /** A future (pending) result of a Pubnub * transaction/operation/request. It is somewhat similar to the diff --git a/cpp/pubnub_common.hpp b/cpp/pubnub_common.hpp index 835b7c3d..a9e0a11a 100644 --- a/cpp/pubnub_common.hpp +++ b/cpp/pubnub_common.hpp @@ -17,19 +17,34 @@ extern "C" { #include "core/pubnub_timers.h" #include "core/pubnub_helper.h" #include "core/pubnub_free_with_timeout.h" +#if defined(PUBNUB_CALLBACK_API) +#include "core/pubnub_ntf_callback.h" +#endif #if PUBNUB_PROXY_API #include "core/pubnub_proxy.h" #endif +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "core/pubnub_subscribe_v2.h" +#endif #if PUBNUB_CRYPTO_API #include "core/pubnub_crypto.h" #endif #include "core/pubnub_advanced_history.h" #define MAX_ERROR_MESSAGE_LENGTH 100 +#if PUBNUB_USE_OBJECTS_API +#include "core/pubnub_objects_api.h" +#define MAX_INCLUDE_DIMENSION 100 +#define MAX_ELEM_LENGTH 30 +#endif #if PUBNUB_USE_EXTERN_C } #endif #include "pubnub_mutex.hpp" +#include "tribool.hpp" +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "pubnub_v2_message.hpp" +#endif #include #include @@ -193,6 +208,44 @@ class subscribe_options { }; +#if PUBNUB_USE_SUBSCRIBE_V2 +/** A wrapper class for subscribe_v2 options, enabling a nicer + usage. Something like: + + pn.subscribe(chan, subscribe_v2_options().heartbeat(412)); +*/ +class subscribe_v2_options { + pubnub_subscribe_v2_options d_; + std::string d_chgrp; + std::string d_filter_expr; + +public: + subscribe_v2_options() { d_ = pubnub_subscribe_v2_defopts(); } + subscribe_v2_options& channel_group(std::string const& chgroup) + { + d_chgrp = chgroup; + d_.channel_group = d_chgrp.empty() ? 0 : d_chgrp.c_str(); + return *this; + } + subscribe_v2_options& channel_group(std::vector const& chgroup) + { + return channel_group(join(chgroup)); + } + subscribe_v2_options& heartbeat(unsigned hb_interval) + { + d_.heartbeat = hb_interval; + return *this; + } + subscribe_v2_options& filter_expr(std::string const& filter_exp) + { + d_filter_expr = filter_exp; + d_.filter_expr = d_filter_expr.empty() ? 0 : d_filter_expr.c_str(); + return *this; + } + pubnub_subscribe_v2_options data() { return d_; } +}; +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + /** A wrapper class for publish options, enabling a nicer usage. Something like: @@ -228,7 +281,7 @@ class publish_options { d_.meta = d_mtdt.c_str(); return *this; } - publish_options& method(pubnub_publish_method method) + publish_options& method(pubnub_method method) { d_.method = method; return *this; @@ -318,6 +371,93 @@ class history_options { pubnub_history_options data() { return d_; } }; +#if PUBNUB_USE_OBJECTS_API +/** A wrapper class for objects api managing include parameter */ +class include_options { + char d_include_c_strings_array[MAX_INCLUDE_DIMENSION][MAX_ELEM_LENGTH + 1]; + size_t d_include_count; + +public: + include_options() + : d_include_count(0) + {} + char const** include_to_c_strings_array(std::vector const& inc) + { + size_t n = inc.size(); + unsigned i; + if (n > MAX_INCLUDE_DIMENSION) { + throw std::range_error("include parameter has too many elements."); + } + for (i = 0; i < n; i++) { + if (inc[i].size() > MAX_ELEM_LENGTH) { + throw std::range_error("include string element is too long."); + } + strcpy(d_include_c_strings_array[i], inc[i].c_str()); + } + d_include_count = n; + return (char const**)d_include_c_strings_array; + } + include_options(std::vector const& inc) + { + include_to_c_strings_array(inc); + } + size_t include_count() { return d_include_count; } + char const** include_c_strings_array() { return (char const**)d_include_c_strings_array; } +}; + +/** A wrapper class for objects api paging option parameters, enabling a nicer + usage. Something like: + pbp.fetch_users(list_options().start(last_bookmark)); + + instead of: + pbp.fetch_users(nullopt, nullopt, last_bookmark, “”, nullopt); + */ +class list_options : public include_options { + size_t d_limit; + std::string d_start; + std::string d_end; + tribool d_count; + +public: + list_options() + : d_limit(0) + , d_count(tribool::not_set) + {} + list_options& limit(size_t lim) + { + d_limit = lim; + return *this; + } + size_t limit() { return d_limit; } + list_options& start(std::string const& st) + { + d_start = st; + return *this; + } + std::string start() { return d_start; } + list_options& end(std::string const& e) + { + d_end = e; + return *this; + } + std::string end() { return d_end; } + list_options& count(tribool co) + { + d_count = co; + return *this; + } + pubnub_tribool count() + { + if (false == d_count) { + return pbccFalse; + } + else if (true == d_count) { + return pbccTrue; + } + return pbccNotSet; + } +}; +#endif /* PUBNUB_USE_OBJECTS_API */ /** The C++ Pubnub context. It is a wrapper of the Pubnub C context, * not a "native" C++ implementation. @@ -455,6 +595,29 @@ class context { return all; } +#if PUBNUB_USE_SUBSCRIBE_V2 + /// Returns the next v2 message from the context. If there are + /// none, returns an empty message structure(checked through + /// v2_mesage::is_empty()). + /// @see pubnub_get_v2 + v2_message get_v2() const + { + return v2_message(pubnub_get_v2(d_pb)); + } + /// Returns a vector of all v2 messages from the context. + std::vector get_all_v2() const + { + std::vector all; + v2_message msg = get_v2(); + + while (!msg.is_empty()) { + all.push_back(msg); + msg = get_v2(); + } + return all; + } +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + #if PUBNUB_CRYPTO_API /// Returns the next message from the context, decrypted with /// @p cipher_key. If there are none, returns an empty string. @@ -529,24 +692,30 @@ class context { return doit(pubnub_publish(d_pb, channel.c_str(), message.c_str())); } - /// Publishes a @p message on the @p channel with "extendded" - /// (full) options. The @p channel can have many channels - /// separated by a comma @see pubnub_publish + /// Publishes a @p message on the @p channel with "extended" + /// (full) options. futres publish(std::string const& channel, std::string const& message, publish_options opt) { - if (message.size() + 1 > sizeof d_message_to_publish) { + if (message.size() + 1 > sizeof d_message_to_send) { throw std::range_error("string for publish too long"); } lock_guard lck(d_mutex); if (!pubnub_can_start_transaction(d_pb)) { return futres(d_pb, *this, PNR_IN_PROGRESS); } - strcpy(d_message_to_publish, message.c_str()); - return doit(pubnub_publish_ex(d_pb, channel.c_str(), d_message_to_publish, opt.data())); + strcpy(d_message_to_send, message.c_str()); + return doit(pubnub_publish_ex(d_pb, channel.c_str(), d_message_to_send, opt.data())); } + /// Sends a signal @p message on the @p channel. + /// @see pubnub_signal + futres signal(std::string const& channel, std::string const& message) + { + return doit(pubnub_signal(d_pb, channel.c_str(), message.c_str())); + } + #if PUBNUB_CRYPTO_API /// Publishes a @p message on the @p channel encrypted with @p /// cipher_key. @@ -594,6 +763,23 @@ class context { return subscribe(join(channel), opt); } +#if PUBNUB_USE_SUBSCRIBE_V2 + /// V2 subscribes to @p channel with "extended" (full) options + /// @see pubnub_subscribe_v2 + futres subscribe_v2(std::string const& channel, subscribe_v2_options opt) + { + char const* ch = channel.empty() ? 0 : channel.c_str(); + return doit(pubnub_subscribe_v2(d_pb, ch, opt.data())); + } + + /// Pass a vector of channels in the @p channel and we'll put + /// commas between them. A helper function. + futres subscribe_v2(std::vector const& channel, subscribe_v2_options opt) + { + return subscribe_v2(join(channel), opt); + } +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + /// Leaves a @p channel and/or @p channel_group /// @see pubnub_leave futres leave(std::string const& channel, std::string const& channel_group) @@ -896,6 +1082,238 @@ class context { return doit(pubnub_list_channel_group(d_pb, channel_group.c_str())); } +#if PUBNUB_USE_OBJECTS_API + /// Starts a transaction for optaining a paginated list of users associated + /// with the subscription key. + /// @see pubnub_fetch_all_users + futres fetch_all_users(list_options& options) + { + return doit(pubnub_fetch_all_users( + d_pb, + options.include_c_strings_array(), + options.include_count(), + options.limit(), + (options.start().size() ? options.start().c_str() : NULL), + (options.end().size() ? options.end().c_str() : NULL), + options.count())); + } + + /// Starts a transaction that creates a user with the attributes specified in @p user_obj. + /// @see pubnub_create_user + futres create_user(std::string const& user_obj, std::vector& include) + { + include_options inc(include); + if (user_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error("string 'user_obj' for transaction 'create_user()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, user_obj.c_str()); + return doit(pubnub_create_user( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } + + /// Starts a transaction that returns the user object specified by @p user_id. + /// @see pubnub_fetch_user + futres fetch_user(std::string const& user_id, std::vector& include) + { + include_options inc(include); + return doit(pubnub_fetch_user( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + user_id.c_str())); + } + + /// Starts a transaction that updates the user object specified by the `id` key + /// of the @p user_obj. + /// @see pubnub_update_user + futres update_user(std::string const& user_obj, std::vector& include) + { + include_options inc(include); + if (user_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error("string 'user_obj' for transaction 'update_user()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, user_obj.c_str()); + return doit(pubnub_update_user( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } + + /// Starts a transaction that deletes the user specified by @p user_id. + /// @see pubnub_delete_user + futres delete_user(std::string const& user_id) + { + return doit(pubnub_delete_user(d_pb, user_id.c_str())); + } + + /// Starts a transaction that returns the spaces associated with the subscriber key. + /// @see pubnub_fetch_all_spaces + futres fetch_all_spaces(list_options& options) + { + return doit(pubnub_fetch_all_spaces( + d_pb, + options.include_c_strings_array(), + options.include_count(), + options.limit(), + (options.start().size() ? options.start().c_str() : NULL), + (options.end().size() ? options.end().c_str() : NULL), + options.count())); + } + + /// Starts a transaction that creates a space with the attributes specified in @p space_obj. + /// @see pubnub_create_space + futres create_space(std::string const& space_obj, std::vector& include) + { + include_options inc(include); + if (space_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error("string 'space_obj' for transaction 'create_space()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, space_obj.c_str()); + return doit(pubnub_create_space( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } + + /// Starts a transaction that returns the space object specified by @p space_id. + /// @see pubnub_fetch_space + futres fetch_space(std::string const& space_id, std::vector& include) + { + include_options inc(include); + return doit(pubnub_fetch_space( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + space_id.c_str())); + } + + /// Starts a transaction that updates the space specified by the `id` property + /// of the @p space_obj. + /// @see pubnub_update_space + futres update_space(std::string const& space_obj, std::vector& include) + { + include_options inc(include); + if (space_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error("string 'space_obj' for transaction 'update_space()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, space_obj.c_str()); + return doit(pubnub_update_space( + d_pb, + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } + + /// Starts a transaction that deletes the space specified with @p space_id. + /// @see pubnub_delete_space + futres delete_space(std::string const& space_id) + { + return doit(pubnub_delete_space(d_pb, space_id.c_str())); + } + + /// Starts a transaction that returns the space memberships of the user specified + /// by @p user_id. + /// @see pubnub_fetch_users_space_memberships + futres fetch_users_space_memberships(std::string const& user_id, list_options& options) + { + return doit(pubnub_fetch_users_space_memberships( + d_pb, + user_id.c_str(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + (options.start().size() ? options.start().c_str() : NULL), + (options.end().size() ? options.end().c_str() : NULL), + options.count())); + } + + /// Starts a transaction that updates the space memberships of the user specified + /// by @p user_id. + /// @see pubnub_update_users_space_memberships + futres update_users_space_memberships(std::string const& user_id, + std::string const& update_obj, + std::vector& include) + { + include_options inc(include); + if (update_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error( + "string 'update_obj' for transaction 'update_users_space_memberships()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, update_obj.c_str()); + return doit(pubnub_update_users_space_memberships( + d_pb, + user_id.c_str(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } + + /// Starts a transaction that returns all users in the space specified by @p space_id. + /// @see pubnub_fetch_members_in_space + futres fetch_members_in_space(std::string const& space_id, list_options& options) + { + return doit(pubnub_fetch_members_in_space( + d_pb, + space_id.c_str(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + (options.start().size() ? options.start().c_str() : NULL), + (options.end().size() ? options.end().c_str() : NULL), + options.count())); + } + + /// Starts a transaction that updates the list of members of the space specified + /// by @p space_id. + /// @see pubnub_update_members_in_space + futres update_members_in_space(std::string const& space_id, + std::string const& update_obj, + std::vector& include) + { + include_options inc(include); + if (update_obj.size() + 1 > sizeof d_message_to_send) { + throw std::range_error( + "string 'update_obj' for transaction 'update_members_in_space()' too long."); + } + lock_guard lck(d_mutex); + if (!pubnub_can_start_transaction(d_pb)) { + return futres(d_pb, *this, PNR_IN_PROGRESS); + } + strcpy(d_message_to_send, update_obj.c_str()); + return doit(pubnub_update_members_in_space( + d_pb, + space_id.c_str(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send)); + } +#endif /* PUBNUB_USE_OBJECTS_API */ + /// Return the HTTP code (result) of the last transaction. /// @see pubnub_last_http_code int last_http_code() const @@ -1073,6 +1491,16 @@ class context { } #endif + /// Enables safe exit from the main() in callback environment by disabling + /// platform watcher thread. + /// @see pubnub_stop() + void stop(void) + { +#if defined(PUBNUB_CALLBACK_API) + pubnub_stop(); +#endif + } + ~context() { if (d_pb) { @@ -1100,8 +1528,8 @@ class context { /// The origin set last time (doen't have to be the one used, /// the default can be used instead) std::string d_origin; - /// Buffer for message to be published - char d_message_to_publish[PUBNUB_BUF_MAXLEN]; + /// Buffer for message to be sent( via PATCH, or POST method) + char d_message_to_send[PUBNUB_BUF_MAXLEN]; /// The (C) Pubnub context pubnub_t* d_pb; }; diff --git a/cpp/pubnub_v2_message.hpp b/cpp/pubnub_v2_message.hpp new file mode 100644 index 00000000..cb949bc8 --- /dev/null +++ b/cpp/pubnub_v2_message.hpp @@ -0,0 +1,38 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PUBNUB_V2_MESSAGE_HPP +#define INC_PUBNUB_V2_MESSAGE_HPP + +#if !PUBNUB_USE_SUBSCRIBE_V2 +#error To use the subscribe V2 API you must define PUBNUB_USE_SUBSCRIBE_V2=1 +#endif + +#include + +namespace pubnub { + +class v2_message { + struct pubnub_v2_message d_; + +public: + v2_message(struct pubnub_v2_message message_v2) { d_ = message_v2; } + v2_message() { memset(&d_, 0, sizeof d_); } + std::string tt() const { return std::string(d_.tt.ptr, d_.tt.size); } + int region() const { return d_.region; } + int flags() const { return d_.flags; } + std::string channel() const { return std::string(d_.channel.ptr, d_.channel.size); } + std::string match_or_group() const + { + return std::string(d_.match_or_group.ptr, d_.match_or_group.size); + } + std::string payload() const { return std::string(d_.payload.ptr, d_.payload.size); } + std::string metadata() const { return std::string(d_.metadata.ptr, d_.metadata.size); } + pubnub_message_type message_type() const { return d_.message_type; } + /** Message structure is considered empty if there is no timetoken info(whose + existence is obligatory for any valid v2 message) + */ + bool is_empty() const { return (0 == d_.tt.ptr) || (0 == d_.tt.size); } +}; + +} // namespace pubnub + +#endif /* INC_PUBNUB_V2_MESSAGE_HPP */ diff --git a/cpp/tribool.hpp b/cpp/tribool.hpp new file mode 100644 index 00000000..57783a1c --- /dev/null +++ b/cpp/tribool.hpp @@ -0,0 +1,105 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_TRIBOOL_HPP +#define INC_TRIBOOL_HPP + +namespace pubnub { + +/** A simple C++ tribool type. It has `true`, `false` and + `not_set`. + */ +class tribool { +public: + enum not_set_t { not_set = 2 }; + tribool() + : d_3log(not_set) + { + } + tribool(enum pubnub_tribool v) + : d_3log(v) + { + } + tribool(bool v) + : d_3log(v) + { + } + tribool(not_set_t) + : d_3log(not_set) + { + } + + tribool operator!() const + { + static const tribool rslt[3] = { true, false, not_set }; + return rslt[d_3log]; + } + bool is_set() const { return d_3log != not_set; } + + tribool operator&&(bool t) const { return *this && tribool(t); } + tribool operator&&(tribool t) const + { + static const tribool rslt[3][3] = { { false, false, false }, + { false, true, not_set }, + { false, not_set, not_set } }; + return rslt[d_3log][t.d_3log]; + } + + tribool operator||(bool t) const { return *this || tribool(t); } + tribool operator||(tribool t) const + { + static const tribool rslt[3][3] = { { false, true, not_set }, + { true, true, true }, + { not_set, true, not_set } }; + return rslt[d_3log][t.d_3log]; + } + + tribool operator==(tribool t) const + { + static const tribool rslt[3][3] = { { true, false, not_set }, + { false, true, not_set }, + { not_set, not_set, not_set } }; + return rslt[d_3log][t.d_3log]; + } + tribool operator==(bool t) const + { + return !is_set() ? not_set + : static_cast(static_cast(d_3log) == t); + } + + tribool operator!=(tribool t) const + { + static const tribool rslt[3][3] = { { false, true, not_set }, + { true, false, not_set }, + { not_set, not_set, not_set } }; + return rslt[d_3log][t.d_3log]; + } + tribool operator!=(bool t) const + { + return !is_set() ? not_set + : static_cast(static_cast(d_3log) != t); + } + + operator bool() const { return true == static_cast(d_3log); } + + std::string to_string() const + { + static char const* rslt[3] = { "false", "true", "not-set" }; + return rslt[d_3log]; + } + +private: + int d_3log; +}; + +inline tribool operator==(tribool, tribool::not_set_t) +{ + return tribool::not_set; +} + +inline tribool operator!=(tribool, tribool::not_set_t) +{ + return tribool::not_set; +} + +} // namespace pubnub + +#endif // !defined INC_TRIBOOL_HPP diff --git a/cpp/windows.mk b/cpp/windows.mk index c696848d..8ee094fd 100644 --- a/cpp/windows.mk +++ b/cpp/windows.mk @@ -1,4 +1,4 @@ -SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_coreapi_ex.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_sockets.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_timers.c ..\core\pubnub_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_json_parse.c ..\core\pubnub_free_with_timeout_std.c ..\lib\md5\md5.c ..\core\pubnub_helper.c pubnub_version_windows.cpp ..\windows\pubnub_generate_uuid_windows.c ..\windows\pbpal_windows_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c +SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_coreapi_ex.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_sockets.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_timers.c ..\core\pubnub_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_json_parse.c ..\core\pubnub_free_with_timeout_std.c ..\lib\md5\md5.c ..\lib\pb_strnlen_s.c ..\core\pubnub_helper.c pubnub_version_windows.cpp ..\windows\pubnub_generate_uuid_windows.c ..\windows\pbpal_windows_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pbcc_subscribe_v2.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c ..\core\pbcc_objects_api.c ..\core\pubnub_objects_api.c LIBS=ws2_32.lib rpcrt4.lib diff --git a/cpp/windows_openssl.mk b/cpp/windows_openssl.mk index 45acf2e3..eec55569 100644 --- a/cpp/windows_openssl.mk +++ b/cpp/windows_openssl.mk @@ -1,4 +1,4 @@ -SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\openssl\pbpal_openssl.c ..\openssl\pbpal_connect_openssl.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_json_parse.c ..\core\pubnub_helper.c pubnub_version_windows.cpp ..\windows\pubnub_generate_uuid_windows.c ..\openssl\pbpal_openssl_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_timers.c ..\core\c99\snprintf.c ..\openssl\pbpal_add_system_certs_windows.c ..\core\pubnub_free_with_timeout_std.c ..\lib\md5\md5.c ..\core\pubnub_ssl.c ..\core\pubnub_crypto.c ..\core\pubnub_coreapi_ex.c ..\openssl\pbaes256.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c +SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\openssl\pbpal_openssl.c ..\openssl\pbpal_connect_openssl.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_json_parse.c ..\core\pubnub_helper.c pubnub_version_windows.cpp ..\windows\pubnub_generate_uuid_windows.c ..\openssl\pbpal_openssl_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_timers.c ..\core\c99\snprintf.c ..\openssl\pbpal_add_system_certs_windows.c ..\core\pubnub_free_with_timeout_std.c ..\lib\md5\md5.c ..\lib\pb_strnlen_s.c ..\core\pubnub_ssl.c ..\core\pubnub_crypto.c ..\core\pubnub_coreapi_ex.c ..\openssl\pbaes256.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pbcc_subscribe_v2.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c ..\core\pbcc_objects_api.c ..\core\pubnub_objects_api.c !ifndef OPENSSLPATH OPENSSLPATH=c:\OpenSSL-Win32 diff --git a/lib/pb_strnlen_s.c b/lib/pb_strnlen_s.c new file mode 100644 index 00000000..af9a0096 --- /dev/null +++ b/lib/pb_strnlen_s.c @@ -0,0 +1,18 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#include "lib/pb_strnlen_s.h" + +size_t pb_strnlen_s(const char *str, size_t strsz) +{ + size_t i; + + if (NULL == str) { + return 0; + } + for(i = 0; i < strsz; i++, str++) { + if ('\0' == *str) { + break; + } + } + + return i; +} diff --git a/lib/pb_strnlen_s.h b/lib/pb_strnlen_s.h new file mode 100644 index 00000000..9e79733f --- /dev/null +++ b/lib/pb_strnlen_s.h @@ -0,0 +1,20 @@ +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#if !defined INC_PB_STRNLEN_S +#define INC_PB_STRNLEN_S + +#include + +/** Returns the length of the given null-terminated byte string, that is, the number + of characters in a character array whose first element is pointed to by @p str up to + and not including the first null character. + The function returns zero if @p str is a null pointer and returns @p strsz if the null + character was not found in the first @p strsz bytes of @p str. + The behavior is undefined if both @p str points to a character array which lacks the null + character and the size of that character array is less than @p strsz; in other words, + an erroneous value of @p strsz does not expose the impending buffer overflow. + @param str pointer to the null-terminated byte string to be examined. + @param strsz maximum number of characters to examine. + */ +size_t pb_strnlen_s(const char *str, size_t strsz); + +#endif /* INC_Pb_STRNLEN_S */ diff --git a/openssl/posix.mk b/openssl/posix.mk index 3af1b482..b8758ab2 100644 --- a/openssl/posix.mk +++ b/openssl/posix.mk @@ -1,6 +1,6 @@ -SOURCEFILES = ../core/pubnub_ssl.c ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c pbpal_openssl.c pbpal_connect_openssl.c pbpal_add_system_certs_posix.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../posix/pubnub_version_posix.c ../posix/pubnub_generate_uuid_posix.c pbpal_openssl_blocking_io.c ../lib/base64/pbbase64.c ../core/pubnub_crypto.c ../core/pubnub_coreapi_ex.c ../core/pubnub_free_with_timeout_std.c pbaes256.c ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c +SOURCEFILES = ../core/pubnub_ssl.c ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c pbpal_openssl.c pbpal_connect_openssl.c pbpal_add_system_certs_posix.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../posix/pubnub_version_posix.c ../posix/pubnub_generate_uuid_posix.c pbpal_openssl_blocking_io.c ../lib/base64/pbbase64.c ../lib/pb_strnlen_s.c ../core/pubnub_crypto.c ../core/pubnub_coreapi_ex.c ../core/pubnub_free_with_timeout_std.c pbaes256.c ../posix/msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c -OBJFILES = pubnub_ssl.o pubnub_pubsubapi.o pubnub_coreapi.o pubnub_ccore_pubsub.o pubnub_ccore.o pubnub_netcore.o pbpal_resolv_and_connect_sockets.o pbpal_openssl.o pbpal_connect_openssl.o pbpal_add_system_certs_posix.o pubnub_alloc_std.o pubnub_assert_std.o pubnub_generate_uuid.o pubnub_blocking_io.o posix_socket_blocking_io.o pubnub_timers.o pubnub_json_parse.o pubnub_helper.o pubnub_version_posix.o pubnub_generate_uuid_posix.o pbpal_openssl_blocking_io.o pbbase64.o pubnub_crypto.o pubnub_coreapi_ex.o pubnub_free_with_timeout_std.o pbaes256.o msstopwatch_monotonic_clock.o pubnub_url_encode.o +OBJFILES = pubnub_ssl.o pubnub_pubsubapi.o pubnub_coreapi.o pubnub_ccore_pubsub.o pubnub_ccore.o pubnub_netcore.o pbpal_resolv_and_connect_sockets.o pbpal_openssl.o pbpal_connect_openssl.o pbpal_add_system_certs_posix.o pubnub_alloc_std.o pubnub_assert_std.o pubnub_generate_uuid.o pubnub_blocking_io.o posix_socket_blocking_io.o pubnub_timers.o pubnub_json_parse.o pubnub_helper.o pubnub_version_posix.o pubnub_generate_uuid_posix.o pbpal_openssl_blocking_io.o pbbase64.o pb_strnlen_s.o pubnub_crypto.o pubnub_coreapi_ex.o pubnub_free_with_timeout_std.o pbaes256.o msstopwatch_monotonic_clock.o pubnub_url_encode.o ifndef ONLY_PUBSUB_API ONLY_PUBSUB_API = 0 @@ -26,6 +26,10 @@ ifndef USE_ADVANCED_HISTORY USE_ADVANCED_HISTORY = 1 endif +ifndef USE_OBJECTS_API +USE_OBJECTS_API = 1 +endif + ifeq ($(USE_PROXY), 1) SOURCEFILES += ../core/pubnub_proxy.c ../core/pubnub_proxy_core.c ../core/pbhttp_digest.c ../core/pbntlm_core.c ../core/pbntlm_packer_std.c OBJFILES += pubnub_proxy.o pubnub_proxy_core.o pbhttp_digest.o pbntlm_core.o pbntlm_packer_std.o @@ -42,8 +46,8 @@ OBJFILES += miniz_tinfl.o pbgzip_decompress.o endif ifeq ($(USE_SUBSCRIBE_V2), 1) -SOURCEFILES += ../core/pubnub_subscribe_v2.c -OBJFILES += pubnub_subscribe_v2.o +SOURCEFILES += ../core/pbcc_subscribe_v2.c ../core/pubnub_subscribe_v2.c +OBJFILES += pbcc_subscribe_v2.o pubnub_subscribe_v2.o endif ifeq ($(USE_ADVANCED_HISTORY), 1) @@ -51,7 +55,12 @@ SOURCEFILES += ../core/pbcc_advanced_history.c ../core/pubnub_advanced_history.c OBJFILES += pbcc_advanced_history.o pubnub_advanced_history.o endif -CFLAGS = -g -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -Wall -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) +ifeq ($(USE_OBJECTS_API), 1) +SOURCEFILES += ../core/pbcc_objects_api.c ../core/pubnub_objects_api.c +OBJFILES += pbcc_objects_api.o pubnub_objects_api.o +endif + +CFLAGS = -g -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -Wall -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) -D PUBNUB_USE_OBJECTS_API=$(USE_OBJECTS_API) # -g enables debugging, remove to get a smaller executable # -fsanitize=address Use AddressSanitizer # -fsanitize=thread Use ThreadSanitizer @@ -71,7 +80,7 @@ endif INCLUDES=-I .. -I . -all: pubnub_sync_sample cancel_subscribe_sync_sample pubnub_sync_subloop_sample pubnub_publish_via_post_sample pubnub_advanced_history_sample pubnub_callback_sample subscribe_publish_callback_sample pubnub_callback_subloop_sample pubnub_fntest pubnub_console_sync pubnub_console_callback pubnub_crypto_sync_sample subscribe_publish_from_callback publish_callback_subloop_sample publish_queue_callback_subloop +all: pubnub_sync_sample metadata cancel_subscribe_sync_sample pubnub_sync_subloop_sample pubnub_publish_via_post_sample pubnub_advanced_history_sample pubnub_callback_sample subscribe_publish_callback_sample pubnub_callback_subloop_sample pubnub_fntest pubnub_console_sync pubnub_console_callback pubnub_crypto_sync_sample subscribe_publish_from_callback publish_callback_subloop_sample publish_queue_callback_subloop SYNC_INTF_SOURCEFILES=../core/pubnub_ntf_sync.c ../core/pubnub_sync_subscribe_loop.c ../core/srand_from_pubnub_time.c SYNC_INTF_OBJFILES=pubnub_ntf_sync.o pubnub_sync_subscribe_loop.o srand_from_pubnub_time.o @@ -111,6 +120,9 @@ pubnub_callback.a : $(SOURCEFILES) $(CALLBACK_INTF_SOURCEFILES) pubnub_sync_sample: ../core/samples/pubnub_sync_sample.c pubnub_sync.a $(CC) -o $@ $(CFLAGS) $(INCLUDES) ../core/samples/pubnub_sync_sample.c pubnub_sync.a $(LDLIBS) +metadata: ../core/samples/metadata.c pubnub_sync.a + $(CC) -o $@ $(CFLAGS) $(INCLUDES) ../core/samples/metadata.c pubnub_sync.a $(LDLIBS) + pubnub_crypto_sync_sample: ../core/samples/pubnub_crypto_sync_sample.c pubnub_sync.a $(CC) -o $@ $(CFLAGS) $(INCLUDES) ../core/samples/pubnub_crypto_sync_sample.c pubnub_sync.a $(LDLIBS) @@ -157,4 +169,4 @@ pubnub_console_callback: $(CONSOLE_SOURCEFILES) ../core/samples/console/pnc_ops_ clean: - rm pubnub_sync_sample pubnub_sync_subloop_sample cancel_subscribe_sync_sample pubnub_publish_via_post_sample pubnub_callback_sample subscribe_publish_callback_sample pubnub_fntest pubnub_console_sync pubnub_console_callback pubnub_crypto_sync_sample pubnub_sync.a pubnub_callback.a pubnub_callback_subloop_sample subscribe_publish_from_callback publish_callback_subloop_sample publish_queue_callback_subloop *.o *.dSYM + rm pubnub_sync_sample metadata pubnub_sync_subloop_sample cancel_subscribe_sync_sample pubnub_publish_via_post_sample pubnub_callback_sample subscribe_publish_callback_sample pubnub_fntest pubnub_console_sync pubnub_console_callback pubnub_crypto_sync_sample pubnub_sync.a pubnub_callback.a pubnub_callback_subloop_sample subscribe_publish_from_callback publish_callback_subloop_sample publish_queue_callback_subloop *.o *.dSYM diff --git a/openssl/pubnub_config.h b/openssl/pubnub_config.h index 17aaffcd..d54bf678 100644 --- a/openssl/pubnub_config.h +++ b/openssl/pubnub_config.h @@ -182,5 +182,13 @@ #define PUBNUB_USE_ADVANCED_HISTORY 1 #endif +#if !defined(PUBNUB_USE_OBJECTS_API) +/** If true (!=0) will enable using the objects API, which is a + collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) + on two new pubnub objects: User and Space, as well as manipulating connections + between them. */ +#define PUBNUB_USE_OBJECTS_API 1 +#endif + #endif /* !defined INC_PUBNUB_CONFIG */ diff --git a/openssl/windows.mk b/openssl/windows.mk index fe82b591..306b5f36 100644 --- a/openssl/windows.mk +++ b/openssl/windows.mk @@ -1,6 +1,6 @@ -SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c pbpal_openssl.c pbpal_connect_openssl.c pbpal_add_system_certs_windows.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_free_with_timeout_std.c ..\core\pubnub_timers.c ..\core\pubnub_json_parse.c ..\lib\md5\md5.c ..\core\pubnub_ssl.c ..\core\pubnub_helper.c ..\windows\pubnub_version_windows.c ..\windows\pubnub_generate_uuid_windows.c pbpal_openssl_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_crypto.c ..\core\pubnub_coreapi_ex.c pbaes256.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c +SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c pbpal_openssl.c pbpal_connect_openssl.c pbpal_add_system_certs_windows.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_free_with_timeout_std.c ..\core\pubnub_timers.c ..\core\pubnub_json_parse.c ..\lib\md5\md5.c ..\lib\pb_strnlen_s.c ..\core\pubnub_ssl.c ..\core\pubnub_helper.c ..\windows\pubnub_version_windows.c ..\windows\pubnub_generate_uuid_windows.c pbpal_openssl_blocking_io.c ..\lib\base64\pbbase64.c ..\core\pubnub_crypto.c ..\core\pubnub_coreapi_ex.c pbaes256.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pbcc_subscribe_v2.c ..\core\pubnub_subscribe_v2.c ..\windows\msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c ..\core\pbcc_objects_api.c ..\core\pubnub_objects_api.c -OBJFILES = pubnub_pubsubapi.obj pubnub_coreapi.obj pubnub_ccore_pubsub.obj pubnub_ccore.obj pubnub_netcore.obj pbpal_resolv_and_connect_sockets.obj pbpal_openssl.obj pbpal_connect_openssl.obj pbpal_add_system_certs_windows.obj pubnub_alloc_std.obj pubnub_assert_std.obj pubnub_generate_uuid.obj pubnub_blocking_io.obj pubnub_free_with_timeout_std.obj pubnub_timers.obj pubnub_json_parse.obj md5.obj pubnub_ssl.obj pubnub_helper.obj pubnub_version_windows.obj pubnub_generate_uuid_windows.obj pbpal_openssl_blocking_io.obj windows_socket_blocking_io.obj pbbase64.obj pubnub_crypto.obj pubnub_coreapi_ex.obj pbaes256.obj snprintf.obj miniz_tinfl.obj miniz_tdef.obj miniz.obj pbcrc32.obj pbgzip_compress.obj pbgzip_decompress.obj pubnub_subscribe_v2.obj msstopwatch_windows.obj pubnub_url_encode.obj pbcc_advanced_history.obj pubnub_advanced_history.obj +OBJFILES = pubnub_pubsubapi.obj pubnub_coreapi.obj pubnub_ccore_pubsub.obj pubnub_ccore.obj pubnub_netcore.obj pbpal_resolv_and_connect_sockets.obj pbpal_openssl.obj pbpal_connect_openssl.obj pbpal_add_system_certs_windows.obj pubnub_alloc_std.obj pubnub_assert_std.obj pubnub_generate_uuid.obj pubnub_blocking_io.obj pubnub_free_with_timeout_std.obj pubnub_timers.obj pubnub_json_parse.obj md5.obj pb_strnlen_s.obj pubnub_ssl.obj pubnub_helper.obj pubnub_version_windows.obj pubnub_generate_uuid_windows.obj pbpal_openssl_blocking_io.obj windows_socket_blocking_io.obj pbbase64.obj pubnub_crypto.obj pubnub_coreapi_ex.obj pbaes256.obj snprintf.obj miniz_tinfl.obj miniz_tdef.obj miniz.obj pbcrc32.obj pbgzip_compress.obj pbgzip_decompress.obj pbcc_subscribe_v2.obj pubnub_subscribe_v2.obj msstopwatch_windows.obj pubnub_url_encode.obj pbcc_advanced_history.obj pubnub_advanced_history.obj pbcc_objects_api.obj pubnub_objects_api.obj !ifndef OPENSSLPATH OPENSSLPATH=c:\OpenSSL-Win32 @@ -35,7 +35,7 @@ CFLAGS = /Zi /MP -D PUBNUB_THREADSAFE /D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNI INCLUDES=-I .. -I . -I ..\core\c99 -I $(OPENSSLPATH)\include -all: pubnub_sync_sample.exe pubnub_crypto_sync_sample.exe cancel_subscribe_sync_sample.exe pubnub_publish_via_post_sample.exe subscribe_publish_callback_sample.exe pubnub_callback_sample.exe pubnub_fntest.exe pubnub_console_sync.exe pubnub_advanced_history_sample.exe pubnub_console_callback.exe subscribe_publish_from_callback.exe publish_callback_subloop_sample.exe publish_queue_callback_subloop.exe +all: pubnub_sync_sample.exe metadata.exe pubnub_crypto_sync_sample.exe cancel_subscribe_sync_sample.exe pubnub_publish_via_post_sample.exe subscribe_publish_callback_sample.exe pubnub_callback_sample.exe pubnub_fntest.exe pubnub_console_sync.exe pubnub_advanced_history_sample.exe pubnub_console_callback.exe subscribe_publish_from_callback.exe publish_callback_subloop_sample.exe publish_queue_callback_subloop.exe SYNC_INTF_SOURCEFILES= ..\core\pubnub_ntf_sync.c ..\core\pubnub_sync_subscribe_loop.c ..\core\srand_from_pubnub_time.c SYNC_INTF_OBJFILES= pubnub_ntf_sync.obj pubnub_sync_subscribe_loop.obj srand_from_pubnub_time.obj @@ -54,6 +54,9 @@ pubnub_callback.lib : $(SOURCEFILES) $(PROXY_INTF_SOURCEFILES) $(CALLBACK_INTF_S pubnub_sync_sample.exe: ..\core\samples\pubnub_sync_sample.c pubnub_sync.lib $(CC) $(CFLAGS) $(INCLUDES) ..\core\samples\pubnub_sync_sample.c pubnub_sync.lib $(LIBS) +metadata.exe: ..\core\samples\metadata.c pubnub_sync.lib + $(CC) $(CFLAGS) $(INCLUDES) ..\core\samples\metadata.c pubnub_sync.lib $(LIBS) + pubnub_crypto_sync_sample.exe: ..\core\samples\pubnub_crypto_sync_sample.c pubnub_sync.lib $(CC) $(CFLAGS) $(INCLUDES) ..\core\samples\pubnub_crypto_sync_sample.c pubnub_sync.lib $(LIBS) diff --git a/posix/posix.mk b/posix/posix.mk index cd4f24ed..36d6c90d 100644 --- a/posix/posix.mk +++ b/posix/posix.mk @@ -1,6 +1,6 @@ -SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_coreapi_ex.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_sockets.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../core/pubnub_helper.c pubnub_version_posix.c pubnub_generate_uuid_posix.c pbpal_posix_blocking_io.c ../core/pubnub_generate_uuid_v3_md5.c ../core/pubnub_free_with_timeout_std.c msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c +SOURCEFILES = ../core/pubnub_pubsubapi.c ../core/pubnub_coreapi.c ../core/pubnub_coreapi_ex.c ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pubnub_netcore.c ../lib/sockets/pbpal_sockets.c ../lib/sockets/pbpal_resolv_and_connect_sockets.c ../core/pubnub_alloc_std.c ../core/pubnub_assert_std.c ../core/pubnub_generate_uuid.c ../core/pubnub_blocking_io.c ../posix/posix_socket_blocking_io.c ../core/pubnub_timers.c ../core/pubnub_json_parse.c ../lib/md5/md5.c ../lib/base64/pbbase64.c ../lib/pb_strnlen_s.c ../core/pubnub_helper.c pubnub_version_posix.c pubnub_generate_uuid_posix.c pbpal_posix_blocking_io.c ../core/pubnub_generate_uuid_v3_md5.c ../core/pubnub_free_with_timeout_std.c msstopwatch_monotonic_clock.c ../core/pubnub_url_encode.c -OBJFILES = pubnub_pubsubapi.o pubnub_coreapi.o pubnub_coreapi_ex.o pubnub_ccore_pubsub.o pubnub_ccore.o pubnub_netcore.o pbpal_sockets.o pbpal_resolv_and_connect_sockets.o pubnub_alloc_std.o pubnub_assert_std.o pubnub_generate_uuid.o pubnub_blocking_io.o posix_socket_blocking_io.o pubnub_timers.o pubnub_json_parse.o md5.o pbbase64.o pubnub_helper.o pubnub_version_posix.o pubnub_generate_uuid_posix.o pbpal_posix_blocking_io.o pubnub_generate_uuid_v3_md5.o pubnub_free_with_timeout_std.o msstopwatch_monotonic_clock.o pubnub_url_encode.o +OBJFILES = pubnub_pubsubapi.o pubnub_coreapi.o pubnub_coreapi_ex.o pubnub_ccore_pubsub.o pubnub_ccore.o pubnub_netcore.o pbpal_sockets.o pbpal_resolv_and_connect_sockets.o pubnub_alloc_std.o pubnub_assert_std.o pubnub_generate_uuid.o pubnub_blocking_io.o posix_socket_blocking_io.o pubnub_timers.o pubnub_json_parse.o md5.o pbbase64.o pb_strnlen_s.o pubnub_helper.o pubnub_version_posix.o pubnub_generate_uuid_posix.o pbpal_posix_blocking_io.o pubnub_generate_uuid_v3_md5.o pubnub_free_with_timeout_std.o msstopwatch_monotonic_clock.o pubnub_url_encode.o ifndef ONLY_PUBSUB_API ONLY_PUBSUB_API = 0 @@ -26,6 +26,9 @@ ifndef USE_ADVANCED_HISTORY USE_ADVANCED_HISTORY = 1 endif +ifndef USE_OBJECTS_API +USE_OBJECTS_API = 1 +endif ifeq ($(USE_PROXY), 1) SOURCEFILES += ../core/pubnub_proxy.c ../core/pubnub_proxy_core.c ../core/pbhttp_digest.c ../core/pbntlm_core.c ../core/pbntlm_packer_std.c @@ -43,8 +46,8 @@ OBJFILES += miniz_tinfl.o pbgzip_decompress.o endif ifeq ($(USE_SUBSCRIBE_V2), 1) -SOURCEFILES += ../core/pubnub_subscribe_v2.c -OBJFILES += pubnub_subscribe_v2.o +SOURCEFILES += ../core/pbcc_subscribe_v2.c ../core/pubnub_subscribe_v2.c +OBJFILES += pbcc_subscribe_v2.o pubnub_subscribe_v2.o endif ifeq ($(USE_ADVANCED_HISTORY), 1) @@ -52,6 +55,11 @@ SOURCEFILES += ../core/pbcc_advanced_history.c ../core/pubnub_advanced_history.c OBJFILES += pbcc_advanced_history.o pubnub_advanced_history.o endif +ifeq ($(USE_OBJECTS_API), 1) +SOURCEFILES += ../core/pbcc_objects_api.c ../core/pubnub_objects_api.c +OBJFILES += pbcc_objects_api.o pubnub_objects_api.o +endif + OS := $(shell uname) ifeq ($(OS),Darwin) SOURCEFILES += monotonic_clock_get_time_darwin.c @@ -63,7 +71,7 @@ OBJFILES += monotonic_clock_get_time_posix.o LDLIBS=-lrt -lpthread endif -CFLAGS =-g -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) +CFLAGS =-g -Wall -D PUBNUB_THREADSAFE -D PUBNUB_LOG_LEVEL=PUBNUB_LOG_LEVEL_WARNING -D PUBNUB_ONLY_PUBSUB_API=$(ONLY_PUBSUB_API) -D PUBNUB_PROXY_API=$(USE_PROXY) -D PUBNUB_USE_GZIP_COMPRESSION=$(USE_GZIP_COMPRESSION) -D PUBNUB_RECEIVE_GZIP_RESPONSE=$(RECEIVE_GZIP_RESPONSE) -D PUBNUB_USE_SUBSCRIBE_V2=$(USE_SUBSCRIBE_V2) -D PUBNUB_USE_OBJECTS_API=$(USE_OBJECTS_API) # -g enables debugging, remove to get a smaller executable # -fsanitize-address Use AddressSanitizer diff --git a/posix/pubnub_config.h b/posix/pubnub_config.h index 63a70e1f..9b59e9f0 100644 --- a/posix/pubnub_config.h +++ b/posix/pubnub_config.h @@ -174,5 +174,13 @@ #define PUBNUB_USE_ADVANCED_HISTORY 1 #endif +#if !defined(PUBNUB_USE_OBJECTS_API) +/** If true (!=0) will enable using the objects API, which is a + collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) + on two new pubnub objects: User and Space, as well as manipulating connections + between them. */ +#define PUBNUB_USE_OBJECTS_API 1 +#endif + #endif /* !defined INC_PUBNUB_CONFIG */ diff --git a/posix/pubnub_ntf_callback_posix.c b/posix/pubnub_ntf_callback_posix.c index 6e4d6012..0af58782 100644 --- a/posix/pubnub_ntf_callback_posix.c +++ b/posix/pubnub_ntf_callback_posix.c @@ -21,8 +21,10 @@ struct SocketWatcherData { struct pbpal_poll_data* poll pubnub_guarded_by(mutw); + bool stop_socket_watcher_thread pubnub_guarded_by(stoplock); pthread_mutex_t mutw; pthread_mutex_t timerlock; + pthread_mutex_t stoplock; pthread_t thread_id; #if PUBNUB_TIMERS_API pubnub_t* timer_head pubnub_guarded_by(timerlock); @@ -62,7 +64,15 @@ void* socket_watcher_thread(void* arg) for (;;) { struct timespec timspec; - + bool stop_thread; + + pthread_mutex_lock(&m_watcher.stoplock); + stop_thread = m_watcher.stop_socket_watcher_thread; + pthread_mutex_unlock(&m_watcher.stoplock); + if (stop_thread) { + break; + } + pbpal_ntf_callback_process_queue(&m_watcher.queue); monotonic_clock_get_time(&timspec); @@ -95,6 +105,14 @@ void* socket_watcher_thread(void* arg) } +void pubnub_stop(void) +{ + pthread_mutex_lock(&m_watcher.stoplock); + m_watcher.stop_socket_watcher_thread = true; + pthread_mutex_unlock(&m_watcher.stoplock); +} + + int pbntf_init(void) { int rslt; @@ -113,10 +131,17 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); return -1; } + rslt = pthread_mutex_init(&m_watcher.stoplock, &attr); + if (rslt != 0) { + PUBNUB_LOG_ERROR("Failed to initialize 'stoplock' mutex, error code: %d", rslt); + pthread_mutexattr_destroy(&attr); + return -1; + } rslt = pthread_mutex_init(&m_watcher.mutw, &attr); if (rslt != 0) { PUBNUB_LOG_ERROR("Failed to initialize mutex, error code: %d", rslt); pthread_mutexattr_destroy(&attr); + pthread_mutex_destroy(&m_watcher.stoplock); return -1; } rslt = pthread_mutex_init(&m_watcher.timerlock, &attr); @@ -124,6 +149,7 @@ int pbntf_init(void) PUBNUB_LOG_ERROR("Failed to initialize mutex for timers, error code: %d", rslt); pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); + pthread_mutex_destroy(&m_watcher.stoplock); return -1; } @@ -132,9 +158,11 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); pthread_mutex_destroy(&m_watcher.timerlock); + pthread_mutex_destroy(&m_watcher.stoplock); return -1; } pbpal_ntf_callback_queue_init(&m_watcher.queue); + m_watcher.stop_socket_watcher_thread = false; #if defined(PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB) \ && (PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB > 0) @@ -148,7 +176,7 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); pthread_mutex_destroy(&m_watcher.timerlock); - pthread_mutex_destroy(&m_watcher.queue_lock); + pthread_mutex_destroy(&m_watcher.stoplock); pbpal_ntf_callback_queue_deinit(&m_watcher.queue); pbpal_ntf_callback_poller_deinit(&m_watcher.poll); return -1; @@ -163,7 +191,7 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); pthread_mutex_destroy(&m_watcher.timerlock); - pthread_mutex_destroy(&m_watcher.queue_lock); + pthread_mutex_destroy(&m_watcher.stoplock); pthread_attr_destroy(&thread_attr); pbpal_ntf_callback_queue_deinit(&m_watcher.queue); pbpal_ntf_callback_poller_deinit(&m_watcher.poll); @@ -177,7 +205,7 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); pthread_mutex_destroy(&m_watcher.timerlock); - pthread_mutex_destroy(&m_watcher.queue_lock); + pthread_mutex_destroy(&m_watcher.stoplock); pthread_attr_destroy(&thread_attr); pbpal_ntf_callback_queue_deinit(&m_watcher.queue); pbpal_ntf_callback_poller_deinit(&m_watcher.poll); @@ -193,6 +221,7 @@ int pbntf_init(void) pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&m_watcher.mutw); pthread_mutex_destroy(&m_watcher.timerlock); + pthread_mutex_destroy(&m_watcher.stoplock); pbpal_ntf_callback_queue_deinit(&m_watcher.queue); pbpal_ntf_callback_poller_deinit(&m_watcher.poll); return -1; diff --git a/qt/fn_test.pro b/qt/fn_test.pro index 7cf36738..a8369919 100644 --- a/qt/fn_test.pro +++ b/qt/fn_test.pro @@ -4,7 +4,7 @@ mac:CONFIG -= app_bundle win32:CONFIG += console CONFIG += c++11 HEADERS += pubnub_qt.h pubnub.hpp -SOURCES += pubnub_qt.cpp pubnub.cpp fntest/pubnub_fntest_runner.cpp ../cpp/fntest/pubnub_fntest.cpp ../cpp/fntest/pubnub_fntest_basic.cpp ../cpp/fntest/pubnub_fntest_medium.cpp ../core/pubnub_ccore.c ../core/pubnub_ccore_pubsub.c ../core/pbcc_advanced_history.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../lib/pbcrc32.c +SOURCES += pubnub_qt.cpp pubnub.cpp fntest/pubnub_fntest_runner.cpp ../cpp/fntest/pubnub_fntest.cpp ../cpp/fntest/pubnub_fntest_basic.cpp ../cpp/fntest/pubnub_fntest_medium.cpp ../core/pubnub_ccore.c ../core/pubnub_ccore_pubsub.c ../core/pbcc_subscribe_v2.c ../core/pbcc_advanced_history.c ../core/pbcc_objects_api.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../lib/pbcrc32.c ../lib/pb_strnlen_s.c win32:SOURCES += ../core/c99/snprintf.c INCLUDEPATH += ../core ../cpp/fntest .. diff --git a/qt/pubnub.pro b/qt/pubnub.pro index 1f3a4384..6c447449 100644 --- a/qt/pubnub.pro +++ b/qt/pubnub.pro @@ -4,7 +4,7 @@ CONFIG += C++11 mac:CONFIG -= app_bundle win32:CONFIG += console HEADERS += pubnub_qt.h pubnub_qt_sample.h -SOURCES += pubnub_qt.cpp pubnub_qt_sample.cpp ../core/pubnub_ccore.c ../core/pubnub_ccore_pubsub.c ../core/pbcc_advanced_history.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../lib/pbcrc32.c +SOURCES += pubnub_qt.cpp pubnub_qt_sample.cpp ../core/pubnub_ccore.c ../core/pubnub_ccore_pubsub.c ../core/pbcc_subscribe_v2.c ../core/pbcc_advanced_history.c ../core/pbcc_objects_api.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../lib/pbcrc32.c ../lib/pb_strnlen_s.c win32:SOURCES += ../core/c99/snprintf.c INCLUDEPATH += .. diff --git a/qt/pubnub_config.h b/qt/pubnub_config.h index 8d1c7bb4..503b90d0 100644 --- a/qt/pubnub_config.h +++ b/qt/pubnub_config.h @@ -40,12 +40,30 @@ /** The maximum channel name length */ #define PUBNUB_MAX_CHANNEL_NAME_LENGTH 92 +/** Minimal presence heartbeat interval supported by + Pubnub, in seconds. +*/ +#define PUBNUB_MINIMAL_HEARTBEAT_INTERVAL 270 + +#if !defined(PUBNUB_USE_SUBSCRIBE_V2) +/** If true (!=0) will enable using the subscribe v2 API, which + provides filter expressions and more data about messages. */ +#define PUBNUB_USE_SUBSCRIBE_V2 1 +#endif + #if !defined(PUBNUB_USE_ADVANCED_HISTORY) /** If true (!=0) will enable using the advanced history API, which provides more data about (unread) messages. */ #define PUBNUB_USE_ADVANCED_HISTORY 1 #endif +#if !defined(PUBNUB_USE_OBJECTS_API) +/** If true (!=0) will enable using the objects API, which is a + collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) + on two new pubnub objects: User and Space, as well as manipulating connections + between them. */ +#define PUBNUB_USE_OBJECTS_API 1 +#endif /** Mininmal duration of the transaction timer, in milliseconds. You * can't set less than this. diff --git a/qt/pubnub_gui.pro b/qt/pubnub_gui.pro index 9641200a..d329ef0b 100644 --- a/qt/pubnub_gui.pro +++ b/qt/pubnub_gui.pro @@ -2,7 +2,7 @@ TEMPLATE = app QT += widgets network CONFIG += C++11 HEADERS += pubnub_qt.h pubnub_qt_gui_sample.h -SOURCES += pubnub_qt.cpp pubnub_qt_gui_sample.cpp ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pbcc_advanced_history.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c +SOURCES += pubnub_qt.cpp pubnub_qt_gui_sample.cpp ../core/pubnub_ccore_pubsub.c ../core/pubnub_ccore.c ../core/pbcc_subscribe_v2.c ../core/pbcc_advanced_history.c ../core/pbcc_objects_api.c ../core/pubnub_url_encode.c ../core/pubnub_assert_std.c ../core/pubnub_json_parse.c ../core/pubnub_helper.c ../lib/pb_strnlen_s.c win32:SOURCES += ../core/c99/snprintf.c INCLUDEPATH += .. diff --git a/qt/pubnub_qt.cpp b/qt/pubnub_qt.cpp index d1eb6152..d7e6d7fc 100644 --- a/qt/pubnub_qt.cpp +++ b/qt/pubnub_qt.cpp @@ -8,6 +8,7 @@ extern "C" { #include "lib/pbcrc32.c" #include "core/pubnub_memory_block.h" #include "core/pubnub_advanced_history.h" +#include "core/pbcc_objects_api.h" #define MAX_ERROR_MESSAGE_LENGTH 100 } @@ -113,26 +114,47 @@ pubnub_res pubnub_qt::startRequest(pubnub_res result, pubnub_trans transaction) if (!d_use_http_keep_alive) { req.setRawHeader("Connection", "Close"); } - if (PBTT_PUBLISH == transaction) { - switch (d_publish_method) { - case pubnubPublishViaPOSTwithGZIP: + switch (transaction) { + case PBTT_PUBLISH: + case PBTT_SIGNAL: +#if PUBNUB_USE_OBJECTS_API + case PBTT_CREATE_USER: + case PBTT_UPDATE_USER: + case PBTT_DELETE_USER: + case PBTT_CREATE_SPACE: + case PBTT_UPDATE_SPACE: + case PBTT_DELETE_SPACE: + case PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS: + case PBTT_UPDATE_MEMBERS_IN_SPACE: +#endif /* PUBNUB_USE_OBJECTS_API */ + switch (d_method) { + case pubnubSendViaPOSTwithGZIP: + case pubnubUsePATCHwithGZIP: req.setRawHeader("Content-Encoding", "gzip"); /* FALLTHRU */ - case pubnubPublishViaPOST: + case pubnubSendViaPOST: + case pubnubUsePATCH: req.setRawHeader("Content-Type", "application/json"); req.setRawHeader("Content-Length", - QByteArray::number(d_message_to_publish.size())); - d_reply.reset(d_qnam.post(req, d_message_to_publish)); + QByteArray::number(d_message_to_send.size())); + d_reply.reset(((pubnubSendViaPOST == d_method) || + (pubnubSendViaPOSTwithGZIP == d_method)) + ? d_qnam.post(req, d_message_to_send) + : d_qnam.put(req, d_message_to_send)); break; - case pubnubPublishViaGET: + case pubnubSendViaGET: d_reply.reset(d_qnam.get(req)); break; + case pubnubUseDELETE: + d_reply.reset(d_qnam.deleteResource(req)); + break; default: break; } - } - else { + break; + default: d_reply.reset(d_qnam.get(req)); + break; } connect(d_reply.data(), SIGNAL(finished()), this, SLOT(httpFinished())); d_transactionTimer->start(d_transaction_timeout_duration_ms); @@ -181,6 +203,28 @@ QStringList pubnub_qt::get_all() const } +#if PUBNUB_USE_SUBSCRIBE_V2 +v2_message pubnub_qt::get_v2() const +{ + KEEP_THREAD_SAFE(); + return v2_message(pbcc_get_msg_v2(d_context.data())); +} + + +QVector pubnub_qt::get_all_v2() const +{ + QVector all; + v2_message msg = get_v2(); + + while (!msg.is_empty()) { + all.append(msg); + msg = get_v2(); + } + return all; +} +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + + QString pubnub_qt::get_channel() const { KEEP_THREAD_SAFE(); @@ -214,14 +258,14 @@ void pubnub_qt::cancel() pubnub_res pubnub_qt::publish(QString const& channel, QString const& message) { QMutexLocker lk(&d_mutex); - d_publish_method = pubnubPublishViaGET; + d_method = pubnubSendViaGET; return startRequest(pbcc_publish_prep(d_context.data(), channel.toLatin1().data(), message.toLatin1().data(), true, false, NULL, - d_publish_method), + d_method), PBTT_PUBLISH); } @@ -230,15 +274,15 @@ pubnub_res pubnub_qt::publish_via_post(QString const& channel, QByteArray const& message) { QMutexLocker lk(&d_mutex); - d_publish_method = pubnubPublishViaPOST; - d_message_to_publish = message; + d_method = pubnubSendViaPOST; + d_message_to_send = message; return startRequest(pbcc_publish_prep(d_context.data(), channel.toLatin1().data(), - d_message_to_publish.data(), + d_message_to_send.data(), true, false, NULL, - d_publish_method), + d_method), PBTT_PUBLISH); } @@ -280,20 +324,43 @@ pubnub_res pubnub_qt::publish_via_post_with_gzip(QString const& channel, QByteArray const& message) { QMutexLocker lk(&d_mutex); - d_message_to_publish = pack_message_to_gzip(message); - d_publish_method = (d_message_to_publish.size() != message.size()) - ? pubnubPublishViaPOSTwithGZIP - : pubnubPublishViaPOST; + d_message_to_send = pack_message_to_gzip(message); + d_method = (d_message_to_send.size() != message.size()) + ? pubnubSendViaPOSTwithGZIP + : pubnubSendViaPOST; return startRequest(pbcc_publish_prep(d_context.data(), channel.toLatin1().data(), - d_message_to_publish.data(), + d_message_to_send.data(), true, false, NULL, - d_publish_method), + d_method), PBTT_PUBLISH); } + +pubnub_res pubnub_qt::signal(QString const& channel, + QByteArray const& message, + pubnub_method method) +{ + QMutexLocker lk(&d_mutex); + if (pubnubSendViaGET == method) { + d_method = method; + } + else { + d_message_to_send = message; + d_method = pubnubSendViaPOST; + } + return startRequest(pbcc_signal_prep(d_context.data(), + channel.toLatin1().data(), + d_method, + (pubnubSendViaGET == method) + ? message.data() + : d_message_to_send.data()), + PBTT_SIGNAL); +} + + pubnub_res pubnub_qt::subscribe(QString const& channel, QString const& channel_group) { KEEP_THREAD_SAFE(); @@ -307,6 +374,20 @@ pubnub_res pubnub_qt::subscribe(QString const& channel, QString const& channel_g } +pubnub_res pubnub_qt::subscribe_v2(QString const& channel, subscribe_v2_options opt) +{ + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_subscribe_v2_prep( + d_context.data(), + channel.isEmpty() ? 0 : channel.toLatin1().data(), + opt.get_chgroup(), + opt.get_heartbeat(), + opt.get_filter_expr()), + PBTT_SUBSCRIBE_V2); +} + + pubnub_res pubnub_qt::leave(QString const& channel, QString const& channel_group) { KEEP_THREAD_SAFE(); @@ -573,9 +654,234 @@ pubnub_res pubnub_qt::list_channel_group(QString const& channel_group) PBTT_LIST_CHANNEL_GROUP); } +#if PUBNUB_USE_OBJECTS_API +pubnub_res pubnub_qt::fetch_all_users(list_options& options) +{ + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_all_users_prep(d_context.data(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + options.start().isEmpty() ? 0 : options.start().toLatin1().data(), + options.end().isEmpty() ? 0 : options.end().toLatin1().data(), + options.count()), + PBTT_FETCH_ALL_USERS); +} + + +pubnub_res pubnub_qt::create_user(QByteArray const& user_obj, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(user_obj); + d_method = (d_message_to_send.size() != user_obj.size()) + ? pubnubSendViaPOSTwithGZIP + : pubnubSendViaPOST; + return startRequest( + pbcc_create_user_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_CREATE_USER); +} + + +pubnub_res pubnub_qt::fetch_user(QString const& user_id, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_user_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + user_id.toLatin1().data()), + PBTT_FETCH_USER); +} + + +pubnub_res pubnub_qt::update_user(QByteArray const& user_obj, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(user_obj); + d_method = (d_message_to_send.size() != user_obj.size()) + ? pubnubUsePATCHwithGZIP + : pubnubUsePATCH; + return startRequest( + pbcc_update_user_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_UPDATE_USER); +} + + +pubnub_res pubnub_qt::delete_user(QString const& user_id) +{ + KEEP_THREAD_SAFE(); + d_method = pubnubUseDELETE; + return startRequest( + pbcc_delete_user_prep(d_context.data(), + user_id.toLatin1().data()), + PBTT_DELETE_USER); +} + + +pubnub_res pubnub_qt::fetch_all_spaces(list_options& options) +{ + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_all_spaces_prep(d_context.data(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + options.start().isEmpty() ? 0 : options.start().toLatin1().data(), + options.end().isEmpty() ? 0 : options.end().toLatin1().data(), + options.count()), + PBTT_FETCH_ALL_SPACES); +} + + +pubnub_res pubnub_qt::create_space(QByteArray const& space_obj, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(space_obj); + d_method = (d_message_to_send.size() != space_obj.size()) + ? pubnubSendViaPOSTwithGZIP + : pubnubSendViaPOST; + return startRequest( + pbcc_create_space_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_CREATE_SPACE); +} + + +pubnub_res pubnub_qt::fetch_space(QString const& space_id, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_space_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + space_id.toLatin1().data()), + PBTT_FETCH_SPACE); +} + + +pubnub_res pubnub_qt::update_space(QByteArray const& space_obj, QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(space_obj); + d_method = (d_message_to_send.size() != space_obj.size()) + ? pubnubUsePATCHwithGZIP + : pubnubUsePATCH; + return startRequest( + pbcc_update_space_prep(d_context.data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_UPDATE_SPACE); +} + + +pubnub_res pubnub_qt::delete_space(QString const& space_id) +{ + KEEP_THREAD_SAFE(); + d_method = pubnubUseDELETE; + return startRequest( + pbcc_delete_space_prep(d_context.data(), + space_id.toLatin1().data()), + PBTT_DELETE_SPACE); +} + + +pubnub_res pubnub_qt::fetch_users_space_memberships(QString const& user_id, + list_options& options) +{ + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_users_space_memberships_prep( + d_context.data(), + user_id.toLatin1().data(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + options.start().isEmpty() ? 0 : options.start().toLatin1().data(), + options.end().isEmpty() ? 0 : options.end().toLatin1().data(), + options.count()), + PBTT_FETCH_USERS_SPACE_MEMBERSHIPS); +} + + +pubnub_res pubnub_qt::update_users_space_memberships(QString const& user_id, + QByteArray const& update_obj, + QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(update_obj); + d_method = (d_message_to_send.size() != update_obj.size()) + ? pubnubUsePATCHwithGZIP + : pubnubUsePATCH; + return startRequest( + pbcc_update_users_space_memberships_prep( + d_context.data(), + user_id.toLatin1().data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS); +} + + +pubnub_res pubnub_qt::fetch_members_in_space(QString const& space_id, + list_options& options) +{ + KEEP_THREAD_SAFE(); + return startRequest( + pbcc_fetch_members_in_space_prep( + d_context.data(), + space_id.toLatin1().data(), + options.include_c_strings_array(), + options.include_count(), + options.limit(), + options.start().isEmpty() ? 0 : options.start().toLatin1().data(), + options.end().isEmpty() ? 0 : options.end().toLatin1().data(), + options.count()), + PBTT_FETCH_MEMBERS_IN_SPACE); +} + + +pubnub_res pubnub_qt::update_members_in_space(QString const& space_id, + QByteArray const& update_obj, + QStringList& include) +{ + include_options inc(include); + KEEP_THREAD_SAFE(); + d_message_to_send = pack_message_to_gzip(update_obj); + d_method = (d_message_to_send.size() != update_obj.size()) + ? pubnubUsePATCHwithGZIP + : pubnubUsePATCH; + return startRequest( + pbcc_update_members_in_space_prep( + d_context.data(), + space_id.toLatin1().data(), + inc.include_c_strings_array(), + inc.include_count(), + d_message_to_send.data()), + PBTT_UPDATE_MEMBERS_IN_SPACE); +} +#endif /* PUBNUB_USE_OBJECTS_API */ int pubnub_qt::last_http_code() const { + KEEP_THREAD_SAFE(); return d_http_code; } @@ -716,10 +1022,33 @@ pubnub_res pubnub_qt::finish(QByteArray const& data, int http_code) case PBTT_LIST_CHANNEL_GROUP: pbres = pbcc_parse_channel_registry_response(d_context.data()); break; +#if PUBNUB_USE_SUBSCRIBE_V2 + case PBTT_SUBSCRIBE_V2: + pbres = pbcc_parse_subscribe_v2_response(d_context.data()); + break; +#endif #if PUBNUB_USE_ADVANCED_HISTORY case PBTT_MESSAGE_COUNTS: pbres = pbcc_parse_message_counts_response(d_context.data()); break; +#endif +#if PUBNUB_USE_OBJECTS_API + case PBTT_FETCH_ALL_USERS: + case PBTT_CREATE_USER: + case PBTT_FETCH_USER: + case PBTT_UPDATE_USER: + case PBTT_DELETE_USER: + case PBTT_FETCH_ALL_SPACES: + case PBTT_CREATE_SPACE: + case PBTT_FETCH_SPACE: + case PBTT_UPDATE_SPACE: + case PBTT_DELETE_SPACE: + case PBTT_FETCH_USERS_SPACE_MEMBERSHIPS: + case PBTT_UPDATE_USERS_SPACE_MEMBERSHIPS: + case PBTT_FETCH_MEMBERS_IN_SPACE: + case PBTT_UPDATE_MEMBERS_IN_SPACE: + pbres = pbcc_parse_objects_api_response(d_context.data()); + break; #endif default: break; @@ -782,6 +1111,8 @@ void pubnub_qt::httpFinished() case QNetworkReply::ProtocolUnknownError: emit outcome(PNR_CONNECT_FAILED); return; + default: + break; } } diff --git a/qt/pubnub_qt.h b/qt/pubnub_qt.h index 24f82bb3..e2798ef3 100644 --- a/qt/pubnub_qt.h +++ b/qt/pubnub_qt.h @@ -2,6 +2,8 @@ #if !defined INC_PUBNUB_QT #define INC_PUBNUB_QT +#include + #include #include #include @@ -16,16 +18,161 @@ extern "C" { #include "core/pubnub_api_types.h" #include "core/pubnub_helper.h" +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "core/pbcc_subscribe_v2.h" +#endif } +#include "cpp/tribool.hpp" +#if PUBNUB_USE_SUBSCRIBE_V2 +#include "cpp/pubnub_v2_message.hpp" +#endif + QT_BEGIN_NAMESPACE class QNetworkReply; class QSslError; QT_END_NAMESPACE +#define MAX_INCLUDE_DIMENSION 100 +#define MAX_ELEM_LENGTH 30 + +#if PUBNUB_USE_OBJECTS_API +/** A wrapper class for objects api managing include parameter */ +class include_options { + char d_include_c_strings_array[MAX_INCLUDE_DIMENSION][MAX_ELEM_LENGTH + 1]; + size_t d_include_count; + +public: + include_options() + : d_include_count(0) + {} + const char** include_to_c_strings_array(QStringList const& inc) + { + size_t n = inc.size(); + unsigned i; + if (n > MAX_INCLUDE_DIMENSION) { + throw std::range_error("include parameter has too many elements."); + } + for (i = 0; i < n; i++) { + if (inc[i].size() > MAX_ELEM_LENGTH) { + throw std::range_error("include string element is too long."); + } + strcpy(d_include_c_strings_array[i], inc[i].toLatin1().data()); + } + d_include_count = n; + return (const char**)d_include_c_strings_array; + } + include_options(QStringList const& inc) + { + include_to_c_strings_array(inc); + } + size_t include_count() { return d_include_count; } + const char** include_c_strings_array() + { + return (const char**)d_include_c_strings_array; + } +}; + +/** A wrapper class for objects api options for manipulating specified requirements + and paged response, enabling a nicer usage. Something like: + pbp.fetch_users(list_options().start(last_bookmark)); + + instead of: + pbp.fetch_users(nullopt, nullopt, last_bookmark, “”, nullopt); + */ +using namespace pubnub; +class list_options : public include_options { + size_t d_limit; + QString d_start; + QString d_end; + tribool d_count; + +public: + list_options() + : d_limit(0) + , d_count(tribool::not_set) + {} + list_options& limit(size_t lim) + { + d_limit = lim; + return *this; + } + size_t limit() { return d_limit; } + list_options& start(QString const& st) + { + d_start = st; + return *this; + } + QString start() { return d_start; } + list_options& end(QString const& e) + { + d_end = e; + return *this; + } + QString end() { return d_end; } + list_options& count(tribool co) + { + d_count = co; + return *this; + } + pubnub_tribool count() + { + if (false == d_count) { + return pbccFalse; + } + else if (true == d_count) { + return pbccTrue; + } + return pbccNotSet; + } +}; +#endif /* PUBNUB_USE_OBJECTS_API */ struct pbcc_context; + +#if PUBNUB_USE_SUBSCRIBE_V2 +/** A wrapper class for subscribe_v2 options, enabling a nicer + usage. Something like: + + pn.subscribe_v2(chan, subscribe_v2_options().heartbeat(412)); +*/ +class subscribe_v2_options { + unsigned d_heartbeat; + std::string d_chgrp; + std::string d_filter_expr; + +public: + subscribe_v2_options() : d_heartbeat(PUBNUB_MINIMAL_HEARTBEAT_INTERVAL) {} + subscribe_v2_options& channel_group(QString const& chgroup) + { + d_chgrp = chgroup.toStdString(); + return *this; + } + subscribe_v2_options& channel_group(QStringList const& chgroup) + { + return channel_group(chgroup.join(",")); + } + subscribe_v2_options& heartbeat(unsigned hb_interval) + { + d_heartbeat = hb_interval; + return *this; + } + subscribe_v2_options& filter_expr(QString const& filter_exp) + { + d_filter_expr = filter_exp.toStdString(); + return *this; + } + unsigned* get_heartbeat() { return &d_heartbeat; } + char const* get_chgroup() { return d_chgrp.empty() ? 0 : d_chgrp.c_str(); } + char const* get_filter_expr() + { + return d_filter_expr.empty() ? 0 : d_filter_expr.c_str(); + } +}; +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + + /** @mainpage Pubnub C-core for Qt This is the C-core implementation of the Pubnub client @@ -129,9 +276,9 @@ class pubnub_qt : public QObject return d_origin; } - /** Returns the string of an arrived message or other element of the + /** Returns the string of an arrived message, or other element of the response to an operation/transaction. Message(s) arrive on finish - of a subscribe operation, while for other operations this will give + of a subscribe operation, while for 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. @@ -148,9 +295,29 @@ class pubnub_qt : public QObject /** Returns all (remaining) messages from a context */ QStringList get_all() const; +#if PUBNUB_USE_SUBSCRIBE_V2 + /** Returns the v2 message object of an arrived message. Message(s) + arrive on finish of a subscribe_v2 operation. + 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 operation was for. + Whather or not message is empty can be checked through class member + function is_empty(). + @see pubnub_v2_message.hpp + + @note Context doesn't keep track of the channel(s) you subscribed(v2) + to. This is a memory saving design decision, as most users won't + change the channel(s) they subscribe_v2 too. + */ + v2_message get_v2() const; + /** Returns all (remaining) v2 messages from a context */ + QVector get_all_v2() const; +#endif + /** Returns a string of a fetched subscribe operation/transaction's - next channel. Each transaction may hold a list of channels, and + 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). @@ -174,7 +341,7 @@ class pubnub_qt : public QObject @p p context. This actually means "initiate a publish transaction". - You can't publish if a transaction is in progress in @p p context. + You can't publish if a transaction is in progress on @p p context. If transaction is not successful (@c PNR_PUBLISH_FAILED), you can get the string describing the reason for failure by calling @@ -190,8 +357,7 @@ class pubnub_qt : public QObject will not be @c PNR_OK (but you will still be able to get the result code and the description). - @param channel The string with the channel (or comma-delimited list - of channels) to publish to. + @param channel The string with the channel to publish to. @param message The message to publish, expected to be in JSON format @return #PNR_STARTED on success, an error otherwise @@ -243,6 +409,45 @@ class pubnub_qt : public QObject return publish_via_post_with_gzip(channel, message.toJson()); } + /** Sends a signal @p message (in JSON format) on @p channel via chosen + @p method(GET, or POST). This actually means "initiate a signal transaction". + It has similar behaviour as publish, but unlike publish transaction, signal + erases previous signal message on server(, on a given channel,) and you + can not send any metadata. + There can be only up to one signal message at the time. If it's not renewed + by another signal, signal message disappears from channel history after + certain amount of time. + Signal message is much shorter and its maximum length( around 500 bytes) + is smaller than full publish message. + If gzip compression is 'activated'(linked) SDK will try to gzip-commpress + signal message before sending it. + + You can't 'signal' if a transaction is in progress on @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 signal 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 channel The string with the channel to signal to. + @param message The signal message to send, expected to be in JSON format + @param method The chosen performing method(GET, or POST) for the transaction + (Method GET by default). + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res signal(QString const &channel, + QByteArray const &message, + pubnub_method method=pubnubSendViaGET); + /** 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 @@ -290,6 +495,34 @@ class pubnub_qt : public QObject return subscribe(channel.join(","), channel_group.join(",")); } +#if PUBNUB_USE_SUBSCRIBE_V2 + /** The V2 subscribe. To get messages for subscribe V2, use pb.get_v2(). + - keep in mind that it can provide you with channel and channel group info. + + Basic usage initiating transaction: + + subscribe_v2_options opt; + pubnub_qt pb; + pbresult = pb.subscribe_v2(pn, "my_channel", opt.filter_expr("'key' == value")); + ... + + @param channel The string with the channel name (or comma-delimited list of + channel names) to subscribe for. + @param opt Subscribe V2 options + @return #PNR_STARTED on success, an error otherwise + + @see get_v2 + */ + pubnub_res subscribe_v2(QString const &channel, subscribe_v2_options opt); + + /** A helper method to subscribe_v2 to several channels and/or channel groups + * by giving a (string) list of channels and passing suitable options. + */ + pubnub_res subscribe_v2(QStringList const &channel, subscribe_v2_options opt) { + return subscribe_v2(channel.join(","), opt); + } +#endif /* PUBNUB_USE_SUBSCRIBE_V2 */ + /** 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 @@ -758,6 +991,470 @@ class pubnub_qt : public QObject */ pubnub_res list_channel_group(QString const& channel_group); +#if PUBNUB_USE_OBJECTS_API + /** Initiates a transaction that returns a paginated list of users + associated with the subscription key, optionally including each + record's custom data object. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param options options for manipulating specified requirements + and paginated response + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_all_users(list_options& options); + + /** Initiates a transaction for creating a user with the attributes specified in + @p user_obj. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the created user object, optionally including the user's custom data object. + + @note User ID and name are required properties in the @p user_obj + @param user_obj The JSON string with the definition of the User + Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res create_user(QByteArray const& user_obj, QStringList& include); + + /** Initiates a transaction for creating a user with the attributes specified in + @p user_obj. + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that receives + byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the created user object, optionally including the user's custom data object. + + @note User ID and name are required properties in the @p user_obj + @param user_obj The JSON string with the definition of the User + Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res create_user(QJsonDocument const& user_obj, QStringList& include) { + return create_user(user_obj.toJson(), include); + } + + /** Initiates transaction that returns the user object specified with @p user_id, + optionally including the user's custom data object. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the created user object, optionally including the user's custom data object. + + @param user_id The User ID for which to retrieve the user object. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_user(QString const& user_id, QStringList& include); + + /** Initiates trnsaction that updates the user object specified with the `id` key + of the @p user_obj with any new information you provide. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the updated user object, optionally including the user's custom data object. + + @note User ID and name are required properties in the @p user_obj + @param user_obj The JSON string with the definition of the User + Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_user(QByteArray const& user_obj, QStringList& include); + + /** Initiates trnsaction that updates the user object specified with the `id` key + of the @p user_obj with any new information you provide. + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that receives + byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the updated user object, optionally including the user's custom data object. + + @note User ID and name are required properties in the @p user_obj + @param user_obj The JSON string with the definition of the User + Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_user(QJsonDocument const& user_obj, QStringList& include) { + return update_user(user_obj.toJson(), include); + } + + /** Initiates transaction that deletes the user specified with @p user_id. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param user_id The User ID. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res delete_user(QString const& user_id); + + /** Initiates transaction that returns the spaces associated with the subscriber key, + optionally including each space's custom data object. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param options options for manipulating specified requirements + and paginated response + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_all_spaces(list_options& options); + + /** Initiates transaction that creates a space with the attributes specified + in @p space_obj. + @note Space ID and name are required properties of @p space_obj + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the created space object, optionally including its custom data object. + + @param space_obj The JSON string with the definition of the Space Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res create_space(QByteArray const& space_obj, QStringList& include); + + /** Initiates transaction that creates a space with the attributes specified + in @p space_obj. + @note Space ID and name are required properties of @p space_obj + + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that + receives byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the created space object, optionally including its custom data object. + + @param space_obj The JSON string with the definition of the Space Object to create. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res create_space(QJsonDocument const& space_obj, QStringList& include) { + return create_space(space_obj.toJson(), include); + } + + /** Initiates transaction that returns the space object specified with @p space_id, + optionally including its custom data object. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param space_id The Space ID for which to retrieve the space object. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_space(QString const& space_id, QStringList& include); + + /** Initiates transaction that updates the space specified by the `id` property + of the @p space_obj. + @note Space ID and name are required properties of @p space_obj + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the space object, optionally including its custom data object. + + @param space_obj The JSON string with the description of the Space Object to update. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_space(QByteArray const& space_obj, QStringList& include); + + /** Initiates transaction that updates the space specified by the `id` property + of the @p space_obj. + @note Space ID and name are required properties of @p space_obj + + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that + receives byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the space object, optionally including its custom data object. + + @param space_obj The JSON string with the description of the Space Object to update. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_space(QJsonDocument const& space_obj, QStringList& include) { + return update_space(space_obj.toJson(), include); + } + + /** Initiates transaction that deletes the space specified with @p space_id. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param space_id The Space ID. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res delete_space(QString const& space_id); + + /** Initiates transaction that returns the space memberships of the user specified + by @p user_id, optionally including the custom data objects for... + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param user_id The User ID for which to retrieve the space memberships for. + @param options options for manipulating specified requirements + and paginated response + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_users_space_memberships(QString const& user_id, list_options& options); + + /** Initiates transaction that updates the space memberships of the user specified + by @p user_id. Use the `add`, `update`, and `remove` properties in the + @p update_obj to perform those operations on one, or more memberships. + An example for @update_obj: + { + "add": [ + { + "id": "my-channel" + } + ], + "update": [ + { + "id": "main", + "custom": { + "starred": true + } + } + ], + "remove": [ + { + "id": "space-0" + } + ] + } + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the user's space memberships, optionally including the custom data objects. + + @param user_id The User ID for which to update the space memberships for. + @param update_obj The JSON object that defines the updates to perform. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_users_space_memberships(QString const& user_id, + QByteArray const& update_obj, + QStringList& include); + + /** Initiates transaction that updates the space memberships of the user specified + by @p user_id. Use the `add`, `update`, and `remove` properties in the + @p update_obj to perform those operations on one, or more memberships. + An example for @update_obj: + { + "add": [ + { + "id": "my-channel" + } + ], + "update": [ + { + "id": "main", + "custom": { + "starred": true + } + } + ], + "remove": [ + { + "id": "space-0" + } + ] + } + + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that + receives byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the user's space memberships, optionally including the custom data objects. + + @param user_id The User ID for which to update the space memberships for. + @param update_obj The JSON object that defines the updates to perform. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_users_space_memberships(QString const& user_id, + QJsonDocument const& update_obj, + QStringList& include) { + return update_users_space_memberships(user_id, + update_obj.toJson(), + include); + } + + /** Initiates transaction that returns all users in the space specified by @p space_id, + optionally including the custom data objects for... + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + + @param space_id The Space ID for which to retrieve all users in the space. + @param options options for manipulating specified requirements + and paginated response + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res fetch_members_in_space(QString const& space_id, list_options& options); + + /** Initiates transaction that updates the list of members of the space specified by + @p space_id. Use the `add`, `update`, and `remove` properties in the @p update_obj + to perform those operations on one or more memberships. + An example for @update_obj: + { + "add": [ + { + "id": "user-1" + } + ], + "update": [ + { + "id": "user-2", + "custom": { + "role": “moderator” + } + } + ], + "remove": [ + { + "id": "user-0" + } + ] + } + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the spaces's memberships, optionally including the custom data objects for... + + @param space_id The Space ID for which to update the list of members in the space. + @param update_obj The JSON object that defines the updates to perform. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_members_in_space(QString const& space_id, + QByteArray const& update_obj, + QStringList& include); + + /** Initiates transaction that updates the list of members of the space specified by + @p space_id. Use the `add`, `update`, and `remove` properties in the @p update_obj + to perform those operations on one or more memberships. + An example for @update_obj: + { + "add": [ + { + "id": "user-1" + } + ], + "update": [ + { + "id": "user-2", + "custom": { + "role": “moderator” + } + } + ], + "remove": [ + { + "id": "user-0" + } + ] + } + + Function receives 'Qt Json' document. + Helpful if you're already using Qt support for Json in your code, ensuring message + you're sending is valid Json, unlike the case when applying the function that + receives byte array and doesn't check whether those bytes represent sound Json. + + If transaction is successful, the response(a JSON object) will + always have key: + - "status": with two possible values "ok" and "error" + Complete answer will be available via pubnub_get(). + Contains the spaces's memberships, optionally including the custom data objects for... + + @param space_id The Space ID for which to update the list of members in the space. + @param update_obj The JSON object that defines the updates to perform. + @param include list with additional/complex attributes to include in response. + Use empty list if you don't want to retrieve additional attributes. + @return #PNR_STARTED on success, an error otherwise + */ + pubnub_res update_members_in_space(QString const& space_id, + QJsonDocument const& update_obj, + QStringList& include) { + return update_members_in_space(space_id, + update_obj.toJson(), + include); + } +#endif /* PUBNUB_USE_OBJECTS_API */ + /** Returns the HTTP code of the last transaction. If the * transaction was succesfull, will return 0. */ @@ -888,11 +1585,11 @@ private slots: /// To Keep-Alive or not to Keep-Alive bool d_use_http_keep_alive; - /// publish transaction method - enum pubnub_publish_method d_publish_method; + /// transaction method(GET, POST, PATCH, or DELETE) + enum pubnub_method d_method; - /// Message to publish via POST method - QByteArray d_message_to_publish; + /// Message to send via POST method + QByteArray d_message_to_send; mutable QMutex d_mutex; }; diff --git a/qt/pubnub_qt_sample.cpp b/qt/pubnub_qt_sample.cpp index f6aa437d..5f85c547 100644 --- a/qt/pubnub_qt_sample.cpp +++ b/qt/pubnub_qt_sample.cpp @@ -144,7 +144,7 @@ void pubnub_qt_sample::execute() connect(&d_pb, SIGNAL(outcome(pubnub_res)), this, SLOT(onPublish(pubnub_res))); pubnub_res result = d_pb.publish(chann, "\"Hello world from Qt!\""); if (result != PNR_STARTED) { - d_out << "Subscribe failed, result: '"<< pubnub_res_2_string(result) << "'\n"; + d_out << "Publish failed, result: '"<< pubnub_res_2_string(result) << "'\n"; QCoreApplication::instance()->quit(); } } diff --git a/windows/pubnub_config.h b/windows/pubnub_config.h index 9ed33018..b215086e 100644 --- a/windows/pubnub_config.h +++ b/windows/pubnub_config.h @@ -179,5 +179,11 @@ provides more data about (unread) messages. */ #define PUBNUB_USE_ADVANCED_HISTORY 1 +/** If true (!=0) will enable using the objects API, which is a + collection of rest API features that enables "CRUD"(Create, Read, Update and Delete) + on two new pubnub objects: User and Space, as well as manipulating connections + between them. */ +#define PUBNUB_USE_OBJECTS_API 1 + #endif /* !defined INC_PUBNUB_CONFIG */ diff --git a/windows/pubnub_ntf_callback_windows.c b/windows/pubnub_ntf_callback_windows.c index 2c3d122d..9435bd5a 100644 --- a/windows/pubnub_ntf_callback_windows.c +++ b/windows/pubnub_ntf_callback_windows.c @@ -1,184 +1,205 @@ -/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ -#include "core/pubnub_ntf_callback.h" - -#include -#include -#include - -#include "pubnub_internal.h" -#include "core/pubnub_assert.h" -#include "core/pubnub_log.h" -#include "core/pubnub_timer_list.h" -#include "core/pbpal.h" - -#include "lib/sockets/pbpal_ntf_callback_poller_poll.h" -#include "core/pbpal_ntf_callback_queue.h" -#include "core/pbpal_ntf_callback_handle_timer_list.h" - -#include -#include - - -#if !defined _Guarded_by_ -#define _Guarded_by_(x) -#endif - - -/** The number of Windows FILETIME intervals in a millisecond. Windows - FILETIME interval is 100 ns. In practice, the actual resolution - may be (much) different, but, nominally it's 100ns. -*/ -#define MSEC_IN_FILETIME_INTERVALS (10 * 1000) - - -struct SocketWatcherData { - _Guarded_by_(mutw) struct pbpal_poll_data* poll; - CRITICAL_SECTION mutw; - CRITICAL_SECTION timerlock; - HANDLE thread_handle; - DWORD thread_id; -#if PUBNUB_TIMERS_API - _Guarded_by_(timerlock) pubnub_t* timer_head; -#endif - struct pbpal_ntf_callback_queue queue; -}; - - -static struct SocketWatcherData m_watcher; - - -static int elapsed_ms(FILETIME prev_timspec, FILETIME timspec) -{ - ULARGE_INTEGER prev; - ULARGE_INTEGER current; - prev.LowPart = prev_timspec.dwLowDateTime; - prev.HighPart = prev_timspec.dwHighDateTime; - current.LowPart = timspec.dwLowDateTime; - current.HighPart = timspec.dwHighDateTime; - return (int)((current.QuadPart - prev.QuadPart) / MSEC_IN_FILETIME_INTERVALS); -} - - -int pbntf_watch_in_events(pubnub_t* pbp) -{ - return pbpal_ntf_watch_in_events(m_watcher.poll, pbp); -} - - -int pbntf_watch_out_events(pubnub_t* pbp) -{ - return pbpal_ntf_watch_out_events(m_watcher.poll, pbp); -} - - -void socket_watcher_thread(void* arg) -{ - FILETIME prev_time; - GetSystemTimeAsFileTime(&prev_time); - - PUBNUB_UNUSED(arg); - - for (;;) { - const DWORD ms = 100; - - pbpal_ntf_callback_process_queue(&m_watcher.queue); - - Sleep(1); - - EnterCriticalSection(&m_watcher.mutw); - pbpal_ntf_poll_away(m_watcher.poll, ms); - LeaveCriticalSection(&m_watcher.mutw); - - if (PUBNUB_TIMERS_API) { - FILETIME current_time; - int elapsed; - GetSystemTimeAsFileTime(¤t_time); - elapsed = elapsed_ms(prev_time, current_time); - if (elapsed > 0) { - EnterCriticalSection(&m_watcher.timerlock); - pbntf_handle_timer_list(elapsed, &m_watcher.timer_head); - LeaveCriticalSection(&m_watcher.timerlock); - - prev_time = current_time; - } +/* -*- c-file-style:"stroustrup"; indent-tabs-mode: nil -*- */ +#include "core/pubnub_ntf_callback.h" + +#include +#include +#include + +#include "pubnub_internal.h" +#include "core/pubnub_assert.h" +#include "core/pubnub_log.h" +#include "core/pubnub_timer_list.h" +#include "core/pbpal.h" + +#include "lib/sockets/pbpal_ntf_callback_poller_poll.h" +#include "core/pbpal_ntf_callback_queue.h" +#include "core/pbpal_ntf_callback_handle_timer_list.h" + +#include +#include + + +#if !defined _Guarded_by_ +#define _Guarded_by_(x) +#endif + + +/** The number of Windows FILETIME intervals in a millisecond. Windows + FILETIME interval is 100 ns. In practice, the actual resolution + may be (much) different, but, nominally it's 100ns. +*/ +#define MSEC_IN_FILETIME_INTERVALS (10 * 1000) + + +struct SocketWatcherData { + _Guarded_by_(mutw) struct pbpal_poll_data* poll; + _Guarded_by_(stoplock) bool stop_socket_watcher_thread; + CRITICAL_SECTION mutw; + CRITICAL_SECTION timerlock; + CRITICAL_SECTION stoplock; + HANDLE thread_handle; + DWORD thread_id; +#if PUBNUB_TIMERS_API + _Guarded_by_(timerlock) pubnub_t* timer_head; +#endif + struct pbpal_ntf_callback_queue queue; +}; + + +static struct SocketWatcherData m_watcher; + + +static int elapsed_ms(FILETIME prev_timspec, FILETIME timspec) +{ + ULARGE_INTEGER prev; + ULARGE_INTEGER current; + prev.LowPart = prev_timspec.dwLowDateTime; + prev.HighPart = prev_timspec.dwHighDateTime; + current.LowPart = timspec.dwLowDateTime; + current.HighPart = timspec.dwHighDateTime; + return (int)((current.QuadPart - prev.QuadPart) / MSEC_IN_FILETIME_INTERVALS); +} + + +int pbntf_watch_in_events(pubnub_t* pbp) +{ + return pbpal_ntf_watch_in_events(m_watcher.poll, pbp); +} + + +int pbntf_watch_out_events(pubnub_t* pbp) +{ + return pbpal_ntf_watch_out_events(m_watcher.poll, pbp); +} + + +void socket_watcher_thread(void* arg) +{ + FILETIME prev_time; + GetSystemTimeAsFileTime(&prev_time); + + PUBNUB_UNUSED(arg); + + for (;;) { + const DWORD ms = 100; + bool stop_thread; + + EnterCriticalSection(&m_watcher.stoplock); + stop_thread = m_watcher.stop_socket_watcher_thread; + LeaveCriticalSection(&m_watcher.stoplock); + if (stop_thread) { + break; } - } -} - - -int pbntf_init(void) -{ - InitializeCriticalSection(&m_watcher.mutw); - InitializeCriticalSection(&m_watcher.timerlock); - - m_watcher.poll = pbpal_ntf_callback_poller_init(); - if (NULL == m_watcher.poll) { - return -1; - } - pbpal_ntf_callback_queue_init(&m_watcher.queue); - - m_watcher.thread_handle = (HANDLE)_beginthread( - socket_watcher_thread, PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB * 1024, NULL); - if ((HANDLE)-1L == m_watcher.thread_handle) { - PUBNUB_LOG_ERROR("Failed to start the polling thread, error code: %d\n", - errno); - DeleteCriticalSection(&m_watcher.mutw); - DeleteCriticalSection(&m_watcher.timerlock); - pbpal_ntf_callback_queue_deinit(&m_watcher.queue); - pbpal_ntf_callback_poller_deinit(&m_watcher.poll); - return -1; - } - m_watcher.thread_id = GetThreadId(m_watcher.thread_handle); - - return 0; -} - - -int pbntf_enqueue_for_processing(pubnub_t* pb) -{ - return pbpal_ntf_callback_enqueue_for_processing(&m_watcher.queue, pb); -} - - -int pbntf_requeue_for_processing(pubnub_t* pb) -{ - return pbpal_ntf_callback_requeue_for_processing(&m_watcher.queue, pb); -} - - -int pbntf_got_socket(pubnub_t* pb) -{ - EnterCriticalSection(&m_watcher.mutw); - pbpal_ntf_callback_save_socket(m_watcher.poll, pb); - LeaveCriticalSection(&m_watcher.mutw); - - if (PUBNUB_TIMERS_API) { - EnterCriticalSection(&m_watcher.timerlock); - m_watcher.timer_head = pubnub_timer_list_add(m_watcher.timer_head, pb); - LeaveCriticalSection(&m_watcher.timerlock); - } - - return +1; -} - - -void pbntf_lost_socket(pubnub_t* pb) -{ - EnterCriticalSection(&m_watcher.mutw); - pbpal_ntf_callback_remove_socket(m_watcher.poll, pb); - LeaveCriticalSection(&m_watcher.mutw); - - pbpal_ntf_callback_remove_from_queue(&m_watcher.queue, pb); - - EnterCriticalSection(&m_watcher.timerlock); - pbpal_remove_timer_safe(pb, &m_watcher.timer_head); - LeaveCriticalSection(&m_watcher.timerlock); -} - - -void pbntf_update_socket(pubnub_t* pb) -{ - EnterCriticalSection(&m_watcher.mutw); - pbpal_ntf_callback_update_socket(m_watcher.poll, pb); - LeaveCriticalSection(&m_watcher.mutw); + + pbpal_ntf_callback_process_queue(&m_watcher.queue); + + Sleep(1); + + EnterCriticalSection(&m_watcher.mutw); + pbpal_ntf_poll_away(m_watcher.poll, ms); + LeaveCriticalSection(&m_watcher.mutw); + + if (PUBNUB_TIMERS_API) { + FILETIME current_time; + int elapsed; + GetSystemTimeAsFileTime(¤t_time); + elapsed = elapsed_ms(prev_time, current_time); + if (elapsed > 0) { + EnterCriticalSection(&m_watcher.timerlock); + pbntf_handle_timer_list(elapsed, &m_watcher.timer_head); + LeaveCriticalSection(&m_watcher.timerlock); + + prev_time = current_time; + } + } + } +} + + +int pbntf_init(void) +{ + InitializeCriticalSection(&m_watcher.stoplock); + InitializeCriticalSection(&m_watcher.mutw); + InitializeCriticalSection(&m_watcher.timerlock); + + m_watcher.stop_socket_watcher_thread = false; + m_watcher.poll = pbpal_ntf_callback_poller_init(); + if (NULL == m_watcher.poll) { + return -1; + } + pbpal_ntf_callback_queue_init(&m_watcher.queue); + + m_watcher.thread_handle = (HANDLE)_beginthread( + socket_watcher_thread, PUBNUB_CALLBACK_THREAD_STACK_SIZE_KB * 1024, NULL); + if ((HANDLE)-1L == m_watcher.thread_handle) { + PUBNUB_LOG_ERROR("Failed to start the polling thread, error code: %d\n", + errno); + DeleteCriticalSection(&m_watcher.mutw); + DeleteCriticalSection(&m_watcher.timerlock); + DeleteCriticalSection(&m_watcher.stoplock); + pbpal_ntf_callback_queue_deinit(&m_watcher.queue); + pbpal_ntf_callback_poller_deinit(&m_watcher.poll); + return -1; + } + m_watcher.thread_id = GetThreadId(m_watcher.thread_handle); + + return 0; +} + + +void pubnub_stop(void) +{ + EnterCriticalSection(&m_watcher.stoplock); + m_watcher.stop_socket_watcher_thread = true; + LeaveCriticalSection(&m_watcher.stoplock); } + + +int pbntf_enqueue_for_processing(pubnub_t* pb) +{ + return pbpal_ntf_callback_enqueue_for_processing(&m_watcher.queue, pb); +} + + +int pbntf_requeue_for_processing(pubnub_t* pb) +{ + return pbpal_ntf_callback_requeue_for_processing(&m_watcher.queue, pb); +} + + +int pbntf_got_socket(pubnub_t* pb) +{ + EnterCriticalSection(&m_watcher.mutw); + pbpal_ntf_callback_save_socket(m_watcher.poll, pb); + LeaveCriticalSection(&m_watcher.mutw); + + if (PUBNUB_TIMERS_API) { + EnterCriticalSection(&m_watcher.timerlock); + m_watcher.timer_head = pubnub_timer_list_add(m_watcher.timer_head, pb); + LeaveCriticalSection(&m_watcher.timerlock); + } + + return +1; +} + + +void pbntf_lost_socket(pubnub_t* pb) +{ + EnterCriticalSection(&m_watcher.mutw); + pbpal_ntf_callback_remove_socket(m_watcher.poll, pb); + LeaveCriticalSection(&m_watcher.mutw); + + pbpal_ntf_callback_remove_from_queue(&m_watcher.queue, pb); + + EnterCriticalSection(&m_watcher.timerlock); + pbpal_remove_timer_safe(pb, &m_watcher.timer_head); + LeaveCriticalSection(&m_watcher.timerlock); +} + + +void pbntf_update_socket(pubnub_t* pb) +{ + EnterCriticalSection(&m_watcher.mutw); + pbpal_ntf_callback_update_socket(m_watcher.poll, pb); + LeaveCriticalSection(&m_watcher.mutw); +} diff --git a/windows/windows.mk b/windows/windows.mk index 53a57a10..9a4eac00 100644 --- a/windows/windows.mk +++ b/windows/windows.mk @@ -1,6 +1,6 @@ -SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_coreapi_ex.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_sockets.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_free_with_timeout_std.c ..\lib\base64\pbbase64.c ..\core\pubnub_timers.c ..\core\pubnub_json_parse.c ..\lib\md5\md5.c ..\core\pubnub_helper.c pubnub_version_windows.c pubnub_generate_uuid_windows.c pbpal_windows_blocking_io.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pubnub_subscribe_v2.c msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c +SOURCEFILES = ..\core\pubnub_pubsubapi.c ..\core\pubnub_coreapi.c ..\core\pubnub_coreapi_ex.c ..\core\pubnub_ccore_pubsub.c ..\core\pubnub_ccore.c ..\core\pubnub_netcore.c ..\lib\sockets\pbpal_sockets.c ..\lib\sockets\pbpal_resolv_and_connect_sockets.c ..\core\pubnub_alloc_std.c ..\core\pubnub_assert_std.c ..\core\pubnub_generate_uuid.c ..\core\pubnub_blocking_io.c ..\windows\windows_socket_blocking_io.c ..\core\pubnub_free_with_timeout_std.c ..\lib\base64\pbbase64.c ..\core\pubnub_timers.c ..\core\pubnub_json_parse.c ..\lib\md5\md5.c ..\lib\pb_strnlen_s.c ..\core\pubnub_helper.c pubnub_version_windows.c pubnub_generate_uuid_windows.c pbpal_windows_blocking_io.c ..\core\c99\snprintf.c ..\lib\miniz\miniz_tinfl.c ..\lib\miniz\miniz_tdef.c ..\lib\miniz\miniz.c ..\lib\pbcrc32.c ..\core\pbgzip_compress.c ..\core\pbgzip_decompress.c ..\core\pbcc_subscribe_v2.c ..\core\pubnub_subscribe_v2.c msstopwatch_windows.c ..\core\pubnub_url_encode.c ..\core\pbcc_advanced_history.c ..\core\pubnub_advanced_history.c ..\core\pbcc_objects_api.c ..\core\pubnub_objects_api.c -OBJFILES = pubnub_pubsubapi.obj pubnub_coreapi.obj pubnub_coreapi_ex.obj pubnub_ccore_pubsub.obj pubnub_ccore.obj pubnub_netcore.obj pbpal_sockets.obj pbpal_resolv_and_connect_sockets.obj pubnub_alloc_std.obj pubnub_assert_std.obj pubnub_generate_uuid.obj pubnub_blocking_io.obj windows_socket_blocking_io.obj pubnub_free_with_timeout_std.obj pbbase64.obj pubnub_timers.obj pubnub_json_parse.obj md5.obj pubnub_helper.obj pubnub_version_windows.obj pubnub_generate_uuid_windows.obj pbpal_windows_blocking_io.obj snprintf.obj miniz_tinfl.obj miniz_tdef.obj miniz.obj pbcrc32.obj pbgzip_compress.obj pbgzip_decompress.obj pubnub_subscribe_v2.obj msstopwatch_windows.obj pubnub_url_encode.obj pbcc_advanced_history.obj pubnub_advanced_history.obj +OBJFILES = pubnub_pubsubapi.obj pubnub_coreapi.obj pubnub_coreapi_ex.obj pubnub_ccore_pubsub.obj pubnub_ccore.obj pubnub_netcore.obj pbpal_sockets.obj pbpal_resolv_and_connect_sockets.obj pubnub_alloc_std.obj pubnub_assert_std.obj pubnub_generate_uuid.obj pubnub_blocking_io.obj windows_socket_blocking_io.obj pubnub_free_with_timeout_std.obj pbbase64.obj pubnub_timers.obj pubnub_json_parse.obj md5.obj pb_strnlen_s.obj pubnub_helper.obj pubnub_version_windows.obj pubnub_generate_uuid_windows.obj pbpal_windows_blocking_io.obj snprintf.obj miniz_tinfl.obj miniz_tdef.obj miniz.obj pbcrc32.obj pbgzip_compress.obj pbgzip_decompress.obj pbcc_subscribe_v2.obj pubnub_subscribe_v2.obj msstopwatch_windows.obj pubnub_url_encode.obj pbcc_advanced_history.obj pubnub_advanced_history.obj pbcc_objects_api.obj pubnub_objects_api.obj LDLIBS=ws2_32.lib IPHlpAPI.lib rpcrt4.lib