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