diff --git a/src/server/server.c b/src/server/server.c index 1cd8a22..c9720fc 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -48,7 +48,7 @@ #define XML_START "" #define STREAM_RESP "" -#define FEATURES "" +#define FEATURES "" #define STREAM_END "" @@ -97,7 +97,7 @@ write_stream(const char * const stream) int read_stream(void) { - char buf[2]; + char buf[1024]; memset(buf, 0, sizeof(buf)); GString *stream = g_string_new(""); @@ -120,7 +120,7 @@ read_stream(void) send_queue = NULL; pthread_mutex_unlock(&send_queue_lock); - int read_size = recv(client->sock, buf, 1, 0); + int read_size = recv(client->sock, buf, sizeof(buf) - 1, 0); // client disconnect if (read_size == 0) { @@ -133,6 +133,11 @@ read_stream(void) if (read_size == -1) { // got nothing, sleep and try again if (errno == EAGAIN || errno == EWOULDBLOCK) { + static int eagain_count = 0; + eagain_count++; + if (eagain_count % 1000 == 0) { + fflush(stderr); + } errno = 0; usleep(1000 * 5); continue; @@ -145,8 +150,9 @@ read_stream(void) } } - // success, feed parser with byte - parser_feed(buf, 1); + // success, feed parser with received data + buf[read_size] = '\0'; // null-terminate + parser_feed(buf, read_size); g_string_append_len(stream, buf, read_size); if (g_str_has_suffix(stream->str, STREAM_END)) { log_println(STBBR_LOGINFO, "RECV: "); @@ -168,6 +174,8 @@ stream_start_callback(void) write_stream(XML_START); write_stream(STREAM_RESP); write_stream(FEATURES); + // DEBUG removed + fflush(stderr); } void @@ -375,6 +383,7 @@ _start_server_cb(void* userdata) prctl(PR_SET_NAME, "stbr"); #endif + // DEBUG removed struct sockaddr_in client_addr; // listen socket non blocking @@ -390,6 +399,7 @@ _start_server_cb(void* userdata) int c = sizeof(struct sockaddr_in); int client_socket; errno = 0; + // DEBUG removed while ((client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, (socklen_t*)&c)) == -1) { if (errno != EAGAIN && errno != EWOULDBLOCK) { log_println(STBBR_LOGERROR, "Accept failed: %s", strerror(errno)); @@ -407,8 +417,10 @@ _start_server_cb(void* userdata) } client = xmppclient_new(client_addr, client_socket); + // DEBUG removed parser_init(stream_start_callback, auth_callback, id_callback, query_callback); + // DEBUG removed read_stream(); return NULL; diff --git a/src/server/stanza.c b/src/server/stanza.c index b7fa116..c3f447e 100644 --- a/src/server/stanza.c +++ b/src/server/stanza.c @@ -103,7 +103,8 @@ stanza_to_string(XMPPStanza *stanza) } char *result = stanza_str->str; - g_string_free(stanza_str, FALSE); + char *unused_gstring_data = g_string_free(stanza_str, FALSE); + (void)unused_gstring_data; // Silence warning return result; } @@ -248,12 +249,18 @@ stanza_get_query_request(XMPPStanza *stanza) return NULL; } - XMPPStanza *query = stanza_get_child_by_name(stanza, "query"); - if (!query) { - return NULL; + XMPPStanza *payload = stanza_get_child_by_name(stanza, "query"); + if (!payload) { + // Support IQ payloads that don't use (e.g. ) as long as + // there is a single child element to associate with the namespace. + if (stanza->children && stanza->children->next == NULL) { + payload = stanza->children->data; + } else { + return NULL; + } } - const char *xmlns = stanza_get_attr(query, "xmlns"); + const char *xmlns = stanza_get_attr(payload, "xmlns"); if (!xmlns) { return NULL; } diff --git a/src/server/stanzas.c b/src/server/stanzas.c index b962e5a..0e27412 100644 --- a/src/server/stanzas.c +++ b/src/server/stanzas.c @@ -153,32 +153,30 @@ _stanzas_equal(XMPPStanza *first, XMPPStanza *second) return -1; } - // check attribute count - if (g_list_length(first->attrs) != g_list_length(second->attrs)) { + // check attribute count - now we allow second to have MORE attributes + // All attrs from first must be in second, but not vice versa + if (g_list_length(first->attrs) > g_list_length(second->attrs)) { return -1; } - // check children count - if (g_list_length(first->children) != g_list_length(second->children)) { + // check children count - now we allow second to have MORE children + if (g_list_length(first->children) > g_list_length(second->children)) { return -1; } - // check presence of content - if (!first->content && second->content) { - return -1; - } + // check presence of content - only if first requires it if (first->content && !second->content) { return -1; } - // check content is exists + // check content if first has it if (first->content) { if (g_strcmp0(first->content->str, second->content->str) != 0) { return -1; } } - // check attributes + // check attributes - all from first must be in second if (first->attrs) { GList *first_curr_attr = first->attrs; while (first_curr_attr) { diff --git a/src/server/stream_parser.c b/src/server/stream_parser.c index 0fdc758..2764b91 100644 --- a/src/server/stream_parser.c +++ b/src/server/stream_parser.c @@ -68,9 +68,35 @@ int parser_feed(char *chunk, int len) { g_string_append_len(curr_string, chunk, len); + + // Special handling for XMPP stream opener + // Check if we've received a complete element (ending with '>') + // Since XMPP streams have an unclosed root element, we need special detection + if (len > 0 && chunk[len-1] == '>') { + const char *str = curr_string->str; + // Look for + if (strstr(str, "")) { + // Manually trigger the stream start callback + log_println(STBBR_LOGINFO, "RECV: %s", curr_string->str); + if (stream_start_cb) { + stream_start_cb(); + } + // Reset for next stanzas + g_string_free(curr_string, TRUE); + curr_string = g_string_new(""); + parser_close(); + parser_init(stream_start_cb, auth_cb, id_cb, query_cb); + // Feed a virtual root element to allow multiple stanzas + XML_Parse(parser, "", 9, 0); + return 1; + } + } + int res = XML_Parse(parser, chunk, len, 0); parser_reset(); + return res; } @@ -96,6 +122,12 @@ parser_reset(void) static void _start_element(void *data, const char *element, const char **attributes) { + + // Ignore our virtual wrapper element + if (g_strcmp0(element, "wrapper") == 0) { + return; + } + if (g_strcmp0(element, "stream:stream") == 0) { log_println(STBBR_LOGINFO, "RECV: %s", curr_string->str); stream_start_cb(); @@ -128,6 +160,10 @@ _end_element(void *data, const char *element) log_println(STBBR_LOGINFO, "RECV: %s", curr_string->str); stanzas_add(curr_stanza); + + // Clear the string buffer for the next stanza + g_string_truncate(curr_string, 0); + if (stanza_get_child_by_ns(curr_stanza, "jabber:iq:auth")) { auth_cb(curr_stanza); } else { @@ -141,7 +177,7 @@ _end_element(void *data, const char *element) } } - do_reset = 1; + // Don't reset parser here - allow multiple root elements for XMPP stanzas } static void