/* register.c * strophe XMPP client library -- In-band registration (XEP-0077) * * Copyright (C) 2020 Dmitry Podgorny * * This software is provided AS-IS with no warranty, either express * or implied. * * This program is dual licensed under the MIT and GPLv3 licenses. */ /* * This example uses a "raw" connection to establish connection to a server * without account. Then it queries server to register new account according * to XEP-0077. * * How to use it. After the application connects and receives instructions * from the server, user will be prompted to type information such as * username, password, etc. Press enter without typing if you want to skip * a field and not to send it to the server. * * Notice, the example doesn't implement forms. Therefore, it won't work * in complicated scenarios. */ #include #include #include #include typedef struct { xmpp_ctx_t *ctx; const char *jid; } xmpp_reg_t; #define FEATURES_TIMEOUT 5000 /* 5 seconds */ static void iq_reg_send_form(xmpp_reg_t *reg, xmpp_conn_t *conn, xmpp_stanza_t *stanza) { xmpp_ctx_t *ctx = reg->ctx; xmpp_stanza_t *query; xmpp_stanza_t *next; xmpp_stanza_t *elem; xmpp_stanza_t *text; xmpp_stanza_t *iq; const char *name; size_t len; char buf[256]; char *s; query = xmpp_stanza_get_child_by_name(stanza, "query"); if (!query) { xmpp_disconnect(conn); return; } next = xmpp_stanza_get_children(query); query = xmpp_stanza_new(ctx); xmpp_stanza_set_name(query, "query"); xmpp_stanza_set_ns(query, XMPP_NS_REGISTER); while (next) { name = xmpp_stanza_get_name(next); if (name && strcmp(name, "instructions") == 0) { s = xmpp_stanza_get_text(next); printf("instructions: %s\n", s); xmpp_free(ctx, s); } else { printf("%s: ", name); s = fgets(buf, sizeof(buf), stdin); if (s != NULL) { len = strlen(s); if (len > 0 && s[len - 1] == '\n') { s[len - 1] = '\0'; --len; } if (len > 0) { elem = xmpp_stanza_new(ctx); text = xmpp_stanza_new(ctx); xmpp_stanza_set_text(text, s); xmpp_stanza_set_name(elem, name); xmpp_stanza_add_child(elem, text); xmpp_stanza_add_child(query, elem); xmpp_stanza_release(text); xmpp_stanza_release(elem); } } } next = xmpp_stanza_get_next(next); } if (xmpp_stanza_get_children(query) == NULL) { fprintf(stderr, "DEBUG: nothing to send\n"); xmpp_disconnect(conn); } else { iq = xmpp_iq_new(ctx, "set", "reg2"); xmpp_stanza_add_child(iq, query); xmpp_stanza_release(query); xmpp_send(conn, iq); xmpp_stanza_release(iq); } } static int iq_reg2_cb(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { const char *type; (void)userdata; type = xmpp_stanza_get_type(stanza); if (!type || strcmp(type, "error") == 0) { fprintf(stderr, "DEBUG: error during registration\n"); goto quit; } if (strcmp(type, "result") != 0) { fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type); goto quit; } fprintf(stderr, "DEBUG: successful registration\n"); quit: xmpp_disconnect(conn); return 0; } static int iq_reg_cb(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { xmpp_reg_t *reg = (xmpp_reg_t *)userdata; xmpp_stanza_t *registered = NULL; xmpp_stanza_t *query; const char *type; type = xmpp_stanza_get_type(stanza); if (!type || strcmp(type, "error") == 0) { fprintf(stderr, "DEBUG: error during registration\n"); xmpp_disconnect(conn); goto quit; } if (strcmp(type, "result") != 0) { fprintf(stderr, "DEBUG: expected type 'result', but got %s\n", type); xmpp_disconnect(conn); goto quit; } query = xmpp_stanza_get_child_by_name(stanza, "query"); if (query) registered = xmpp_stanza_get_child_by_name(query, "registered"); if (registered != NULL) { fprintf(stderr, "DEBUG: already registered\n"); xmpp_disconnect(conn); goto quit; } xmpp_id_handler_add(conn, iq_reg2_cb, "reg2", reg); iq_reg_send_form(reg, conn, stanza); quit: return 0; } static int _handle_error(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { (void)stanza; (void)userdata; fprintf(stderr, "DEBUG: received stream error\n"); xmpp_disconnect(conn); return 0; } static int _handle_proceedtls_default(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { const char *name = xmpp_stanza_get_name(stanza); (void)userdata; if (strcmp(name, "proceed") == 0) { fprintf(stderr, "DEBUG: proceeding with TLS\n"); if (xmpp_conn_tls_start(conn) == 0) { xmpp_handler_delete(conn, _handle_error); xmpp_conn_open_stream_default(conn); } else { fprintf(stderr, "DEBUG: TLS failed\n"); /* failed tls spoils the connection, so disconnect */ xmpp_disconnect(conn); } } return 0; } static int _handle_missing_features(xmpp_conn_t *const conn, void *const userdata) { (void)userdata; fprintf(stderr, "DEBUG: timeout\n"); xmpp_disconnect(conn); return 0; } static int _handle_features(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { xmpp_reg_t *reg = (xmpp_reg_t *)userdata; xmpp_ctx_t *ctx = reg->ctx; xmpp_stanza_t *child; xmpp_stanza_t *iq; char *domain; xmpp_timed_handler_delete(conn, _handle_missing_features); /* secure connection if possible */ child = xmpp_stanza_get_child_by_name(stanza, "starttls"); if (child && (strcmp(xmpp_stanza_get_ns(child), XMPP_NS_TLS) == 0)) { fprintf(stderr, "DEBUG: server supports TLS, try to establish\n"); child = xmpp_stanza_new(ctx); xmpp_stanza_set_name(child, "starttls"); xmpp_stanza_set_ns(child, XMPP_NS_TLS); xmpp_handler_add(conn, _handle_proceedtls_default, XMPP_NS_TLS, NULL, NULL, NULL); xmpp_send(conn, child); xmpp_stanza_release(child); return 0; } /* check whether server supports in-band registration */ child = xmpp_stanza_get_child_by_name(stanza, "register"); if (child && strcmp(xmpp_stanza_get_ns(child), XMPP_NS_REGISTER) == 0) { fprintf(stderr, "DEBUG: server doesn't support in-band registration\n"); xmpp_disconnect(conn); return 0; } fprintf(stderr, "DEBUG: server supports in-band registration\n"); domain = xmpp_jid_domain(ctx, reg->jid); iq = xmpp_iq_new(ctx, "get", "reg1"); xmpp_stanza_set_to(iq, domain); child = xmpp_stanza_new(ctx); xmpp_stanza_set_name(child, "query"); xmpp_stanza_set_ns(child, XMPP_NS_REGISTER); xmpp_stanza_add_child(iq, child); xmpp_handler_add(conn, iq_reg_cb, XMPP_NS_REGISTER, "iq", NULL, reg); xmpp_send(conn, iq); xmpp_free(ctx, domain); xmpp_stanza_release(child); xmpp_stanza_release(iq); return 0; } static void conn_handler(xmpp_conn_t *const conn, const xmpp_conn_event_t status, const int error, xmpp_stream_error_t *const stream_error, void *const userdata) { xmpp_reg_t *reg = (xmpp_reg_t *)userdata; int secured; (void)error; (void)stream_error; if (status == XMPP_CONN_RAW_CONNECT) { fprintf(stderr, "DEBUG: raw connection established\n"); xmpp_conn_open_stream_default(conn); } else if (status == XMPP_CONN_CONNECT) { fprintf(stderr, "DEBUG: stream opened\n"); secured = xmpp_conn_is_secured(conn); fprintf(stderr, "DEBUG: connection is %s.\n", secured ? "secured" : "NOT secured"); /* setup handler for stream:error */ xmpp_handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL); /* setup handlers for incoming */ xmpp_handler_add(conn, _handle_features, XMPP_NS_STREAMS, "features", NULL, reg); xmpp_timed_handler_add(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL); } else { fprintf(stderr, "DEBUG: disconnected\n"); xmpp_stop(reg->ctx); } } xmpp_reg_t *xmpp_reg_new(void) { xmpp_reg_t *reg; reg = malloc(sizeof(*reg)); if (reg != NULL) { memset(reg, 0, sizeof(*reg)); } return reg; } void xmpp_reg_release(xmpp_reg_t *reg) { free(reg); } int main(int argc, char **argv) { xmpp_ctx_t *ctx; xmpp_conn_t *conn; xmpp_log_t *log; xmpp_reg_t *reg; const char *jid; const char *host = NULL; char *domain; if (argc < 2 || argc > 3) { fprintf(stderr, "Usage: %s []\n", argv[0]); return 1; } jid = argv[1]; if (argc > 2) host = argv[2]; /* * Note, this example doesn't handle errors. Applications should check * return values of non-void functions. */ xmpp_initialize(); log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); ctx = xmpp_ctx_new(NULL, log); conn = xmpp_conn_new(ctx); /* jid can be a jid or domain for "raw" connection */ domain = xmpp_jid_domain(ctx, jid); xmpp_conn_set_jid(conn, domain); xmpp_free(ctx, domain); /* private data */ reg = xmpp_reg_new(); reg->ctx = ctx; reg->jid = jid; xmpp_connect_raw(conn, host, 0, conn_handler, reg); xmpp_run(ctx); /* release private data */ xmpp_reg_release(reg); xmpp_conn_release(conn); xmpp_ctx_free(ctx); xmpp_shutdown(); return 0; }