diff options
-rw-r--r-- | README.md | 50 | ||||
-rw-r--r-- | data.c | 149 | ||||
-rw-r--r-- | data.h | 40 | ||||
-rw-r--r-- | gtk.c | 272 | ||||
-rw-r--r-- | gui.c (renamed from sdl2.c) | 23 | ||||
-rw-r--r-- | gui.h | 12 | ||||
-rw-r--r-- | main.c | 50 | ||||
-rw-r--r-- | meson.build | 5 | ||||
-rw-r--r-- | net.h | 14 | ||||
-rw-r--r-- | strophe.c | 228 | ||||
-rw-r--r-- | xmpp.c | 48 | ||||
-rw-r--r-- | xmpp.h | 1 |
12 files changed, 68 insertions, 824 deletions
@@ -6,15 +6,8 @@ The Linux on mobile XMPP (LimoX) client. It should be fast, compliant and usable on mobile linux devices like smartphones or tablets. Also mind the [project page][2] and my [packaging repository][3] for Arch Linux -with support for LimoX. - -Currently there is an ongoing rewrite in progress. There is the initial version -of LimoX and a rewrite (`limox_sdl2` in the build folder). The rewrite -substitutes GTK4 by SDL2 for graphics and maybe will also drop libstrophe as a -dependency. - -A nice reference which helps me a lot is [Beej's Guide to Network -Programming][4]. +with support for LimoX. A nice reference which helps me a lot is [Beej's Guide +to Network Programming][4]. ## Building and dependencies @@ -27,8 +20,6 @@ Install these build dependencies: Install these runtime dependencies: -- libstrophe -- gtk4 - sdl2 Just run these commands to build and run: @@ -37,7 +28,6 @@ Just run these commands to build and run: meson setup build ninja -C build ./build/limox -./build/limox_sdl2 # the SDL2 based rewrite ``` Run `rm -r build` to clean the build folder. You can get a help page by running @@ -46,37 +36,15 @@ Run `rm -r build` to clean the build folder. You can get a help page by running ## Roadmap -- [x] build a GTK 4 GUI -- [x] finish minimal viable product (MVP) to send and receive text messages - - [x] static GUI widgets - - [x] dynamic GUI widgets (like text messages) - - [x] roster request - - [x] receiving one-to-one text messages - - [x] sending one-to-one text messages -- [ ] switch from GTK4 to SDL2 - - [x] implement second executable with empty SDL2 window - - [ ] integrate libstrophe into SDL2 event loop (validate with presence) - - [ ] re-implement login page - - [ ] re-implement roster page - - [ ] re-implement chat page -- [ ] refactoring - - [x] fix reconnection bug - - [ ] check memory management - - [ ] handle roster updates - - [ ] remove states enum in net.c (doubles logic from libstrophe) - - [ ] consistent error handling (maybe including semantic error codes) - - [ ] logging / debug flags and output - - [ ] OS signal handling - - [ ] write docstrings - - [ ] check if code is splint-clean -- [ ] persistence with sqlite3 -- [ ] XEP-0077: In-Band Registration (just password change) -- [ ] XEP-0313: Message Archive Management +- [x] develop minimal viable product with GTK4 (branch `legacy/gtk4-libstrophe`) - [ ] implement XMPP core client ([compliance suite 2022][1]) - [ ] RFC 6120: XMPP Core - [ ] RFC 7590: TLS for XMPP - [ ] XEP-0030: Service Discovery - [ ] XEP-0115: Entity Capabilities +- [ ] implement XMPP mobile client ([compliance suite 2022][1]) + - [ ] XEP-0198: Stream Management + - [ ] XEP-0352: Client State Indication - [ ] implement XMPP IM client ([compliance suite 2022][1]) - [ ] RFC 6121: XMPP Instant Messaging - [ ] XEP-0245: The /me Command @@ -85,11 +53,9 @@ Run `rm -r build` to clean the build folder. You can get a help page by running - [ ] XEP-0045: Multi-User Chat - [ ] XEP-0249: Direct MUC Invitations - [ ] XEP-0363: HTTP File Upload -- [ ] implement XMPP mobile client ([compliance suite 2022][1]) - - [ ] XEP-0198: Stream Management - - [ ] XEP-0352: Client State Indication -- [ ] evaluate multi-platform support (up to Linux, Windows, Android, Mac, iOS) - [ ] implement further XEPs + - [ ] XEP-0077: In-Band Registration (just password change) + - [ ] XEP-0313: Message Archive Management - [ ] XEP-0286: Mobile Considerations on LTE Networks - [ ] XEP-0333: Chat Markers - [ ] XEP-0085: Chat State Notifications @@ -1,149 +0,0 @@ - - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <stdbool.h> - -#include "gui.h" -#include "data.h" - - -static roster_item_t* roster = NULL; - - -/* check if JID is already in roster data structure */ -bool is_jid_known(const char* jid) { - - roster_item_t* i; - - // iterate over all roster items - for (i = roster; i != NULL; i=i->next) { - if (strcmp(i->jid, jid) == 0) { - return true; - } - } - - return false; - -} - -void data_add_roster_item(const char* jid, const char* subscription, - const char* name) { - - // FIXME handle roster updates - if (is_jid_known(jid)) { - return; - } - - // allocate datastructures - roster_item_t* item = malloc(sizeof(roster_item_t)); - chat_t* chat = malloc(sizeof(chat_t)); - - // initialize chat - chat->messages = NULL; - chat->jid = malloc(sizeof(char)*strlen(jid)); - strcpy(chat->jid, jid); - gui_add_chat_widget(chat); - - // initialize roster item - if (name == NULL) { - item->name = NULL; - } else { - item->name = malloc(sizeof(char)*strlen(name)); - strcpy(item->name, name); - } - item->jid = malloc(sizeof(char)*strlen(jid)); - strcpy(item->jid, jid); - if (strcmp(subscription, "none") == 0) { - item->sub = SUB_NONE; - } else if (strcmp(subscription, "to") == 0) { - item->sub = SUB_TO; - } else if (strcmp(subscription, "from") == 0) { - item->sub = SUB_FROM; - } else if (strcmp(subscription, "both") == 0) { - item->sub = SUB_BOTH; - } else { - fprintf(stderr, "Invalid subscription '%s'!\n", subscription); - return; - } - item->chat = chat; - item->next = NULL; - gui_add_roster_item_widget(item); - - // add item to roster datastructure - if (roster == NULL) { - roster = item; - } else { - roster_item_t* current = roster; - while (current->next != NULL) // loop until end of linked list - current = current->next; - current->next = item; - } - -} - -void data_add_incoming_message(const char* sender_jid, const char* content) { - - // TODO rework based on chat->jid - // find correct chat - chat_t* chat = NULL; - roster_item_t* i; - int bare_len; // length of bare JID - for (i = roster; i != NULL; i=i->next) { - bare_len = strlen(i->jid); - if (strncmp(i->jid, sender_jid, bare_len) == 0) { - chat = i->chat; - break; - } - } - if (chat == NULL) { - fprintf(stderr, "Could not find chat for message from '%s'!\n", sender_jid); - return; - } - - // initialize message_t - message_t* msg = malloc(sizeof(message_t)); - msg->sender_jid = malloc(sizeof(char)*strlen(sender_jid)); - strcpy(msg->sender_jid, sender_jid); - msg->content = malloc(sizeof(char)*strlen(content)); - strcpy(msg->content, content); - msg->next = NULL; - gui_add_message_widget(msg, i->chat); - - // find pointer to next message of chat - message_t* m; - for (m = chat->messages; m != NULL; m=m->next); - - // append message to chat - m = msg; - - // TODO could be more correct to update chat->jid to full JID of just - // received message so that future responses go to this ressource - -} - -void data_add_outgoing_message(const char* sender_jid, const char* content, - chat_t* chat) { - - // allocate and initialize message_t - message_t* msg = malloc(sizeof(message_t)); - msg->sender_jid = malloc(sizeof(char)*strlen(sender_jid)); - strcpy(msg->sender_jid, sender_jid); - msg->recipient_jid = malloc(sizeof(char)*strlen(chat->jid)); - strcpy(msg->recipient_jid, chat->jid); - msg->content = malloc(sizeof(char)*strlen(content)); - strcpy(msg->content, content); - msg->next = NULL; - - // find pointer to next message of chat and add this message - message_t* m; - for (m = chat->messages; m != NULL; m=m->next); - m = msg; - - // TODO send this message via XMPP - - // create GUI widget for this message - gui_add_message_widget(msg, chat); - -} @@ -1,40 +0,0 @@ -#ifndef DATA_H -#define DATA_H - -// https://www.rfc-editor.org/rfc/rfc6121#section-2.1.2.5 -typedef enum { - SUB_NONE, - SUB_TO, - SUB_FROM, - SUB_BOTH -} subscription_t; - -typedef struct _message_t { - char* content; - char* sender_jid; - char* recipient_jid; - struct _message_t* next; - void* widget; -} message_t; - -typedef struct _chat_t { - char* jid; - message_t* messages; - void* widget; -} chat_t; - -typedef struct _roster_item_t { - char* name; // could be NULL - char* jid; - subscription_t sub; - chat_t* chat; - struct _roster_item_t* next; - void* widget; -} roster_item_t; - -void data_add_roster_item(const char* jid, const char* subscription, - const char* name); -void data_add_incoming_message(const char* sender_jid, const char* content); -void data_add_outgoing_message(const char* sender_jid, const char* content, - chat_t* chat); -#endif @@ -1,272 +0,0 @@ - - -#include <gtk-4.0/gtk/gtk.h> -#include <stdio.h> - -#include "net.h" -#include "data.h" - - -typedef struct _chat_widget_t { - GtkWidget* page; - GtkWidget* content; - GtkWidget* entry; -} chat_widget_t; - - -// the GTK application is available as global variable -static GtkApplication* app; - -// all GTK widgets are accessible in this file via global variables -static GtkWidget* window; -static GtkWidget* stack; - -// connector page -static GtkStackPage* connector_page; -static GtkWidget* connector_box; -static GtkWidget* connector_jid_label; -static GtkWidget* connector_jid_entry; -static GtkWidget* connector_pwd_label; -static GtkWidget* connector_pwd_entry; -static GtkWidget* connector_button; - -// roster page -static GtkStackPage* roster_page; -static GtkWidget* roster_layout_box; -static GtkWidget* roster_scrolled; -static GtkWidget* roster_content_box; -static GtkWidget* roster_button; - - -static void quit_cb(void) { - g_application_quit(G_APPLICATION(app)); -} - -static void connect_cb(void) { - - const char* jid_text = gtk_editable_get_text(GTK_EDITABLE(connector_jid_entry)); - const char* pwd_text = gtk_editable_get_text(GTK_EDITABLE(connector_pwd_entry)); - - gtk_stack_set_visible_child(GTK_STACK(stack), roster_layout_box); - - // just dummy output - fprintf(stderr, "Connecting with:\nJID: %s\nPWD: %s\n", jid_text, pwd_text); - - net_connect(jid_text, pwd_text); -} - -static void disconnect_cb(void) { - - gtk_stack_set_visible_child(GTK_STACK(stack), connector_box); - - // just dummy output - fprintf(stderr, "Disconnected!\n"); - - net_disconnect(); -} - -void send_message(chat_t* chat) { - - // cast void* to GUI-specific chat_widget_t* - chat_widget_t* chat_widget = (chat_widget_t*)chat->widget; - - // get text of message and own JID - const char* text = gtk_editable_get_text(GTK_EDITABLE(chat_widget->entry)); - const char* self = gtk_editable_get_text(GTK_EDITABLE(connector_jid_entry)); - - // add message to datastructure and send it - data_add_outgoing_message(self, text, chat); - net_send_message(self, text, chat->jid); - - // clear text input - GtkEntryBuffer* buffer; - buffer = gtk_entry_get_buffer(GTK_ENTRY(chat_widget->entry)); - gtk_entry_buffer_delete_text(buffer, 0, -1); -} - -/* Set credentials from environment variables if given */ -static void set_debug_credentials(GtkWidget* user, GtkWidget* pwd) { - - char* user_str = getenv("LIMOX_USER"); - if (user_str != NULL) { - GtkEntryBuffer* buffer; - buffer = gtk_entry_get_buffer(GTK_ENTRY(user)); - gtk_entry_buffer_set_text(buffer, user_str, -1); - } - - char* pwd_str = getenv("LIMOX_PWD"); - if (pwd_str != NULL) { - gtk_editable_set_text(GTK_EDITABLE(pwd), pwd_str); - } - -} - -static void build_static_widgets(void) { - - // main window with stack - window = gtk_window_new(); - gtk_window_set_title(GTK_WINDOW(window), "LimoX"); - gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); - stack = gtk_stack_new(); - gtk_window_set_child(GTK_WINDOW(window), stack); - - // connector page - connector_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 20); - connector_page = gtk_stack_add_child(GTK_STACK(stack), connector_box); - connector_jid_label = gtk_label_new("XMPP address (JID)"); - gtk_box_append(GTK_BOX(connector_box), connector_jid_label); - connector_jid_entry = gtk_entry_new(); - gtk_box_append(GTK_BOX(connector_box), connector_jid_entry); - connector_pwd_label = gtk_label_new("Password"); - gtk_box_append(GTK_BOX(connector_box), connector_pwd_label); - connector_pwd_entry = gtk_password_entry_new(); - gtk_box_append(GTK_BOX(connector_box), connector_pwd_entry); - connector_button = gtk_button_new_with_label("Connect"); - gtk_box_append(GTK_BOX(connector_box), connector_button); - set_debug_credentials(connector_jid_entry, connector_pwd_entry); - - // roster page - roster_layout_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 20); - roster_page = gtk_stack_add_child(GTK_STACK(stack), roster_layout_box); - roster_button = gtk_button_new_with_label("Disconnect"); - gtk_box_append(GTK_BOX(roster_layout_box), roster_button); - roster_scrolled = gtk_scrolled_window_new(); - gtk_widget_set_vexpand(roster_scrolled, TRUE); - roster_content_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(roster_scrolled), - roster_content_box); - gtk_box_append(GTK_BOX(roster_layout_box), roster_scrolled); - -} - -static void activate(void) { - - build_static_widgets(); - - // configure widgets - gtk_window_set_application(GTK_WINDOW(window), app); - g_signal_connect_swapped(window, "close-request", G_CALLBACK(quit_cb), app); - g_signal_connect_swapped( - connector_button, "clicked", G_CALLBACK(connect_cb), NULL); - g_signal_connect_swapped( - roster_button, "clicked", G_CALLBACK(disconnect_cb), NULL); - - gtk_widget_show(window); -} - -static void idle_cb(void) { - - net_run_once(); - -} - -void gui_run(void) { - -#ifdef GTK_SRCDIR - g_chdir(GTK_SRCDIR); -#endif - - app = gtk_application_new("eu.xengineering.limox", G_APPLICATION_DEFAULT_FLAGS); - g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); - g_idle_add(G_SOURCE_FUNC(idle_cb), NULL); - - g_application_run(G_APPLICATION(app), 0, NULL); - g_object_unref(app); - -} - -static void to_roster(void) { - - gtk_stack_set_visible_child(GTK_STACK(stack), roster_layout_box); - -} - -void gui_add_chat_widget(chat_t* chat) { - - // allocate chat_widget_t and save as void* in given chat_t - chat_widget_t* chat_widget = malloc(sizeof(chat_widget_t)); - chat->widget = (void*)chat_widget; - - // create chat page (GtkBox) and add it to the stack - GtkWidget* chat_layout_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 20); - gtk_stack_add_child(GTK_STACK(stack), chat_layout_box); - chat_widget->page = chat_layout_box; - - // create and add back button - GtkWidget* back = gtk_button_new_with_label("back"); - gtk_box_append(GTK_BOX(chat_layout_box), back); - g_signal_connect_swapped(back, "clicked", G_CALLBACK(to_roster), NULL); - - // create and add scrolled window - GtkWidget* scrolled = gtk_scrolled_window_new(); - gtk_widget_set_vexpand(scrolled, TRUE); - gtk_box_append(GTK_BOX(chat_layout_box), scrolled); - - // create and add content box for dynamic message widgets - GtkWidget* chat_content_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled), - chat_content_box); - chat_widget->content = chat_content_box; - - // create and add text input field - GtkWidget* text_entry = gtk_entry_new(); - gtk_box_append(GTK_BOX(chat_layout_box), text_entry); - chat_widget->entry = text_entry; - - // create and add send button - GtkWidget* send_button = gtk_button_new_with_label("send"); - gtk_box_append(GTK_BOX(chat_layout_box), send_button); - g_signal_connect_swapped(send_button, "clicked", G_CALLBACK(send_message), - chat); - -} - -void gui_add_message_widget(message_t* message, chat_t* chat) { - - const char* connector = "\n"; - char* label_content = malloc(sizeof(char)*(strlen(message->sender_jid) - + strlen(connector) - + strlen(message->content))); - sprintf(label_content, "%s%s%s", message->sender_jid, connector, - message->content); - GtkWidget* label = gtk_label_new(label_content); - gtk_label_set_wrap(GTK_LABEL(label), TRUE); - gtk_label_set_selectable(GTK_LABEL(label), TRUE); - message->widget = (void*)label; - chat_widget_t* chat_widget = (chat_widget_t*)chat->widget; - gtk_box_append(GTK_BOX(chat_widget->content), label); - free(label_content); - -} - -static void to_chat(chat_t* chat) { - - chat_widget_t* chat_widget = (chat_widget_t*)chat->widget; - gtk_stack_set_visible_child(GTK_STACK(stack), chat_widget->page); - -} - -void gui_add_roster_item_widget(roster_item_t* item) { - - // print debug message - if (item->name) { - fprintf(stderr, "roster item: %s, %s, sub:%d\n", item->name, item->jid, - item->sub); - } else { - fprintf(stderr, "roster item: (no name), %s, sub:%d\n", item->jid, item->sub); - } - - // create widget for roster item - GtkWidget* roster_item_widget; - if (item->name == NULL || strcmp(item->name, "") == 0) { - roster_item_widget = gtk_button_new_with_label(item->jid); - } else { - roster_item_widget = gtk_button_new_with_label(item->name); - } - - // add roster item widget to roster page - gtk_box_append(GTK_BOX(roster_content_box), roster_item_widget); - g_signal_connect_swapped(roster_item_widget, "clicked", G_CALLBACK(to_chat), - item->chat); - -} @@ -6,9 +6,6 @@ #include <SDL2/SDL.h> #include <stdbool.h> -#include "net.h" -#include "data.h" - void gui_run(void) { @@ -66,23 +63,3 @@ void gui_run(void) { SDL_Quit(); } - -void gui_connected(char* jid, char* password) { - -} - -void gui_disconnected(void) { - -} - -void gui_add_roster_item_widget(roster_item_t* item) { - -} - -void gui_add_chat_widget(chat_t* chat) { - -} - -void gui_add_message_widget(message_t* message, chat_t* chat) { - -} @@ -1,13 +1 @@ -#ifndef GUI_H -#define GUI_H - -#include "data.h" - void gui_run(void); -void gui_connected(char* jid, char* password); -void gui_disconnected(void); -void gui_add_roster_item_widget(roster_item_t* item); -void gui_add_chat_widget(chat_t* chat); -void gui_add_message_widget(message_t* message, chat_t* chat); - -#endif @@ -5,17 +5,9 @@ #include <getopt.h> #include "gui.h" -#include "net.h" +#include "xmpp.h" -// error code definition -typedef enum error { - Ok = 0, - UnknownError = 1, - WrongArgs = 2, - GuiError = 3, -} Error; - // configuration struct for all command line options typedef struct options { bool unknown; @@ -23,28 +15,6 @@ typedef struct options { } Options; -// prototypes -static void get_opts(Options* opts, int argc, char* argv[]); -static void print_help(void); - - -int main(int argc, char* argv[]) { - - // parse command line options - Options opts; - get_opts(&opts, argc, argv); - - // handle correct use case - if (opts.unknown || opts.help) { - print_help(); - } else { - net_init(); - gui_run(); - net_quit(); - } - -} - // parses all command line arguments. static void get_opts(Options* opts, int argc, char* argv[]) { @@ -63,9 +33,7 @@ static void get_opts(Options* opts, int argc, char* argv[]) { default: opts->unknown = true; } - } - } // prints out the help page. @@ -81,3 +49,19 @@ static void print_help(void) { ); } + +int main(int argc, char* argv[]) { + + // parse command line options + Options opts; + get_opts(&opts, argc, argv); + + // handle correct use case + if (opts.unknown || opts.help) { + print_help(); + } else { + xmpp_connect(); + gui_run(); + } + +} diff --git a/meson.build b/meson.build index 8a21907..a675aa6 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,3 @@ project('LimoX', 'c') -gtkdep = dependency('gtk4') -strophedep = dependency('libstrophe') sdl2dep = dependency('sdl2') -executable('limox', ['main.c', 'gtk.c', 'strophe.c', 'data.c'], dependencies : [gtkdep, strophedep]) -executable('limox_sdl2', ['main.c', 'sdl2.c', 'xmpp.c', 'data.c'], dependencies : [sdl2dep]) +executable('limox', ['main.c', 'gui.c', 'xmpp.c'], dependencies : [sdl2dep]) @@ -1,14 +0,0 @@ -#ifndef NET_H -#define NET_H - -// life cycle / event loop related functions -void net_init(void); // initialize net -void net_run_once(void); // process event loop for a short amount of time -void net_quit(void); // disconnect and clean up - -// interface for the GUI implementation -void net_connect(const char* jid, const char* password); -void net_disconnect(void); -void net_send_message(const char* sender, const char* content, - const char* recipient); -#endif diff --git a/strophe.c b/strophe.c deleted file mode 100644 index c037bc1..0000000 --- a/strophe.c +++ /dev/null @@ -1,228 +0,0 @@ - - -#include <time.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <signal.h> - -#include <strophe.h> - -#include "gui.h" -#include "net.h" -#include "data.h" - - -// the state of net -typedef enum { - DISCONNECTED, // initial state - CONNECTING, // connection was requested but is not yet established - CONNECTED, // there is an active XMPP connetion to the server - SUSPENDED // not connected, but there is an open XMPP stream to be - // reconnected (see XEP-0198 for details) -} net_state_t; - - -// these variables stay initialized for the whole runtime -static net_state_t state; -static xmpp_ctx_t* ctx; - -// these variables stay initialized while the application is not disconnected -static xmpp_conn_t* conn; -static long flags; - - -static int message_handler(xmpp_conn_t* conn, xmpp_stanza_t* stanza, - void* userdata) { - - xmpp_stanza_t* body = xmpp_stanza_get_child_by_name(stanza, "body"); - if (body == NULL) { - fprintf(stderr, "DEBUG: Got message stanza of type char without body!\n"); - return 1; - } - - const char* content = xmpp_stanza_get_text(body); - const char* sender = xmpp_stanza_get_from(stanza); - - data_add_incoming_message(sender, content); - - return 1; -} - -static int roster_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, - void *userdata) { - - // iterate over roster result - xmpp_stanza_t* query = xmpp_stanza_get_child_by_name(stanza, "query"); - for (xmpp_stanza_t* item = xmpp_stanza_get_children(query); item; - item = xmpp_stanza_get_next(item)) { - - data_add_roster_item(xmpp_stanza_get_attribute(item, "jid"), - xmpp_stanza_get_attribute(item, "subscription"), - xmpp_stanza_get_attribute(item, "name")); - } - - return 1; -} - -static void conn_handler(xmpp_conn_t* conn, xmpp_conn_event_t status,int error, - xmpp_stream_error_t* stream_error, void* userdata) { - - // FIXME handle error and stream_error (this could be a major bug) - // this implements at least debugging: - if (error != 0) { - fprintf(stderr, "conn_handler got error=%d\n", error); - } - if (stream_error != NULL) { - fprintf(stderr, "conn_handler got stream_error of type %d\n", - stream_error->type); - } - - switch (status) { - - case XMPP_CONN_CONNECT: - fprintf(stderr, "DEBUG: Got XMPP_CONN_CONNECT\n"); - - // add handler for <message> stanzas - xmpp_handler_add(conn, message_handler, NULL, "message", "chat", - NULL); - - // add handler for roster response - xmpp_handler_add(conn, roster_handler, "jabber:iq:roster", "iq", - "result", NULL); - - // send initial presence - xmpp_stanza_t* presence; - presence = xmpp_presence_new(ctx); - xmpp_send(conn, presence); - xmpp_stanza_release(presence); - - // send roster request - xmpp_stanza_t* iq = xmpp_iq_new(ctx, "get", "initial_roster"); - xmpp_stanza_t* query = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(query, "query"); - xmpp_stanza_set_ns(query, XMPP_NS_ROSTER); - xmpp_stanza_add_child(iq, query); - xmpp_stanza_release(query); - xmpp_send(conn, iq); - xmpp_stanza_release(iq); - - break; - - case XMPP_CONN_RAW_CONNECT: - fprintf(stderr, "DEBUG: Got XMPP_CONN_RAW_CONNECT\n"); - break; - - case XMPP_CONN_DISCONNECT: - fprintf(stderr, "DEBUG: Got XMPP_CONN_DISCONNECT\n"); - break; - - case XMPP_CONN_FAIL: - fprintf(stderr, "DEBUG: Got XMPP_CONN_FAIL\n"); - break; - - default: - fprintf(stderr, "DEBUG: Got unknown connection status '%d'!\n", status); - exit(1); - } -} - -void net_init(void) { - - fprintf(stderr, "net_init()\n"); - - xmpp_initialize(); - ctx = xmpp_ctx_new(NULL, xmpp_get_default_logger(XMPP_LEVEL_DEBUG)); - - state = DISCONNECTED; - -} - -static void delay(unsigned long int micros) { - - unsigned long int now = clock(); - while ((clock() - now) < micros) {} - -} - -void net_run_once(void) { - - if (state == CONNECTING) { - if (xmpp_connect_client(conn, NULL, 0, conn_handler, NULL) - == XMPP_EOK) { - state = CONNECTED; - } else { - net_disconnect(); - } - } - - if (state != DISCONNECTED) { - xmpp_run_once(ctx, 200); - } - - // TODO This delay is useless. But without it this function gets only - // called once by GTK. This could be a GTK bug. - delay(10000); - -} - -void net_quit(void) { - - fprintf(stderr, "net_quit()\n"); - - net_disconnect(); - xmpp_shutdown(); - -} - -void net_connect(const char* jid, const char* password) { - - fprintf(stderr, "net_connect()\n"); - - conn = xmpp_conn_new(ctx); - - flags = 0; - flags |= XMPP_CONN_FLAG_MANDATORY_TLS; - xmpp_conn_set_flags(conn, flags); - - xmpp_conn_set_jid(conn, jid); - xmpp_conn_set_pass(conn, password); - - state = CONNECTING; - -} - -void net_disconnect(void) { - - fprintf(stderr, "net_disconnect()\n"); - - if (conn != NULL && xmpp_conn_is_connected(conn)) { - xmpp_disconnect(conn); - while (xmpp_conn_is_connected(conn)) { - xmpp_run_once(ctx, 200); // TODO avoid with additional - // state "disconnecting" - } - if (!xmpp_conn_release(conn)) { - fprintf(stderr, "DEBUG: Could not free connection!\n"); - } - } - - flags = 0; - state = DISCONNECTED; - -} - -void net_send_message(const char* sender, const char* content, - const char* recipient) { - - if (xmpp_conn_is_connected(conn)) { - char* uuid = xmpp_uuid_gen(ctx); - xmpp_stanza_t* msg = xmpp_message_new(ctx, "chat", recipient, uuid); - xmpp_stanza_set_from(msg, sender); - xmpp_message_set_body(msg, content); - xmpp_send(conn, msg); - } else { - fprintf(stderr, "net_send_message() called while not connected\n"); - } - -} @@ -30,14 +30,51 @@ char *domainpart(char *jid) } } - char* retval = (char *)malloc((stop-start+1) * sizeof(char)); - memcpy(retval, jid+start, (stop-start)*sizeof(char)); + char *retval = (char *)malloc((stop-start+1) * sizeof(char)); + memcpy(retval, jid+start, (stop-start) * sizeof(char)); retval[stop] = '\0'; return retval; } -void net_init(void) +/* + * Return the preferred struct addrinfo * or NULL + * + * This handles DNS resolution. It returns the first valid addrinfo which is + * returned by getaddrinfo. Mind that the addrinfo could use IPv6 instead of + * IPv4. + */ +struct addrinfo *get_addrinfo(char *domain) +{ + struct addrinfo hints; + struct addrinfo *servinfo; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(domain, "xmpp-client", &hints, &servinfo) != 0) { + printf("Failed to resolve hostname '%s'.\n", domain); + return NULL; + } + struct addrinfo *p; + for(p=servinfo; p!=NULL; p=p->ai_next) { + if (p->ai_family == AF_INET) { + printf("an IPv4!\n"); // TODO + + } else if (p->ai_family == AF_INET6) { + printf("an IPv6!\n"); // TODO + } else { + printf("Unknown addrinfo address type.\n"); + } + } + return NULL; +} + +/* + * Initialize the network connection to the XMPP server + * + * TODO: Error handling is missing. + */ +void xmpp_connect(void) { printf("net_init()\n"); @@ -46,9 +83,6 @@ void net_init(void) char *domain = domainpart(user_str); printf("Trying to connect as '%s' with '%s'.\n", user_str, pwd_str); printf("Domainpart is '%s'.\n", domain); -} -void net_quit(void) -{ - printf("net_quit()\n"); + get_addrinfo(domain); } @@ -0,0 +1 @@ +void xmpp_connect(void); |