summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md50
-rw-r--r--data.c149
-rw-r--r--data.h40
-rw-r--r--gtk.c272
-rw-r--r--gui.c (renamed from sdl2.c)23
-rw-r--r--gui.h12
-rw-r--r--main.c50
-rw-r--r--meson.build5
-rw-r--r--net.h14
-rw-r--r--strophe.c228
-rw-r--r--xmpp.c48
-rw-r--r--xmpp.h1
12 files changed, 68 insertions, 824 deletions
diff --git a/README.md b/README.md
index a82b47d..ca58bbd 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/data.c b/data.c
deleted file mode 100644
index f19075c..0000000
--- a/data.c
+++ /dev/null
@@ -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);
-
-}
diff --git a/data.h b/data.h
deleted file mode 100644
index 68a4b9b..0000000
--- a/data.h
+++ /dev/null
@@ -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
diff --git a/gtk.c b/gtk.c
deleted file mode 100644
index af24a73..0000000
--- a/gtk.c
+++ /dev/null
@@ -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);
-
-}
diff --git a/sdl2.c b/gui.c
index e034446..04a53b3 100644
--- a/sdl2.c
+++ b/gui.c
@@ -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) {
-
-}
diff --git a/gui.h b/gui.h
index d4a91f4..24d4878 100644
--- a/gui.h
+++ b/gui.h
@@ -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
diff --git a/main.c b/main.c
index 016ea99..b86e911 100644
--- a/main.c
+++ b/main.c
@@ -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])
diff --git a/net.h b/net.h
deleted file mode 100644
index 3bd184a..0000000
--- a/net.h
+++ /dev/null
@@ -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");
- }
-
-}
diff --git a/xmpp.c b/xmpp.c
index 865b235..e1ab985 100644
--- a/xmpp.c
+++ b/xmpp.c
@@ -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);
}
diff --git a/xmpp.h b/xmpp.h
new file mode 100644
index 0000000..b07e3b9
--- /dev/null
+++ b/xmpp.h
@@ -0,0 +1 @@
+void xmpp_connect(void);