Introduced UNUSED macro with cast to void in commoh.h for internal use. Used cast to void directly in those files which do not include common.h. Although this change doesn't fix semantic issues with unused function parameters, it does explicitly mark all those places, which might require attention in future.
369 lines
10 KiB
C
369 lines
10 KiB
C
/* register.c
|
|
* strophe XMPP client library -- In-band registration (XEP-0077)
|
|
*
|
|
* Copyright (C) 2020 Dmitry Podgorny <pasis.ua@gmail.com>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <strophe.h>
|
|
|
|
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;
|
|
ssize_t rc;
|
|
size_t size;
|
|
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 = NULL;
|
|
size = 0;
|
|
rc = getline(&s, &size, stdin);
|
|
if (rc > 0 && s[rc - 1] == '\n')
|
|
s[rc - 1] = '\0';
|
|
if (rc > 0 && strlen(s) > 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);
|
|
}
|
|
free(s);
|
|
}
|
|
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 <stream:features> */
|
|
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 <jid> [<host>]\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;
|
|
}
|