XEP-0077: add examples/register

XEP-0077 will not be implemented as part of libstrophe's API, but an
example is provided. The example shows how to use the "raw" connection
to register a user.

Closes #54.
This commit is contained in:
Dmitry Podgorny
2016-04-26 18:50:00 +00:00
parent 1b10e8f987
commit 8cfb2d2a03
5 changed files with 368 additions and 1 deletions

357
examples/register.c Normal file
View File

@@ -0,0 +1,357 @@
/* 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;
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)
{
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);
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)
{
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;
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;
}