mirror of
https://github.com/strophe/libstrophe.git
synced 2026-07-17 19:36:22 +00:00
It has been pointed out that the wording of the license of this library is not entirely clear. The term "dual licensing" usually refers to a licence choice of two licenses "LICENSE1 _or_ LICENSE2. Instead the license of this library claimed "LICENSE1 _and_ LICENSE2". After an internal discussion with @metajack and @pasis it was made clear that the initial idea was to dual license the library in the usual way. This was also made clear by jack on the ML in the past [0]. As of jack, these licensing terms originated from jquery, which also used the 'and' version in the past and has since been corrected [1]. This patch changes the license terms to 'MIT or GPLv3' and also adds SPDX headers [2]. [0] https://groups.google.com/g/libstrophe/c/JkFgr601JQc [1] https://stackoverflow.com/q/2758409 [2] https://spdx.org Signed-off-by: Steffen Jaeckel <jaeckel-floss@eyet-services.de>
365 lines
10 KiB
C
365 lines
10 KiB
C
/* SPDX-License-Identifier: MIT OR GPL-3.0-only */
|
|
/* 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 or 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;
|
|
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 *conn, xmpp_stanza_t *stanza, void *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 *conn, xmpp_stanza_t *stanza, void *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 *conn, xmpp_stanza_t *stanza, void *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 *conn,
|
|
xmpp_stanza_t *stanza,
|
|
void *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 *conn, void *userdata)
|
|
{
|
|
(void)userdata;
|
|
|
|
fprintf(stderr, "DEBUG: timeout\n");
|
|
xmpp_disconnect(conn);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_handle_features(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *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 *conn,
|
|
xmpp_conn_event_t status,
|
|
int error,
|
|
xmpp_stream_error_t *stream_error,
|
|
void *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;
|
|
}
|