diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/basic.c | 232 | ||||
-rw-r--r-- | examples/weechat.c | 355 |
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; } |