diff --git a/.gitignore b/.gitignore index 960cd92..abceee2 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ examples/bot examples/component examples/roster examples/uuid +examples/vcard test_stamp test-suite.log tests/*.o diff --git a/Makefile.am b/Makefile.am index 0ec3db2..dce5560 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,7 +99,8 @@ noinst_PROGRAMS = \ examples/bot \ examples/component \ examples/roster \ - examples/uuid + examples/uuid \ + examples/vcard examples_active_SOURCES = examples/active.c examples_active_CFLAGS = $(STROPHE_FLAGS) @@ -119,6 +120,9 @@ examples_roster_LDADD = $(STROPHE_LIBS) examples_uuid_SOURCES = examples/uuid.c examples_uuid_CFLAGS = $(STROPHE_FLAGS) examples_uuid_LDADD = $(STROPHE_LIBS) +examples_vcard_SOURCES = examples/vcard.c +examples_vcard_CFLAGS = $(STROPHE_FLAGS) +examples_vcard_LDADD = $(STROPHE_LIBS) ## Tests diff --git a/examples/vcard.c b/examples/vcard.c new file mode 100644 index 0000000..c584063 --- /dev/null +++ b/examples/vcard.c @@ -0,0 +1,281 @@ +/* vcard.c + * strophe XMPP client library -- vCard example + * + * Copyright (C) 2016 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. + */ + +#include +#include /* basename */ +#include +#include +#include +#include + +typedef struct { + xmpp_ctx_t *ctx; + const char *recipient; + const char *img_path; +} vcard_t; + +typedef void (*vcard_cb_t)(vcard_t *, xmpp_stanza_t *); + +#define REQ_TIMEOUT 5000 +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static void vcard_photo(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *tmp; + char *s; + char *tok; + char *saveptr = NULL; + char *copy; + unsigned char *img; + size_t img_size; + FILE *fd; + + tmp = xmpp_stanza_get_child_by_name(stanza, "TYPE"); + assert(tmp != NULL); + s = xmpp_stanza_get_text(tmp); + assert(s != NULL); + printf("PHOTO: %s, saving to file %s\n", s, vc->img_path); + xmpp_free(vc->ctx, s); + + tmp = xmpp_stanza_get_child_by_name(stanza, "BINVAL"); + assert(tmp != NULL); + s = xmpp_stanza_get_text(tmp); + assert(s != NULL); + + /* remove \n and \r */ + copy = (char *)malloc(strlen(s) + 1); + assert(copy != NULL); + copy[0] = '\0'; + tok = strtok_r(s, "\n\r", &saveptr); + while (tok != NULL) { + strcat(copy, tok); + tok = strtok_r(NULL, "\n\r", &saveptr); + } + + xmpp_base64_decode_bin(vc->ctx, copy, strlen(copy), &img, &img_size); + assert(img != NULL); + + fd = fopen(vc->img_path, "w"); + assert(fd != NULL); + fwrite(img, 1, img_size, fd); + fclose(fd); + + free(copy); + xmpp_free(vc->ctx, s); + xmpp_free(vc->ctx, img); +} + +static void vcard_print_string(vcard_t *vc, xmpp_stanza_t *stanza, + const char *info) +{ + char *s = xmpp_stanza_get_text(stanza); + + assert(s != NULL); + printf("%s: %s\n", info, s); + xmpp_free(vc->ctx, s); +} + +static void vcard_bday(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Birthday"); +} + +static void vcard_desc(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Description"); +} + +static void vcard_email(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *userid = xmpp_stanza_get_child_by_name(stanza, "USERID"); + + if (userid != NULL) + vcard_print_string(vc, userid, "E-mail"); +} + +static void vcard_fn(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Full name"); +} + +static void vcard_name(vcard_t *vc, xmpp_stanza_t *stanza) +{ + xmpp_stanza_t *name = xmpp_stanza_get_child_by_name(stanza, "GIVEN"); + xmpp_stanza_t *family = xmpp_stanza_get_child_by_name(stanza, "FAMILY"); + + if (name != NULL) + vcard_print_string(vc, name, "Given name"); + if (family != NULL) + vcard_print_string(vc, family, "Family name"); +} + +static void vcard_nick(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "Nickname"); +} + +static void vcard_url(vcard_t *vc, xmpp_stanza_t *stanza) +{ + vcard_print_string(vc, stanza, "URL"); +} + +static vcard_cb_t vcard_cb_get(xmpp_stanza_t *stanza) +{ + vcard_cb_t cb = NULL; + const char *tag; + int i; + + static struct { + const char *tag; + vcard_cb_t cb; + } vcard_tbl[] = { + { "PHOTO", vcard_photo }, + { "BDAY", vcard_bday }, + { "DESC", vcard_desc }, + { "EMAIL", vcard_email }, + { "FN", vcard_fn }, + { "N", vcard_name }, + { "NICKNAME", vcard_nick }, + { "URL", vcard_url }, + }; + + tag = xmpp_stanza_get_name(stanza); + if (tag == NULL) + goto exit; + + for (i = 0; i < ARRAY_SIZE(vcard_tbl); ++i) { + if (strcmp(tag, vcard_tbl[i].tag) == 0) { + cb = vcard_tbl[i].cb; + break; + } + } + +exit: + return cb; +} + +static int timedout(xmpp_conn_t * const conn, void * const userdata) +{ + fprintf(stderr, "Timeout reached.\n"); + xmpp_disconnect(conn); + + return 0; +} + +static int recv_vcard(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, + void * const userdata) +{ + vcard_t *vc = userdata; + vcard_cb_t cb; + xmpp_stanza_t *child; + char *s; + size_t s_size; + int rc; + + printf("Received vCard.\n\n"); + + child = xmpp_stanza_get_child_by_name(stanza, "error"); + if (child != NULL) { + rc = xmpp_stanza_to_text(child, &s, &s_size); + assert(rc == XMPP_EOK); + printf("Error returned: %s.\n", s); + xmpp_free(vc->ctx, s); + goto exit; + } + + child = xmpp_stanza_get_child_by_name(stanza, "vCard"); + assert(child != NULL); + child = xmpp_stanza_get_children(child); + while (child != NULL) { + cb = vcard_cb_get(child); + if (cb != NULL) + cb(vc, child); + child = xmpp_stanza_get_next(child); + } + +exit: + xmpp_disconnect(conn); + + return 0; +} + +static void send_vcard_req(xmpp_conn_t *conn, const char *to, const char *id) +{ + printf("Requesting vCard from %s.\n", to); + xmpp_send_raw_string(conn, "" + "", + xmpp_conn_get_bound_jid(conn), to, id); +} + +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) +{ + vcard_t *vc = userdata; + + if (status == XMPP_CONN_CONNECT) { + send_vcard_req(conn, vc->recipient, "vc1"); + xmpp_id_handler_add(conn, recv_vcard, "vc1", vc); + xmpp_timed_handler_add(conn, timedout, REQ_TIMEOUT, NULL); + } else { + if (error != 0) + fprintf(stderr, "Disconnected with error=%d.\n", error); + if (stream_error != NULL) + fprintf(stderr, "Stream error type=%d text=%s.\n", + stream_error->type, stream_error->text); + xmpp_stop(vc->ctx); + } +} + +int main(int argc, char **argv) +{ + xmpp_log_t *log; + xmpp_ctx_t *ctx; + xmpp_conn_t *conn; + const char *jid; + const char *pass; + char *prog; + vcard_t vcard; + + if (argc < 4 || argc > 5) { + prog = argc > 0 ? strdup(argv[0]) : NULL; + printf("Usage: %s " + "[image-file]\n\n", prog == NULL ? "vcard" : basename(prog)); + printf("If vCard contains a photo it will be stored to " + "image-file. If you don't provide the image-file " + "default filename will be generated.\n"); + free(prog); + return 1; + } + + jid = argv[1]; + pass = argv[2]; + vcard.recipient = argv[3]; + vcard.img_path = argc > 4 ? argv[4] : "vcard.jpg"; + + xmpp_initialize(); + log = xmpp_get_default_logger(XMPP_LEVEL_INFO); + ctx = xmpp_ctx_new(NULL, log); + conn = xmpp_conn_new(ctx); + xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_pass(conn, pass); + vcard.ctx = ctx; + xmpp_connect_client(conn, NULL, 0, conn_handler, &vcard); + xmpp_run(ctx); + xmpp_conn_release(conn); + xmpp_ctx_free(ctx); + xmpp_shutdown(); + + return 0; +} +