diff options
| author | xengineering <me@xengineering.eu> | 2022-11-27 18:53:12 +0100 | 
|---|---|---|
| committer | xengineering <me@xengineering.eu> | 2022-11-27 19:07:49 +0100 | 
| commit | 2efc9022bf064136bb7cd25bd59971f2b419ff48 (patch) | |
| tree | 87ea72030a3bb28f1ddba1675482a9346c5df814 | |
| parent | 8b9ce1d7848bd8ad417a3834d11e1b00c028f8eb (diff) | |
| download | limox-2efc9022bf064136bb7cd25bd59971f2b419ff48.tar limox-2efc9022bf064136bb7cd25bd59971f2b419ff48.tar.zst limox-2efc9022bf064136bb7cd25bd59971f2b419ff48.zip | |
Switch completely to SDL2 version
The now legacy version of LimoX with GTK4 and libstrophe is now not part
of the main branch anymore. There might be a legacy branch keeping this
version at the time of reading.
This cut of implemented functionality is motivated by these reasons:
- Implementing XMPP is fun, educative and gives full control.
- Low level graphics with SDL2 is portable, fast, educative an mature.
- I do not have to use GLib and a crazy event loop anymore (run and hide)
| -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); | 
