summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/basic.c232
-rw-r--r--examples/weechat.c355
2 files changed, 406 insertions, 181 deletions
diff --git a/examples/basic.c b/examples/basic.c
index cbc9f4f..5df4f65 100644
--- a/examples/basic.c
+++ b/examples/basic.c
@@ -1,13 +1,30 @@
+/**
+ @file basic.c
+ @brief A reference rexmpp-based client.
+ @author defanor <defanor@uberspace.net>
+ @date 2020--2021
+ @copyright MIT license.
+*/
+
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
-#include <gnutls/gnutls.h>
#include <gsasl.h>
-
+#include <time.h>
+#include <stdlib.h>
#include <rexmpp.h>
+#include <rexmpp_xml.h>
+#include <rexmpp_sasl.h>
+
+int log_level = 8;
+/* A logger callback. This one just prints all the logs into
+ stderr. */
void my_logger (rexmpp_t *s, int priority, const char *fmt, va_list args) {
+ if (priority >= log_level) {
+ return;
+ }
char *priority_str = "unknown";
switch (priority) {
case LOG_EMERG: priority_str = "emerg"; break;
@@ -24,82 +41,139 @@ void my_logger (rexmpp_t *s, int priority, const char *fmt, va_list args) {
fprintf(stderr, "\n");
}
-int my_sasl_property_cb (rexmpp_t *s, Gsasl_property prop) {
- if (prop == GSASL_PASSWORD) {
- char buf[4096];
+/* A SASL property callback, used to retrieve credentials. This one
+ just asks user for a password and provides AUTHID based on the
+ initial JID. */
+int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
+ if (prop == REXMPP_SASL_PROP_PASSWORD) {
+ char *buf = NULL;
+ size_t buf_len = 4096;
printf("password: ");
- gets(buf);
- gsasl_property_set (s->sasl_session, GSASL_PASSWORD, buf);
- return GSASL_OK;
- }
- if (prop == GSASL_AUTHID) {
- char *domainpart = strchr(s->initial_jid, '@');
- if (domainpart != NULL) {
- int localpart_len = domainpart - s->initial_jid;
- char *localpart = malloc(localpart_len + 1);
- localpart[localpart_len] = 0;
- strncpy(localpart, s->initial_jid, localpart_len);
- gsasl_property_set (s->sasl_session, GSASL_AUTHID, localpart);
- free(localpart);
- return GSASL_OK;
+ getline(&buf, &buf_len, stdin);
+ if (buf != NULL) {
+ if (buf[strlen(buf) - 1] == '\n') {
+ buf[strlen(buf) - 1] = '\0';
+ }
+ rexmpp_sasl_property_set (s, REXMPP_SASL_PROP_PASSWORD, buf);
+ free(buf);
}
+ return 0;
+ }
+ if (prop == REXMPP_SASL_PROP_AUTHID) {
+ rexmpp_sasl_property_set (s, REXMPP_SASL_PROP_AUTHID, s->initial_jid.local);
+ return 0;
}
- printf("unhandled gsasl property: %d\n", prop);
- return GSASL_NO_CALLBACK;
+ printf("unhandled SASL property: %d\n", prop);
+ return -1;
}
-int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+/* An XML in callback, printing what was received. */
+int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("recv: %s\n", xml_buf);
free(xml_buf);
return 0;
}
-int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+/* An XML out callback, printing what is about to be sent. */
+int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("send: %s\n", xml_buf);
free(xml_buf);
return 0;
}
-main (int argc, char **argv) {
+int my_console_print_cb (rexmpp_t *s, const char *fmt, va_list args) {
+ vprintf(fmt, args);
+ return 0;
+}
+
+/* void my_socket_options(rexmpp_t *s, int sock) { */
+/* int pmtudisc = IP_PMTUDISC_WANT; */
+/* setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &pmtudisc, sizeof(pmtudisc)); */
+/* } */
+
+void print_help (char *prog_name) {
+ printf("Usage: %s [options] <jid>\n" \
+ "Options:\n" \
+ "-c\tenable textual console\n" \
+ "-x\tenable XML console\n" \
+ "-l <n>\tset log level (0 to disable, 8 to print everything)\n"
+ "-h\tprint this help message\n"
+ , prog_name);
+}
+
+int main (int argc, char **argv) {
+ int c, xml_console = 0, txt_console = 0, log = 0;
+ if (argc < 2) {
+ print_help(argv[0]);
+ return -1;
+ }
+ while ((c = getopt (argc, argv, "xchl:")) != -1) {
+ if (c == 'x') {
+ xml_console = 1;
+ } else if (c == 'c') {
+ txt_console = 1;
+ } else if (c == 'l') {
+ log_level = atoi(optarg);
+ } else if (c == 'h') {
+ print_help(argv[0]);
+ return 0;
+ }
+ }
+
+ /* The minimal initialisation: provide an allocated rexmpp_t
+ structure and an initial jid. */
rexmpp_t s;
rexmpp_err_t err;
- if (argc != 2) {
- printf("Usage: %s <jid>", argv[0]);
+ err = rexmpp_init(&s, argv[argc - 1], my_logger);
+ if (err != REXMPP_SUCCESS) {
+ puts("Failed to initialise rexmpp.");
return -1;
}
- err = rexmpp_init(&s, argv[1]);
- s.log_function = my_logger;
+ /* Set the primary callback functions: for console, SASL, XML in and
+ out. */
+ if (txt_console) {
+ s.console_print_cb = my_console_print_cb;
+ }
s.sasl_property_cb = my_sasl_property_cb;
- s.xml_in_cb = my_xml_in_cb;
- s.xml_out_cb = my_xml_out_cb;
- if (err != REXMPP_SUCCESS) {
- puts("error");
- return -1;
+ if (xml_console) {
+ s.xml_in_cb = my_xml_in_cb;
+ s.xml_out_cb = my_xml_out_cb;
}
- /* gnutls_certificate_set_x509_key_file(s.gnutls_cred, */
- /* "cert.pem", */
- /* "key.pem", */
- /* GNUTLS_X509_FMT_PEM); */
- fd_set read_fds, write_fds;
- int nfds;
- struct timeval tv;
- struct timeval *mtv;
- int n = 0;
+ /* Could set a client certificate for SASL EXTERNAL authentication
+ and Jingle's DTLS here. */
+ s.x509_key_file = "client.key";
+ s.x509_cert_file = "client.crt";
+
+ /* Could also set various other things manually. */
/* s.socks_host = "127.0.0.1"; */
/* s.socks_port = 4321; */
- /* s.manual_host = "foo.custom"; */
- /* gnutls_certificate_set_x509_trust_file(s.gnutls_cred, */
- /* "foo.custom.crt", */
- /* GNUTLS_X509_FMT_PEM); */
-
+ /* s.manual_host = "localhost"; */
+ s.local_address = "192.168.1.8";
+ /* rexmpp_tls_set_x509_trust_file(&s, "localhost.crt"); */
+ /* rexmpp_openpgp_set_home_dir(&s, "pgp"); */
s.roster_cache_file = "roster.xml";
+ /* s.tls_policy = REXMPP_TLS_AVOID; */
+ /* s.socket_cb = my_socket_options; */
+
+ /* Once the main structure is initialised and everything is
+ sufficiently configured, we are ready to run the main loop and
+ call rexmpp from it. */
+
+ fd_set read_fds, write_fds;
+ int nfds;
+ struct timespec tv;
+ struct timespec *mtv;
+ struct timeval tv_ms;
+ struct timeval *mtv_ms;
+ int n = 0;
do {
+ /* Check if we have some user input. */
if (n > 0 && FD_ISSET(STDIN_FILENO, &read_fds)) {
char input[4097];
ssize_t input_len;
@@ -109,45 +183,32 @@ main (int argc, char **argv) {
} else {
input[input_len - 1] = '\0';
if (strlen(input) != 0) {
- if (input[0] == '<') {
- xmlDocPtr doc = xmlReadMemory(input, input_len, "", "utf-8", 0);
- if (doc != NULL) {
- xmlNodePtr node = xmlDocGetRootElement(doc);
- if (node != NULL) {
- xmlUnlinkNode(node);
- rexmpp_send(&s, node);
- } else {
- puts("No root node");
- }
- xmlFreeDoc(doc);
+ if (input[0] == '<' && xml_console) {
+ /* Raw XML input. */
+ rexmpp_xml_t *node = rexmpp_xml_parse(input, input_len);
+ if (node != NULL) {
+ rexmpp_send(&s, node);
} else {
- puts("Failed to read a document");
+ puts("Failed to parse XML");
}
- } else if (strcmp(input, ".") == 0) {
- rexmpp_stop(&s);
- } else if (strcmp(input, "connerr") == 0) {
- close(s.server_socket);
- s.server_socket = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
- gnutls_transport_set_int(s.gnutls_session, s.server_socket);
- } else {
- xmlNodePtr msg = rexmpp_xml_add_id(&s, xmlNewNode(NULL, "message"));
- xmlNewProp(msg, "to", "test2@foo.custom");
- xmlNewProp(msg, "type", "chat");
- xmlNewTextChild(msg, NULL, "body", input);
- rexmpp_send(&s, msg);
+ } else if (txt_console) {
+ rexmpp_console_feed(&s, input, input_len);
}
}
}
}
+
+ /* Run a single rexmpp iteration. */
err = rexmpp_run(&s, &read_fds, &write_fds);
if (err == REXMPP_SUCCESS) {
puts("done");
break;
}
if (err != REXMPP_E_AGAIN) {
- puts("error");
+ printf("error: %s\n", rexmpp_strerror(err));
break;
}
+ /* Could inspect the state here. */
/* printf("res %d / conn %d / tls %d / sasl %d / stream %d / carbons %d\n", */
/* s.resolver_state, */
/* s.tcp_state, */
@@ -155,18 +216,35 @@ main (int argc, char **argv) {
/* s.sasl_state, */
/* s.stream_state, */
/* s.carbons_state); */
+
+ /* Ask rexmpp which file descriptors it is interested in, and what
+ the timeouts should be. */
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
-
nfds = rexmpp_fds(&s, &read_fds, &write_fds);
- mtv = rexmpp_timeout(&s, NULL, (struct timeval*)&tv);
+ mtv = rexmpp_timeout(&s, NULL, &tv);
+ mtv_ms = NULL;
+ if (mtv != NULL) {
+ tv_ms.tv_sec = mtv->tv_sec;
+ tv_ms.tv_usec = mtv->tv_nsec / 1000;
+ mtv_ms = &tv_ms;
+ }
+ /* Add other file descriptors we are interested in, particularly
+ stdin for user input. */
FD_SET(STDIN_FILENO, &read_fds);
- n = select(nfds, &read_fds, &write_fds, NULL, mtv);
+
+ /* Run select(2) with all those file descriptors and timeouts,
+ waiting for either user input or some rexmpp event to occur. */
+ n = select(nfds, &read_fds, &write_fds, NULL, mtv_ms);
if (n == -1) {
printf("select error: %s\n", strerror(errno));
break;
}
} while (1);
+
+ /* Deinitialise the rexmpp structure in the end, freeing whatever it
+ allocated. */
rexmpp_done(&s);
+ return 0;
}
diff --git a/examples/weechat.c b/examples/weechat.c
index 0c26f6e..cd89adb 100644
--- a/examples/weechat.c
+++ b/examples/weechat.c
@@ -1,11 +1,36 @@
-/* This is quite messy and should be refactored, but good enough for
- testing. */
+/*
+ This is quite messy and should be refactored, but good enough for
+ testing.
+
+ Building:
+
+ gcc -fPIC -Wall -Wno-pointer-sign -c `pkg-config --cflags --libs weechat libgsasl libxml-2.0 gnutls rexmpp` examples/weechat.c
+ gcc `pkg-config --cflags --libs weechat libgsasl libxml-2.0 gnutls rexmpp` -shared -fPIC -o weechat.so weechat.o
+ mv weechat.so ~/.weechat/plugins/rexmpp.so
+
+ Usage:
+
+ Connect: /xmpp <jid> <password>
+ Open a chat buffer (from the server buffer): q <jid>
+ Join a conference (from the server buffer): j <room>@<server>/<nick>
+
+ TODO:
+
+ - Refine/rethink control/commands.
+ - Add settings (SASL parameters and regular rexmpp configuration).
+ - Add commands for roster management and other functionality.
+ - Refactor the hacky bits of this plugin.
+*/
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
+#include <libxml/tree.h>
#include "weechat-plugin.h"
#include "rexmpp.h"
+#include "rexmpp_roster.h"
+#include "rexmpp_jid.h"
+#include "rexmpp_openpgp.h"
WEECHAT_PLUGIN_NAME("rexmpp");
WEECHAT_PLUGIN_DESCRIPTION("XMPP plugin using librexmpp");
@@ -22,16 +47,17 @@ struct weechat_rexmpp {
struct weechat_rexmpp_muc {
struct weechat_rexmpp *wr;
- char *jid;
+ struct rexmpp_jid jid;
};
struct t_weechat_plugin *weechat_plugin = NULL;
-void my_logger (const struct weechat_rexmpp *wr,
+void my_logger (rexmpp_t *s,
int priority,
const char *fmt,
va_list args)
{
+ struct weechat_rexmpp *wr = (struct weechat_rexmpp *)s;
char *priority_str = "unknown";
switch (priority) {
case LOG_EMERG: priority_str = "emerg"; break;
@@ -50,143 +76,217 @@ void my_logger (const struct weechat_rexmpp *wr,
weechat_printf(wr->server_buffer, "%s\n", buf);
}
-int my_sasl_property_cb (const struct weechat_rexmpp *wr, Gsasl_property prop) {
- rexmpp_t *s = &wr->rexmpp_state;
+int my_sasl_property_cb (rexmpp_t *s, Gsasl_property prop) {
+ struct weechat_rexmpp *wr = (struct weechat_rexmpp *)s;
if (prop == GSASL_PASSWORD) {
gsasl_property_set (s->sasl_session, GSASL_PASSWORD, wr->password);
return GSASL_OK;
}
if (prop == GSASL_AUTHID) {
- char *domainpart = strchr(s->initial_jid, '@');
- if (domainpart != NULL) {
- int localpart_len = domainpart - s->initial_jid;
- char *localpart = malloc(localpart_len + 1);
- localpart[localpart_len] = 0;
- strncpy(localpart, s->initial_jid, localpart_len);
- gsasl_property_set (s->sasl_session, GSASL_AUTHID, localpart);
- free(localpart);
- return GSASL_OK;
- }
+ gsasl_property_set (s->sasl_session, GSASL_AUTHID, s->initial_jid.local);
+ return GSASL_OK;
}
weechat_printf(wr->server_buffer, "unhandled gsasl property: %d\n", prop);
return GSASL_NO_CALLBACK;
}
-int query_input_cb (const struct weechat_rexmpp *wr, void *data,
+int query_input_cb (const void *ptr, void *data,
struct t_gui_buffer *buffer, const char *input_data)
{
+ struct weechat_rexmpp *wr = (void*)ptr;
rexmpp_t *s = &wr->rexmpp_state;
- char *to = weechat_buffer_get_string(buffer, "name");
+ const char *to = weechat_buffer_get_string(buffer, "name");
xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
xmlNewProp(msg, "to", to);
xmlNewProp(msg, "type", "chat");
xmlNewTextChild(msg, NULL, "body", input_data);
rexmpp_send(s, msg);
- weechat_printf_date_tags(buffer, 0, "self_msg", "%s\t%s\n", s->assigned_jid, input_data);
+ weechat_printf_date_tags(buffer, 0, "self_msg", "%s\t%s\n", ">", input_data);
return WEECHAT_RC_OK;
}
-int query_close_cb (struct weechat_rexmpp *wr, void *data,
+int query_close_cb (const void *ptr, void *data,
struct t_gui_buffer *buffer)
{
+ /* struct weechat_rexmpp *wr = (void*)ptr; */
return WEECHAT_RC_OK;
}
-int muc_input_cb (const struct weechat_rexmpp_muc *wrm, void *data,
+int muc_input_cb (const void *ptr, void *data,
struct t_gui_buffer *buffer, const char *input_data)
{
+ struct weechat_rexmpp_muc *wrm = (void*)ptr;
rexmpp_t *s = &wrm->wr->rexmpp_state;
- char *to = weechat_buffer_get_string(buffer, "name");
xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
- xmlNewProp(msg, "to", to);
+ xmlNewProp(msg, "to", wrm->jid.bare);
xmlNewProp(msg, "type", "groupchat");
xmlNewTextChild(msg, NULL, "body", input_data);
rexmpp_send(s, msg);
return WEECHAT_RC_OK;
}
-int muc_close_cb (struct weechat_rexmpp_muc *wrm, void *data,
+int muc_close_cb (const void *ptr, void *data,
struct t_gui_buffer *buffer)
{
+ struct weechat_rexmpp_muc *wrm = (void*)ptr;
rexmpp_t *s = &wrm->wr->rexmpp_state;
xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid);
- xmlNewProp(presence, "to", wrm->jid);
+ xmlNewProp(presence, "from", s->assigned_jid.full);
+ xmlNewProp(presence, "to", wrm->jid.full);
xmlNewProp(presence, "type", "unavailable");
rexmpp_send(s, presence);
free(wrm);
return WEECHAT_RC_OK;
}
-int my_xml_in_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
+void display_message (struct t_gui_buffer *buf,
+ const char *display_name,
+ xmlNodePtr body)
+{
+ xmlChar *str = xmlNodeGetContent(body);
+ if (str != NULL) {
+ char tags[4096];
+ snprintf(tags, 4096, "nick_%s", display_name);
+ weechat_printf_date_tags(buf, 0, tags, "%s\t%s\n", display_name, str);
+ xmlFree(str);
+ }
+}
+
+int my_xml_in_cb (rexmpp_t *s, xmlNodePtr node) {
+ struct weechat_rexmpp *wr = (struct weechat_rexmpp *)s;
char *xml_buf = rexmpp_xml_serialize(node);
weechat_printf(wr->server_buffer, "recv: %s\n", xml_buf);
/* free(xml_buf); */
if (rexmpp_xml_match(node, "jabber:client", "message")) {
char *from = xmlGetProp(node, "from");
- char *display_name = from;
- int i, resource_removed = 0;
- for (i = 0; i < strlen(from); i++) {
- if (from[i] == '/') {
- from[i] = 0;
- display_name = from + i + 1;
- resource_removed = 1;
- break;
- }
- }
if (from != NULL) {
- struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from);
+ struct rexmpp_jid from_jid;
+ rexmpp_jid_parse(from, &from_jid);
+ xmlFree(from);
+ char *display_name = from_jid.full;
+ if (from_jid.resource[0]) {
+ display_name = from_jid.resource;
+ }
+ struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from_jid.bare);
if (buf == NULL) {
- buf = weechat_buffer_new (from,
+ buf = weechat_buffer_new (from_jid.bare,
&query_input_cb, wr, NULL,
&query_close_cb, wr, NULL);
weechat_buffer_set(buf, "nicklist", "1");
}
- if (resource_removed) {
- from[i] = '/'; /* restore */
- }
xmlNodePtr body = rexmpp_xml_find_child(node, "jabber:client", "body");
- if (body != NULL) {
- xmlChar *str = xmlNodeGetContent(body);
- if (str != NULL) {
- char tags[4096];
- snprintf(tags, 4096, "nick_%s", display_name);
- weechat_printf_date_tags(buf, 0, tags, "%s\t%s\n", display_name, str);
- xmlFree(str);
+
+ xmlNodePtr openpgp = rexmpp_xml_find_child(node, "urn:xmpp:openpgp:0", "openpgp");
+ if (openpgp != NULL) {
+ int valid;
+ xmlNodePtr elem = rexmpp_openpgp_decrypt_verify_message(s, node, &valid);
+ if (! valid) {
+ weechat_printf(buf, "An invalid OpenPGP message!");
+ }
+
+ if (elem != NULL) {
+ xmlNodePtr payload =
+ rexmpp_xml_find_child(elem, "urn:xmpp:openpgp:0", "payload");
+ if (payload != NULL) {
+ xmlNodePtr pl_body =
+ rexmpp_xml_find_child(payload, "jabber:client", "body");
+ if (pl_body != NULL) {
+ display_message(buf, display_name, pl_body);
+ body = NULL;
+ }
+ }
+ xmlFreeNode(elem);
}
}
- xmlFree(from);
+
+ if (body != NULL) {
+ display_message(buf, display_name, body);
+ }
}
}
if (rexmpp_xml_match(node, "jabber:client", "presence")) {
char *presence_type = xmlGetProp(node, "type");
- char *jid = xmlGetProp(node, "from");
- int i;
- char *resource = "";
- for (i = 0; i < strlen(jid); i++) {
- if (jid[i] == '/') {
- jid[i] = 0;
- resource = jid + i + 1;
- break;
+ char *from = xmlGetProp(node, "from");
+ struct rexmpp_jid from_jid;
+ rexmpp_jid_parse(from, &from_jid);
+ xmlFree(from);
+
+ xmlNodePtr muc =
+ rexmpp_xml_find_child(node, "http://jabber.org/protocol/muc#user", "x");
+ if (muc != NULL) {
+
+ /* Handle newly joined MUCs */
+ if (presence_type == NULL) {
+ xmlNodePtr status =
+ rexmpp_xml_find_child(muc, "http://jabber.org/protocol/muc#user",
+ "status");
+ if (status != NULL) {
+ char *code = xmlGetProp(status, "code");
+ if (code != NULL) {
+ if (strcmp(code, "110") == 0) {
+ struct weechat_rexmpp_muc *wrm =
+ malloc(sizeof(struct weechat_rexmpp_muc));
+ wrm->wr = wr;
+ rexmpp_jid_parse(from_jid.full, &(wrm->jid));
+ struct t_gui_buffer *buf =
+ weechat_buffer_search("rexmpp", wrm->jid.bare);
+ if (buf == NULL) {
+ buf = weechat_buffer_new (wrm->jid.bare,
+ &muc_input_cb, wrm, NULL,
+ &muc_close_cb, wrm, NULL);
+ weechat_buffer_set(buf, "nicklist", "1");
+ }
+ }
+ free(code);
+ }
+ }
}
- }
- if (rexmpp_xml_find_child(node, "http://jabber.org/protocol/muc#user", "x")) {
+
/* Update MUC nicklist */
- struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", jid);
+ struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", from_jid.bare);
if (buf != NULL) {
if (presence_type != NULL && strcmp(presence_type, "unavailable") == 0) {
struct t_gui_nick *nick =
- weechat_nicklist_search_nick(buf, NULL, resource);
+ weechat_nicklist_search_nick(buf, NULL, from_jid.resource);
if (nick != NULL) {
weechat_nicklist_remove_nick(buf, nick);
}
} else {
- weechat_nicklist_add_nick(buf, NULL, resource,
+ weechat_nicklist_add_nick(buf, NULL, from_jid.resource,
"bar_fg", "", "lightgreen", 1);
}
}
+ } else if (rexmpp_roster_find_item(s, from_jid.bare, NULL) != NULL) {
+ /* A roster item. */
+ struct t_gui_nick *nick = weechat_nicklist_search_nick(wr->server_buffer, NULL, from_jid.bare);
+ if (presence_type == NULL) {
+ /* An "available" presence: just ensure that it's shown as
+ online. */
+ weechat_nicklist_nick_set(wr->server_buffer, nick, "prefix", "+");
+ } else if (strcmp(presence_type, "unavailable") == 0) {
+ /* An "unavailable" presence: set it to "offline" if there's
+ no remaining online resources (i.e., if we can find an
+ online resource for this bare JID other than the one that
+ just went offline). */
+ xmlNodePtr cur;
+ int found = 0;
+ struct rexmpp_jid cur_from_jid;
+ for (cur = s->roster_presence;
+ cur != NULL;
+ cur = xmlNextElementSibling(cur)) {
+ char *cur_from = xmlGetProp(cur, "from");
+ rexmpp_jid_parse(cur_from, &cur_from_jid);
+ xmlFree(cur_from);
+ if (strcmp(cur_from_jid.bare, from_jid.bare) == 0 &&
+ strcmp(cur_from_jid.resource, from_jid.resource) != 0) {
+ found = 1;
+ }
+ }
+ if (! found) {
+ weechat_nicklist_nick_set(wr->server_buffer, nick, "prefix", "");
+ }
+ }
}
- free(jid);
if (presence_type != NULL) {
free(presence_type);
}
@@ -195,17 +295,25 @@ int my_xml_in_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
return 0;
}
-int my_xml_out_cb (struct weechat_rexmpp *wr, xmlNodePtr node) {
+int my_xml_out_cb (rexmpp_t *s, xmlNodePtr node) {
+ struct weechat_rexmpp *wr = (struct weechat_rexmpp *)s;
char *xml_buf = rexmpp_xml_serialize(node);
weechat_printf(wr->server_buffer, "send: %s\n", xml_buf);
free(xml_buf);
return 0;
}
+void my_console_print_cb (struct weechat_rexmpp *wr, const char *fmt, va_list args) {
+ char str[4096];
+ vsnprintf(str, 4096, fmt, args);
+ weechat_printf(wr->server_buffer, "%s", str);
+}
+
int
-my_input_cb (const struct weechat_rexmpp *wr, void *data,
+my_input_cb (const void *ptr, void *data,
struct t_gui_buffer *buffer, const char *input_data)
{
+ struct weechat_rexmpp *wr = (void*)ptr;
rexmpp_t *s = &wr->rexmpp_state;
if (input_data[0] == '<') {
xmlDocPtr doc = xmlReadMemory(input_data, strlen(input_data), "", "utf-8", 0);
@@ -222,7 +330,7 @@ my_input_cb (const struct weechat_rexmpp *wr, void *data,
weechat_printf(buffer, "Failed to read a document\n");
}
} else if (input_data[0] == 'q' && input_data[1] == ' ') {
- char *jid = input_data + 2;
+ const char *jid = input_data + 2;
struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", jid);
if (buf == NULL) {
buf = weechat_buffer_new (jid,
@@ -230,42 +338,22 @@ my_input_cb (const struct weechat_rexmpp *wr, void *data,
&query_close_cb, wr, NULL);
weechat_buffer_set(buf, "nicklist", "1");
}
- } else if (input_data[0] == 'j' && input_data[1] == ' ') {
- char *jid = input_data + 2;
- xmlNodePtr presence = rexmpp_xml_add_id(s, xmlNewNode(NULL, "presence"));
- xmlNewProp(presence, "from", s->assigned_jid);
- xmlNewProp(presence, "to", jid);
- xmlNodePtr x = xmlNewNode(NULL, "x");
- xmlNewNs(x, "http://jabber.org/protocol/muc", NULL);
- xmlAddChild(presence, x);
- rexmpp_send(s, presence);
- int i;
- struct weechat_rexmpp_muc *wrm = malloc(sizeof(struct weechat_rexmpp_muc));
- wrm->wr = wr;
- wrm->jid = strdup(jid);
- for (i = 0; i < strlen(jid); i++) {
- if (jid[i] == '/') {
- jid[i] = 0;
- break;
- }
- }
- struct t_gui_buffer *buf = weechat_buffer_search("rexmpp", jid);
- if (buf == NULL) {
- buf = weechat_buffer_new (jid,
- &muc_input_cb, wrm, NULL,
- &muc_close_cb, wrm, NULL);
- weechat_buffer_set(buf, "nicklist", "1");
- }
+ } else {
+ rexmpp_console_feed(s, input_data, strlen(input_data));
}
return WEECHAT_RC_OK;
}
-void my_roster_modify_cb (struct weechat_rexmpp *wr, xmlNodePtr item) {
+void my_roster_modify_cb (rexmpp_t *s, xmlNodePtr item) {
+ struct weechat_rexmpp *wr = (struct weechat_rexmpp *)s;
char *subscription = xmlGetProp(item, "subscription");
char *jid = xmlGetProp(item, "jid");
if (subscription != NULL && strcmp(subscription, "remove") == 0) {
/* delete */
- weechat_nicklist_remove_nick(wr->server_buffer, jid);
+ struct t_gui_nick *nick = weechat_nicklist_search_nick(wr->server_buffer, NULL, jid);
+ if (nick != NULL) {
+ weechat_nicklist_remove_nick(wr->server_buffer, nick);
+ }
} else {
/* add or modify */
weechat_nicklist_add_nick(wr->server_buffer, NULL, jid,
@@ -278,11 +366,12 @@ void my_roster_modify_cb (struct weechat_rexmpp *wr, xmlNodePtr item) {
}
int
-my_close_cb (struct weechat_rexmpp *wr, void *data, struct t_gui_buffer *buffer)
+my_close_cb (const void *ptr, void *data, struct t_gui_buffer *buffer)
{
/* todo: close MUC buffers first? or at least mark them somehow, so
that they won't attempt to send "unavailable" presence on
closing. */
+ struct weechat_rexmpp *wr = (void*)ptr;
wr->server_buffer = NULL;
rexmpp_stop(&wr->rexmpp_state);
return WEECHAT_RC_OK;
@@ -290,7 +379,8 @@ my_close_cb (struct weechat_rexmpp *wr, void *data, struct t_gui_buffer *buffer)
void iter (struct weechat_rexmpp *wr, fd_set *rfds, fd_set *wfds);
-int fd_read_cb (const struct weechat_rexmpp *wr, void *data, int fd) {
+int fd_read_cb (const void *ptr, void *data, int fd) {
+ struct weechat_rexmpp *wr = (void*)ptr;
/* weechat_printf(wr->server_buffer, "read hook fired"); */
fd_set read_fds, write_fds;
FD_ZERO(&read_fds);
@@ -300,7 +390,8 @@ int fd_read_cb (const struct weechat_rexmpp *wr, void *data, int fd) {
return 0;
}
-int fd_write_cb (const struct weechat_rexmpp *wr, void *data, int fd) {
+int fd_write_cb (const void *ptr, void *data, int fd) {
+ struct weechat_rexmpp *wr = (void*)ptr;
/* weechat_printf(wr->server_buffer, "write hook fired"); */
fd_set read_fds, write_fds;
FD_ZERO(&read_fds);
@@ -310,7 +401,8 @@ int fd_write_cb (const struct weechat_rexmpp *wr, void *data, int fd) {
return 0;
}
-int timer_cb (const struct weechat_rexmpp *wr, void *data, int remaining_calls) {
+int timer_cb (const void *ptr, void *data, int remaining_calls) {
+ struct weechat_rexmpp *wr = (void*)ptr;
/* weechat_printf(wr->server_buffer, "timer hook fired"); */
fd_set read_fds, write_fds;
FD_ZERO(&read_fds);
@@ -319,8 +411,8 @@ int timer_cb (const struct weechat_rexmpp *wr, void *data, int remaining_calls)
return 0;
}
-void hook_free_cb (void *data, struct t_arraylist *arraylist, struct t_hook *hook) {
- weechat_unhook(hook);
+void hook_free_cb (void *data, struct t_arraylist *arraylist, void *hook) {
+ weechat_unhook((struct t_hook *)hook);
}
void iter (struct weechat_rexmpp *wr, fd_set *rfds, fd_set *wfds) {
@@ -340,7 +432,7 @@ void iter (struct weechat_rexmpp *wr, fd_set *rfds, fd_set *wfds) {
return;
}
if (err != REXMPP_E_AGAIN) {
- weechat_printf(wr->server_buffer, "rexmpp error");
+ weechat_printf(wr->server_buffer, "rexmpp error: %s", rexmpp_strerror(err));
return;
}
fd_set read_fds, write_fds;
@@ -378,6 +470,54 @@ void iter (struct weechat_rexmpp *wr, fd_set *rfds, fd_set *wfds) {
}
int
+command_sc_cb (const void *wr_ptr, void *data,
+ struct t_gui_buffer *buffer,
+ int argc, char **argv, char **argv_eol)
+{
+ struct weechat_rexmpp *wr = (void*)wr_ptr;
+ rexmpp_t *s = &wr->rexmpp_state;
+ const char *to = weechat_buffer_get_string(buffer, "name");
+ xmlNodePtr body = xmlNewNode(NULL, "body");
+ xmlNewNs(body, "jabber:client", NULL);
+ xmlNodeAddContent(body, argv_eol[1]);
+
+ const char *rcpt[2];
+ rcpt[0] = to;
+ rcpt[1] = NULL;
+
+ char *b64 = rexmpp_openpgp_encrypt_sign(s, body, rcpt);
+ if (b64 == NULL) {
+ weechat_printf(buffer, "Failed to encrypt a message.");
+ return WEECHAT_RC_OK;
+ }
+
+ xmlNodePtr openpgp = xmlNewNode(NULL, "openpgp");
+ xmlNewNs(openpgp, "urn:xmpp:openpgp:0", NULL);
+ xmlNodeAddContent(openpgp, b64);
+ free(b64);
+
+ xmlNodePtr msg = rexmpp_xml_add_id(s, xmlNewNode(NULL, "message"));
+ xmlNewProp(msg, "to", to);
+ xmlNewProp(msg, "type", "chat");
+ xmlAddChild(msg, openpgp);
+
+ body = xmlNewNode(NULL, "body");
+ xmlNewNs(body, "jabber:client", NULL);
+ xmlNodeAddContent(body, "This is a secret message.");
+ xmlAddChild(msg, body);
+
+ /* XEP-0380: Explicit Message Encryption */
+ xmlNodePtr eme = xmlNewNode(NULL, "encryption");
+ xmlNewNs(eme, "urn:xmpp:eme:0", NULL);
+ xmlNewProp(eme, "namespace", "urn:xmpp:openpgp:0");
+ xmlAddChild(msg, eme);
+
+ rexmpp_send(s, msg);
+ weechat_printf_date_tags(buffer, 0, "self_msg", "%s\t%s\n", ">", argv_eol[1]);
+ return WEECHAT_RC_OK;
+}
+
+int
command_xmpp_cb (const void *pointer, void *data,
struct t_gui_buffer *buffer,
int argc, char **argv, char **argv_eol)
@@ -391,16 +531,23 @@ command_xmpp_cb (const void *pointer, void *data,
wr->password = strdup(argv[2]);
wr->hooks = weechat_arraylist_new(42, 0, 0, NULL, NULL, hook_free_cb, NULL);
rexmpp_t *s = &wr->rexmpp_state;
- rexmpp_init(s, argv[1]);
- s->log_function = my_logger;
+ rexmpp_init(s, argv[1], my_logger);
s->sasl_property_cb = my_sasl_property_cb;
s->xml_in_cb = my_xml_in_cb;
s->xml_out_cb = my_xml_out_cb;
s->roster_modify_cb = my_roster_modify_cb;
+ s->console_print_cb = my_console_print_cb;
fd_set read_fds, write_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
iter(wr, &read_fds, &write_fds);
+
+ weechat_hook_command ("sc",
+ "Sign and encrypt a message",
+ "<message>",
+ "message: a message to send",
+ NULL,
+ &command_sc_cb, wr, NULL);
}
return WEECHAT_RC_OK;
}