mirror of
https://github.com/strophe/libstrophe.git
synced 2026-07-18 11:56:22 +00:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,6 +34,7 @@ examples/active
|
||||
examples/basic
|
||||
examples/bot
|
||||
examples/component
|
||||
examples/register
|
||||
examples/roster
|
||||
examples/uuid
|
||||
examples/vcard
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
0.10.0
|
||||
- c-ares support
|
||||
- LibreSSL support
|
||||
- examples/register implements XEP-0077
|
||||
- New API:
|
||||
- xmpp_stanza_get_child_by_name_and_ns()
|
||||
- xmpp_conn_is_connecting()
|
||||
|
||||
@@ -101,7 +101,8 @@ noinst_PROGRAMS = \
|
||||
examples/component \
|
||||
examples/roster \
|
||||
examples/uuid \
|
||||
examples/vcard
|
||||
examples/vcard \
|
||||
examples/register
|
||||
|
||||
examples_active_SOURCES = examples/active.c
|
||||
examples_active_CFLAGS = $(STROPHE_FLAGS)
|
||||
@@ -124,6 +125,9 @@ examples_uuid_LDADD = $(STROPHE_LIBS)
|
||||
examples_vcard_SOURCES = examples/vcard.c
|
||||
examples_vcard_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_vcard_LDADD = $(STROPHE_LIBS)
|
||||
examples_register_SOURCES = examples/register.c
|
||||
examples_register_CFLAGS = $(STROPHE_FLAGS)
|
||||
examples_register_LDADD = $(STROPHE_LIBS)
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
357
examples/register.c
Normal file
357
examples/register.c
Normal 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;
|
||||
}
|
||||
@@ -71,6 +71,10 @@ extern "C" {
|
||||
* Namespace definition for 'jabber:iq:roster'.
|
||||
*/
|
||||
#define XMPP_NS_ROSTER "jabber:iq:roster"
|
||||
/** @def XMPP_NS_REGISTER
|
||||
* Namespace definition for 'jabber:iq:register'.
|
||||
*/
|
||||
#define XMPP_NS_REGISTER "jabber:iq:register"
|
||||
|
||||
/* error defines */
|
||||
/** @def XMPP_EOK
|
||||
|
||||
Reference in New Issue
Block a user