void ortc_presence(ortc_context* context, char* channel, void (*callback)(ortc_context*, char*, char*, ortc_presenceData*)){ pthread_t pThread; ortc_presenceParams *p; int ret; if(context->state != CONNECTED){ _ortc_exception(context, "Not connected"); } else if(!channel || strlen(channel)==0){ _ortc_exception(context, "Channel is null or empty"); } else if(!_ortc_isValidInput(context, channel)) { _ortc_exception(context, "Channel has invalid characters"); } else if(strlen(channel) > ORTC_CHANNEL_MAX_SIZE){ _ortc_exception(context, "Channel size exceeds the limit of characters"); } else if(!callback) { _ortc_exception(context, "Callback is empty"); } else { p = (ortc_presenceParams *)malloc(sizeof(ortc_presenceParams)); if(p == NULL){ callback(context, channel, "malloc ortc_presenceParams failed!", NULL); return; } p->isExtended = 0; p->context = context; p->channel = channel; p->callbackGet = callback; ret = pthread_create(&pThread, NULL, _ortc_presence, p); if(ret!=0){ callback(context, channel, "Error creating presence thread!", NULL); free(p); } } }
void ortc_save_authentication_ex(ortc_context *context, char* url, int isCluster, char* authToken, int isPrivate, char* appKey, int ttl, char *privateKey, ortc_channelPermissions *permissions, int sizeOfChannelPermissions, void (*callback)(ortc_context*, char*, char*)){ pthread_t pThread; ortc_authenticationParams *p; int ret; if(!permissions){ _ortc_exception(context, "Channel permissions are empty"); } else if(!callback) { _ortc_exception(context, "Callback is empty"); } else if(!privateKey || strlen(privateKey)==0){ _ortc_exception(context, "Private key is null or empty"); } else if(!_ortc_isValidInput(context, privateKey)) { _ortc_exception(context, "Private key has invalid characters"); } else if(!authToken || strlen(authToken)==0){ _ortc_exception(context, "Authentication token is null or empty"); } else if(!_ortc_isValidInput(context, authToken)) { _ortc_exception(context, "Authentication token has invalid characters"); } else if(!appKey || strlen(appKey)==0) { _ortc_exception(context, "Application Key is null or empty"); } else if(!_ortc_isValidInput(context, appKey)) { _ortc_exception(context, "Application Key has invalid characters"); } else if(!url || strlen(url)==0){ _ortc_exception(context, "Url is null or empty"); } else if(url && !_ortc_isValidUrl(context, url)) { _ortc_exception(context, "Invalid URL"); } else { isCluster = (isCluster>0) ? 1 : 0; isPrivate = (isPrivate>0) ? 1 : 0; p = (ortc_authenticationParams *)malloc(sizeof(ortc_authenticationParams)); if(p == NULL){ callback(context, "malloc ortc_authenticationParams failed", NULL); return; } p->isExtended = 1; p->context = context; p->url = url; p->isCluster = isCluster; p->appKey = appKey; p->authToken = authToken; p->isPrivate = isPrivate; p->ttl = ttl; p->privateKey = privateKey; p->permissions = permissions; p->sizeOfChannelPermissions = sizeOfChannelPermissions; p->callback = callback; ret = pthread_create(&pThread, NULL, _ortc_saveAuthentication, p); if(ret!=0){ callback(context, "Error creating authentication thread!", NULL); free(p); } } }
void ortc_enable_presence_ex(ortc_context* context, char* url, int isCluster, char* appKey, char* privateKey, char* channel, int metadata, void (*callback)(ortc_context*, char*, char*, char*)){ pthread_t pThread; ortc_presenceParams *p; int ret; if(!url || strlen(url)==0){ _ortc_exception(context, "Url is null or empty"); } else if (url && !_ortc_isValidUrl(context, url)) { _ortc_exception(context, "Invalid URL"); } else if (!appKey || strlen(appKey)==0) { _ortc_exception(context, "Application Key is null or empty"); } else if (!_ortc_isValidInput(context, appKey)) { _ortc_exception(context, "Application Key has invalid characters"); } else if(!channel || strlen(channel)==0){ _ortc_exception(context, "Channel is null or empty"); } else if(!_ortc_isValidInput(context, channel)) { _ortc_exception(context, "Channel has invalid characters"); } else if(strlen(channel) > ORTC_CHANNEL_MAX_SIZE){ _ortc_exception(context, "Channel size exceeds the limit of characters"); } else if(!callback) { _ortc_exception(context, "Callback is empty"); } else if(!privateKey || strlen(privateKey)==0){ _ortc_exception(context, "Private key is null or empty"); } else if(!_ortc_isValidInput(context, privateKey)) { _ortc_exception(context, "Private key has invalid characters"); } else { isCluster = (isCluster>0) ? 1 : 0; metadata = (metadata>0) ? 1 : 0; p = (ortc_presenceParams *)malloc(sizeof(ortc_presenceParams)); if(p == NULL){ callback(context, channel, "malloc ortc_presenceParams failed!", NULL); return; } p->isExtended = 1; p->url = url; p->isCluster = isCluster; p->appKey = appKey; p->context = context; p->privateKey = privateKey; p->channel = channel; p->metadata = metadata; p->callbackCmd = callback; ret = pthread_create(&pThread, NULL, _ortc_enablePresence, p); if(ret!=0){ callback(context, channel, "Error creating presence thread!", NULL); free(p); } } }
void _ortc_send(ortc_context* context, char* channel, char* message){ int i; size_t len; char *hash = _ortc_get_channel_permission(context, channel); char messageId[9], sParts[15], sMessageCount[15]; int messageCount = 0; char* messagePart, *m; size_t parts = strlen(message) / ORTC_MAX_MESSAGE_SIZE; _ortc_random_string(messageId, 9); if(strlen(message) % ORTC_MAX_MESSAGE_SIZE > 0) parts++; sprintf(sParts, "%d", (int)parts); for(i=0; i<parts; i++){ size_t messageSize; char *messageR; messageSize = strlen(message) - i * ORTC_MAX_MESSAGE_SIZE; if(messageSize > ORTC_MAX_MESSAGE_SIZE) messageSize = ORTC_MAX_MESSAGE_SIZE; messageCount = i + 1; sprintf(sMessageCount, "%d", messageCount); messagePart = (char*)malloc(messageSize+1); if(messagePart==NULL){ _ortc_exception(context, "malloc() failed in ortc send!"); return; } memcpy(messagePart, message + i * ORTC_MAX_MESSAGE_SIZE, messageSize); messagePart[messageSize] = '\0'; messageR = _ortc_escape_sequences_before(messagePart); len = 15 + strlen(context->appKey) + strlen(context->authToken) + strlen(channel) + strlen(hash) + strlen(messageId) + strlen(sParts) + strlen(sMessageCount) + strlen(messageR); m = (char*)malloc(len + 1); if(m == NULL){ _ortc_exception(context, "malloc() failed in ortc send!"); free(messagePart); free(messageR); return; } snprintf(m, len, "\"send;%s;%s;%s;%s;%s_%d-%d_%s\"", context->appKey, context->authToken, channel, hash, messageId, messageCount, (int)parts, messageR); free(messagePart); free(messageR); _ortc_send_message(context, m); } }
void ortc_send(ortc_context* context, char *channel, char *message){ if(context->state != CONNECTED){ _ortc_exception(context, "Not connected"); } else if(!channel || strlen(channel)==0){ _ortc_exception(context, "Channel is null or empty"); } else if(!_ortc_isValidInput(context, channel)) { _ortc_exception(context, "Channel has invalid characters"); } else if(!message || strlen(message)==0){ _ortc_exception(context, "Message is null or empty"); } else if(strlen(channel) > ORTC_CHANNEL_MAX_SIZE){ _ortc_exception(context, "Channel size exceeds the limit of characters"); } else { _ortc_send(context, channel, message); } }
void _ortc_save_permissions(ortc_context *context, char *permissions){ int rc, offset = 0; regex_t rePerm; size_t nmatch = 3; regmatch_t pmatch[3]; char *channel, *hash; _ortc_dlist_clear(context->permissions); if(strcmp(permissions, "null")!=0){ if (0 != (rc = regcomp(&rePerm, "\\\\\"\\(.[^\\\\\"]*\\)\\\\\":\\\\\"\\(.[^\\\\\"]*\\)\\\\\"", 0))) { _ortc_exception(context, "regcomp() failed, returning nonzero in ortc save permissions"); return; } while(1){ if(regexec(&rePerm, permissions + offset, nmatch, pmatch, 0)) break; channel = _ortc_get_from_regmatch(permissions, pmatch[1]); hash = _ortc_get_from_regmatch(permissions + offset, pmatch[2]); _ortc_dlist_insert(context->permissions, channel, hash, NULL, 0, NULL); offset += pmatch[2].rm_eo; free(channel); free(hash); } regfree(&rePerm); } }
void ortc_disconnect(ortc_context* context){ if(context->state != DISCONNECTED){ _ortc_disconnect(context); } else { _ortc_exception(context, "Not connected!"); } }
int _ortc_prepare_websocket(ortc_context* context){ struct lws_context_creation_info info; if(context->lws_context) libwebsocket_context_destroy(context->lws_context); context->lws_context = NULL; if(context->host) free(context->host); context->host = NULL; if(context->server) free(context->server); context->server = NULL; memset(&info, 0, sizeof info); info.port = CONTEXT_PORT_NO_LISTEN; info.gid = -1; info.uid = -1; info.protocols = ortc_protocols; info.ssl_cipher_list = "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL"; info.ka_time = 0; info.ka_interval = 0; info.ka_probes = 0; context->lws_context = libwebsocket_create_context(&info); if (context->lws_context == NULL) { _ortc_exception(context, "Creating libwebsocket context failed!"); return -1; } return 0; }
void ortc_unsubscribe(ortc_context* context, char *channel){ if(context->state != CONNECTED){ _ortc_exception(context, "Not connected"); } else if(!channel || strlen(channel)==0){ _ortc_exception(context, "Channel is null or empty"); } else if(!_ortc_isValidInput(context, channel)) { _ortc_exception(context, "Channel has invalid characters"); } else if(!ortc_is_subscribed(context, channel)){ char *ex_msg = _ortc_ch_ex_msg("Not subscribed to the channel", channel); _ortc_exception(context, ex_msg); free(ex_msg); } else if(strlen(channel) > ORTC_CHANNEL_MAX_SIZE){ _ortc_exception(context, "Channel size exceeds the limit of characters"); } else { _ortc_unsubscribe(context, channel); } }
void ortc_subscribe(ortc_context* context, char *channel, int subscribeOnReconnected, void (*onMessage)(ortc_context*, char*, char*)){ if(context->state != CONNECTED){ _ortc_exception(context, "Not connected"); } else if(!channel || strlen(channel)==0){ _ortc_exception(context, "Channel is null or empty"); } else if(!_ortc_isValidInput(context, channel)) { _ortc_exception(context, "Channel has invalid characters"); } else if(_ortc_is_subscribing(context, channel)){ char *ex_msg = _ortc_ch_ex_msg("Already trying to subscribe this channel", channel); _ortc_exception(context, ex_msg); free(ex_msg); } else if(ortc_is_subscribed(context, channel)){ char *ex_msg = _ortc_ch_ex_msg("Already subscribed to this channel", channel); _ortc_exception(context, ex_msg); free(ex_msg); } else if(strlen(channel) > ORTC_CHANNEL_MAX_SIZE){ _ortc_exception(context, "Channel size exceeds the limit of characters"); } else if(!onMessage){ _ortc_exception(context, "The argument \"onMessage\" must point to a function"); } else { _ortc_subscribe(context, channel, subscribeOnReconnected, 1, onMessage); } }
void _ortc_fireOnReconnecting(ortc_context *context){ pthread_t thr; if(context->onReconnecting != NULL){ int tret = pthread_create(&thr, NULL, _ortc_job_onReconnecting, context); if(tret!=0){ _ortc_exception(context, (char*)"Error creating event (onReconnecting) thread!"); } } }
void _ortc_unsubscribe(ortc_context* context, char* channel){ int len = 16 + strlen(context->appKey) + strlen(channel); char *unsubscribeCommand = (char*)malloc(len + 1); ortc_dnode *ch; if(unsubscribeCommand == NULL){ _ortc_exception(context, "malloc() failed in ortc unsubscribe"); return; } ch = _ortc_dlist_search(context->channels, channel); if(ch != NULL) ch->num = -1; //is trying to unsbscribed snprintf(unsubscribeCommand, len, "\"unsubscribe;%s;%s\"", context->appKey, channel); _ortc_send_command(context, unsubscribeCommand); }
void _ortc_subscribe(ortc_context* context, char* channel, int subscribeOnReconnected, int toBeSaved, void (*onMessage)(ortc_context*, char*, char*)){ char *hash = _ortc_get_channel_permission(context, channel); int len = 16 + strlen(context->appKey) + strlen(context->authToken) + strlen(channel) + strlen(hash); char *subscribeCommand = (char*)malloc(len + 1); if(subscribeCommand == NULL){ _ortc_exception(context, "malloc() failed in ortc subscribe"); return; } subscribeOnReconnected = (subscribeOnReconnected>0)?1:0; if(toBeSaved) _ortc_dlist_insert(context->channels, channel, NULL, NULL, subscribeOnReconnected, (void(*)(ortc_context*, char*, char*))onMessage); snprintf(subscribeCommand, len, "\"subscribe;%s;%s;%s;%s\"", context->appKey, context->authToken, channel, hash); _ortc_send_command(context, subscribeCommand); }
void _ortc_parse_message(ortc_context *context, char *message){ char *messageId, *messageCount, *messageTotal, *messagePart, *channelNameStr, *messageStr, *params, *permissionsStr, *exceptionStr, *validateString, *operationType; struct cap pmatch[3], pmatch2[5]; int iMessageTotal, wsSock, opt; size_t len, hbLen; ortc_dnode *ch; char hbStr[24]; if(message[0] == 'a') { if (slre_match(&context->reMessage, message, (int)strlen(message), pmatch)) { //is message channelNameStr = _ortc_get_from_slre(1, pmatch); messageStr = _ortc_get_from_slre(2, pmatch); if(slre_match(&context->reMultipart, messageStr, (int)strlen(messageStr), pmatch2)){ messageId = _ortc_get_from_slre(1, pmatch2); messageCount = _ortc_get_from_slre(2, pmatch2); messageTotal = _ortc_get_from_slre(3, pmatch2); messagePart = _ortc_get_from_slre(4, pmatch2); iMessageTotal = atoi(messageTotal); if(iMessageTotal > 1){ //multipart message _ortc_dlist_insert(context->multiparts, messageId, channelNameStr, messagePart, atoi(messageCount), NULL); _ortc_check_if_got_all_parts(context, messageId, iMessageTotal); } else { _ortc_fire_onMessage(context, channelNameStr, messagePart); } free(messageId); free(messageCount); free(messageTotal); free(messagePart); } else { _ortc_fire_onMessage(context, channelNameStr, messageStr); } free(channelNameStr); free(messageStr); } else if (slre_match(&context->reOperation, message, (int)strlen(message), pmatch)) { params = _ortc_get_from_slre(2, pmatch); operationType = _ortc_get_from_slre(1, pmatch); if(strncmp(operationType, "ortc-validated", 14)==0){ if(slre_match(&context->rePermissions, params, (int)strlen(params), pmatch2)){ permissionsStr = _ortc_get_from_slre(1, pmatch2); _ortc_save_permissions(context, permissionsStr); free(permissionsStr); } _ortc_change_state(context, CONNECTED); } else if(strncmp(operationType, "ortc-subscribed", 15)==0){ if(slre_match(&context->reChannel, params, (int)strlen(params), pmatch2)){ channelNameStr = _ortc_get_from_slre(1, pmatch2); ch = _ortc_dlist_search(context->channels, channelNameStr); if(ch != NULL) ch->num += 2; //isSubscribed if(context->onSubscribed != NULL) context->onSubscribed(context, channelNameStr); free(channelNameStr); } } else if(strncmp(operationType, "ortc-unsubscribed", 17)==0){ if(slre_match(&context->reChannel, params, (int)strlen(params), pmatch2)){ channelNameStr = _ortc_get_from_slre(1, pmatch2); _ortc_dlist_delete(context->channels, channelNameStr); if(context->onUnsubscribed != NULL) context->onUnsubscribed(context, channelNameStr); free(channelNameStr); } } else if(strncmp(operationType, "ortc-error", 10)==0){ if(slre_match(&context->reException, params, (int)strlen(params), pmatch2)){ _ortc_cancel_connecting(context); exceptionStr = _ortc_get_from_slre(1, pmatch2); _ortc_exception(context, exceptionStr); free(exceptionStr); } } free(params); free(operationType); } } else if(message[0] == 'o' && strlen(message)==1){ wsSock = libwebsocket_get_socket_fd(context->wsi); opt = ORTC_SNDBUF_SIZE; setsockopt(wsSock, SOL_SOCKET, SO_SNDBUF, (const char*)&opt, sizeof(opt)); if(context->heartbeatActive){ snprintf(hbStr, sizeof(hbStr), "%d;%d;", context->heartbeatTime, context->heartbeatFails); hbLen = strlen(hbStr); } else { hbLen = 0; } len = 17 + strlen(context->appKey) + strlen(context->authToken) + strlen(context->announcementSubChannel) + strlen(context->sessionId) + strlen(context->metadata) + hbLen; validateString = malloc(len+1); if(validateString == NULL){ _ortc_exception(context, "malloc() failed in ortc parese message"); return; } if(context->heartbeatActive) snprintf(validateString, len, "\"validate;%s;%s;%s;%s;%s;%s\"", context->appKey, context->authToken, context->announcementSubChannel, context->sessionId, context->metadata, hbStr); else snprintf(validateString, len, "\"validate;%s;%s;%s;%s;%s;\"", context->appKey, context->authToken, context->announcementSubChannel, context->sessionId, context->metadata); _ortc_send_command(context, validateString); } else if(strncmp(message, "c[1000,\"Normal closure\"]", 20)==0){ _ortc_exception(context, "Server is about to close the websocket!"); } }
void ortc_connect(ortc_context* context, char* applicationKey, char* authenticationToken){ int tret; if(context->state == CONNECTED){ _ortc_exception(context, "Already connected"); } else if (context->state != DISCONNECTED){ _ortc_exception(context, "Already trying to connect"); } else if (!context->url && !context->cluster){ _ortc_exception(context, "URL and Cluster URL are null or empty"); } else if (!applicationKey || strlen(applicationKey)==0) { _ortc_exception(context, "Application Key is null or empty"); } else if (!authenticationToken || strlen(authenticationToken)==0) { _ortc_exception(context, "Authentication Token is null or empty"); } else if (context->url && !_ortc_isValidUrl(context, context->url)) { _ortc_exception(context, "Invalid URL"); } else if (context->cluster && !_ortc_isValidUrl(context, context->cluster)) { _ortc_exception(context, "Invalid Cluster URL"); } else if (!_ortc_isValidInput(context, applicationKey)) { _ortc_exception(context, "Application Key has invalid characters"); } else if (!_ortc_isValidInput(context, authenticationToken)) { _ortc_exception(context, "Authentication Token has invalid characters"); } else if (!_ortc_isValidInput(context, context->announcementSubChannel)) { _ortc_exception(context, "Announcement Subchannel has invalid characters"); } else if (strlen(context->metadata) > ORTC_CONNECTION_METADATA_MAX_SIZE){ _ortc_exception(context, "Connection metadata size exceeds the limit"); } else { context->appKey = applicationKey; context->authToken = authenticationToken; _ortc_init_connection(context); } }