/* bot/context.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <glib.h>
#include <strophe.h>
#include <openssl/sha.h> /* For SHA1 */
#include <unistd.h>
#include <signal.h>
#include "context.h"
extern struct BotXmppCtx *g_bctx = NULL;
struct BotXmppCtx *bot_get_ctx()
{
return g_bctx;
}
void bot_set_ctx(struct BotXmppCtx *ctx)
{
if(g_bctx) {
fprintf(stderr, "Dev error: cannot set XMPP ctx twice in the same program run.\n");
} else {
g_bctx = ctx;
}
}
void bot_free_user_data(struct BotUserInfo user, GString *filename, bool freeSegment)
{
g_string_free(filename, freeSegment);
g_string_free(user.jid, freeSegment);
g_string_free(user.pass, freeSegment);
}
/* Implement https://xmpp.org/extensions/xep-0084.html */
/* TODO need to understand the format for data stanza encoding (bytes? base64?) */
/* int bot_xmpp_set_avatar_user(struct BotXmppCtx * const bctx, const GString *avatarFile) */
/* { */
/* gchar *avatar; */
/* gsize avatarLenght; */
/* xmpp_stanza_t *iq, *pubsub, *publish, *item, *data; */
/* /\* 1. Create IQ 'set' stanza *\/ */
/* iq = xmpp_stanza_new(bctx->ctx); */
/* xmpp_stanza_set_name(iq, "iq"); */
/* xmpp_stanza_set_type(iq, "set"); */
/* xmpp_stanza_set_id(iq, "publish1"); */
/* /\* Create <pubsub> protocol element*\/ */
/* pubsub = xmpp_stanza_new(bctx->ctx); */
/* xmpp_stanza_set_name(pubsub, "pubsub"); */
/* xmpp_stanza_set_ns(pubsub, "http://jabber.org/protocol/pubsub"); */
/* xmpp_stanza_add_child(iq, pubsub); */
/* /\* Create <publish> element *\/ */
/* publish = xmpp_stanza_new(bctx->ctx); */
/* xmpp_stanza_set_name(publish, "publish"); */
/* xmpp_stanza_set_attribute(publish, "node", "urn:xmpp:avatar:data"); */
/* xmpp_stanza_add_child(pubsub, publish); */
/* /\* Add <item> where id should be the sha1sum of the image *\/ */
/* item = xmpp_stanza_new(bctx->ctx); */
/* xmpp_stanza_set_name(item, "item"); */
/* xmpp_stanza_set_id(item, "SHA1SUM"); */
/* xmpp_stanza_add_child(publish, item); */
/* /\* Add <data> with base64 data *\/ */
/* data = xmpp_stanza_new(bctx->ctx); */
/* xmpp_stanza_set_name(data, "data"); */
/* xmpp_stanza_set_ns(data, "urn:xmpp:avatar:data"); */
/* if(g_file_get_contents(avatarFile->str, &avatar, &avatarLenght, NULL)) { */
/* xmpp_stanza_set_text(data, avatar); */
/* } else { */
/* fprintf(stderr, "Unable to read png avatar.\n"); */
/* } */
/* xmpp_stanza_set_text(data, "DATA"); */
/* xmpp_stanza_add_child(item, data); */
/* /\* 6. Send stanza and release it + all children *\/ */
/* xmpp_send(bctx->conn, iq); */
/* xmpp_stanza_release(iq); */
/* return 0; */
/* } */
/* implement https://xmpp.org/extensions/xep-0153.html */
int bot_xmpp_set_avatar(struct BotXmppCtx * const bctx, const GString *avatarFile)
{
gchar *avatar;
gsize avatarLenght;
/* GString *avatarID; */ // TODO
unsigned char hash[SHA_DIGEST_LENGTH]; /* == 20 */
xmpp_stanza_t *iq, *vcard, *photo, *type, *binval;
/* 1. Create IQ 'set' stanza */
iq = xmpp_stanza_new(bctx->ctx);
xmpp_stanza_set_name(iq, "iq");
xmpp_stanza_set_type(iq, "set");
xmpp_stanza_set_id(iq, "vcard_set_1");
/* Create <vCard xmlns='vcard-temp' */
vcard = xmpp_stanza_new(bctx->ctx);
xmpp_stanza_set_name(vcard, "vCard");
xmpp_stanza_set_ns(vcard, "vcard-temp");
xmpp_stanza_add_child(iq, vcard);
/* Create <PHOTO> element */
photo = xmpp_stanza_new(bctx->ctx);
xmpp_stanza_set_name(photo, "PHOTO");
xmpp_stanza_add_child(vcard, photo);
/* Add <TYPE> (e.g., image/jpeg) */
type = xmpp_stanza_new(bctx->ctx);
xmpp_stanza_set_name(type, "TYPE");
xmpp_stanza_set_text(type, "image/jpeg");
xmpp_stanza_add_child(photo, type);
/* Add <BINVAL> with base64 data */
binval = xmpp_stanza_new(bctx->ctx);
xmpp_stanza_set_name(binval, "BINVAL");
if(g_file_get_contents(avatarFile->str, &avatar, &avatarLenght, NULL)) {
SHA1((const unsigned char*)avatarFile->str, sizeof(avatarFile->str)-1, hash);
xmpp_stanza_set_text(binval, avatar);
} else {
fprintf(stderr, "Unable to read BASE64-encoded avatar.\n");
}
xmpp_stanza_add_child(photo, binval);
/* 6. Send stanza and release it + all children */
xmpp_send(bctx->conn, iq);
xmpp_stanza_release(iq);
return 0;
}
int bot_xmpp_startup(struct BotUserInfo user, GString *avatarFile)
{
xmpp_log_t* newLog;
xmpp_ctx_t* newCtx;
xmpp_conn_t* newCon;
/* GString *avatarID; */ // TODO
int connectStatus;
struct BotXmppCtx *bctx = malloc(sizeof *bctx);
/* init library */
xmpp_initialize();
/* create a context */
newLog = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */
newCtx = xmpp_ctx_new(NULL, newLog);
if (!(newLog && newCtx)) {
fprintf(stderr, "Unable to startup. Context creation failed.");
return 1;
}
/* create a connection */
newCon = xmpp_conn_new(newCtx);
if (!newCon) {
fprintf(stderr, "Unable to startup. Connection failed.");
return 1;
}
/* setup authentication information */
xmpp_conn_set_jid(newCon, user.jid->str);
xmpp_conn_set_pass(newCon, user.pass->str);
bctx->log = newLog;
bctx->ctx = newCtx;
bctx->conn = newCon;
/* TODO properly set avatarID in presence stanza */
bot_xmpp_set_avatar(bctx, avatarFile);
/* initiate connection */
connectStatus = xmpp_connect_client(newCon, NULL, 0, conn_handler, newCtx);
if(connectStatus != 0) {
/* connecting went wrong */
return connectStatus;
}
/* store ctx as global */
bot_set_ctx(bctx);
/* install signal handler */
signal(SIGALRM, bot_reminder_alarm_func);
return 0;
}
void bot_xmpp_shutdown()
{
struct BotXmppCtx *bctx = bot_get_ctx();
/* enter the event loop -
our connect handler will trigger an exit */
xmpp_run(bctx->ctx);
/* release our connection and context */
xmpp_conn_release(bctx->conn);
xmpp_ctx_free(bctx->ctx);
/* final shutdown of the library */
xmpp_shutdown();
}
/* send a message when SIGALRM is triggered */
void bot_reminder_alarm_func(int s)
{
unsigned int minutes;
BotReminder *reminder = bot_get_last_reminder();
struct BotXmppCtx *bctx = bot_get_ctx();
GString *msg = g_string_new(NULL);
if(!reminder) {
fprintf(stderr, "SIGALRM was raised but no reminder was set.\n");
return;
}
minutes = reminder->minutes;
g_string_printf(msg, "OHI! Segna le spese! Te lo ricordo di nuovo tra %d minuti.\n", minutes);
send_message(bctx->conn, reminder->from, bctx->ctx, msg);
g_string_free(msg, true);
/* reinstall alarm */
alarm(minutes * 60);
}