Skip to content

Instantly share code, notes, and snippets.

@sepi
Created March 18, 2016 17:50
Show Gist options
  • Save sepi/13d1da27d5599ba105b8 to your computer and use it in GitHub Desktop.
Save sepi/13d1da27d5599ba105b8 to your computer and use it in GitHub Desktop.
/* Libotr bindings for emacs
Copyright 2016 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <stdbool.h>
#include <emacs-module.h>
#include <libotr/proto.h>
#include <libotr/privkey.h>
#include <libotr/message.h>
#define STR_SIZE_MAX 2048
#define EMACS_FUNCTION(name) \
static emacs_value \
F##name (emacs_env *env, int nargs, emacs_value args[], void *data)
int plugin_is_GPL_compatible;
static emacs_value t;
static emacs_value nil;
/* Bind NAME to FUN. */
static void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
emacs_value Qfset = env->intern (env, "fset");
emacs_value Qsym = env->intern (env, name);
emacs_value args[] = { Qsym, Sfun };
env->funcall (env, Qfset, 2, args);
}
/* Provide FEATURE to Emacs. */
static void
provide (emacs_env *env, const char *feature)
{
emacs_value Qfeat = env->intern (env, feature);
emacs_value Qprovide = env->intern (env, "provide");
emacs_value args[] = { Qfeat };
env->funcall (env, Qprovide, 1, args);
}
void
otr_user_state_finalizer(void *data)
{
OtrlUserState *user_state = (OtrlUserState*)data;
if (user_state != NULL)
{
otrl_userstate_free(*user_state);
}
}
void
otr_ui_ops_finalizer(void *data)
{
OtrlMessageAppOps *ui_ops = data;
if (ui_ops != NULL)
{
free(ui_ops);
}
}
/* TODO: remember to call relevant function with env as opdata!!! */
char ui_op_policy_name[STR_SIZE_MAX] = "";
OtrlPolicy
ui_op_policy(void *opdata, ConnContext *context)
{
emacs_env *env = (emacs_env *)opdata;
emacs_value function_name = env->intern (env, ui_op_policy_name);
emacs_value context_ptr = env->make_user_ptr(env, NULL, (void *)context);
emacs_value lisp_policy = env->funcall (env, function_name, 1, &context_ptr);
OtrlPolicy policy = env->extract_integer (env, lisp_policy);
return policy;
}
char ui_op_create_privkey_name[STR_SIZE_MAX] = "";
void
ui_op_create_privkey(void *opdata, const char *accountname, const char *protocol)
{
emacs_env *env = (emacs_env *)opdata;
emacs_value function_name = env->intern (env, ui_op_create_privkey_name);
emacs_value lisp_accountname = env->make_string (env, accountname, strlen(accountname));
emacs_value lisp_protocol = env->make_string (env, protocol, strlen(protocol));
emacs_value args[] = {lisp_accountname, lisp_protocol};
env->funcall (env, function_name, 2, args);
}
char ui_op_is_logged_in_name[STR_SIZE_MAX] = "";
int
ui_op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
const char *recipient, const char *message)
{
emacs_env *env = (emacs_env *)opdata;
emacs_value function_name = env->intern (env, ui_op_is_logged_in_name);
emacs_value lisp_accountname = env->make_string (env, accountname, strlen(accountname));
emacs_value lisp_protocol = env->make_string (env, protocol, strlen(protocol));
emacs_value lisp_recipient = env->make_string (env, recipient, strlen(recipient));
emacs_value lisp_message = env->make_string (env, message, strlen(message));
emacs_value args[] = {lisp_accountname, lisp_protocol, lisp_recipient, lisp_message};
emacs_value ret = env->funcall (env, function_name , 4, args);
return env->extract_integer (env, ret);
}
EMACS_FUNCTION(otr_uiops_create)
{
OtrlMessageAppOps *ui_ops = (OtrlMessageAppOps *) malloc (sizeof (OtrlMessageAppOps));
/* TODO: Handle allocation error (although not relevant on linux afaik) */
ui_ops->policy = NULL;
ui_ops->create_privkey = NULL;
ui_ops->is_logged_in = NULL;
ui_ops->inject_message = NULL;
ui_ops->update_context_list = NULL;
ui_ops->new_fingerprint = NULL;
ui_ops->write_fingerprints = NULL;
ui_ops->gone_secure = NULL;
ui_ops->gone_insecure = NULL;
ui_ops->still_secure = NULL;
ui_ops->max_message_size = NULL;
ui_ops->account_name = NULL;
ui_ops->account_name_free = NULL;
ui_ops->received_symkey = NULL;
ui_ops->otr_error_message = NULL;
ui_ops->otr_error_message_free = NULL;
ui_ops->resent_msg_prefix = NULL;
ui_ops->resent_msg_prefix_free = NULL;
ui_ops->handle_smp_event = NULL;
ui_ops->handle_msg_event = NULL;
ui_ops->create_instag = NULL;
ui_ops->convert_msg = NULL;
ui_ops->convert_free = NULL;
ui_ops->timer_control = NULL;
emacs_value ui_ops_ptr = env->make_user_ptr(env,
otr_ui_ops_finalizer,
(void *)ui_ops);
return ui_ops_ptr;
}
/* takes UI-ops object, callback name as a string and an emacs function and
assigns the emacs function to the correct slot of the UI-ops object */
EMACS_FUNCTION(otr_uiops_set_callback)
{
OtrlMessageAppOps *ui_ops = env->get_user_ptr (env, args[0]);
char callback_name[STR_SIZE_MAX]; /* The name of the callback as defined in the lib */
size_t bytes_copied = STR_SIZE_MAX;
bool copied = env->copy_string_contents (env, args[1],
callback_name,
&bytes_copied);
/* TODO */
/* if (!copied) */
/* { */
/* } */
char callback_symbol_name[STR_SIZE_MAX]; /* The name of the lisp
function to be called as
a string */
bytes_copied = STR_SIZE_MAX;
copied = env->copy_string_contents (env, args[2],
callback_symbol_name,
&bytes_copied);
#define SET_CB(name) \
if (strncmp(#name, callback_name, STR_SIZE_MAX)) { \
strncpy(ui_op_ ## name ## _name, callback_symbol_name, STR_SIZE_MAX); \
ui_ops->name = ui_op_ ## name; \
return t; \
}
if (copied)
{
SET_CB(policy);
SET_CB(create_privkey);
SET_CB(is_logged_in);
}
else
{
return nil;
}
}
EMACS_FUNCTION(otr_userstate_create)
{
OtrlUserState user_state = otrl_userstate_create();
emacs_value user_state_ptr = env->make_user_ptr(env,
otr_user_state_finalizer,
(void*)&user_state);
return user_state_ptr;
}
EMACS_FUNCTION(otr_privkey_read)
{
OtrlUserState *userstate = env->get_user_ptr (env, args[0]);
char privkey_filename[STR_SIZE_MAX];
size_t bytes_copied = STR_SIZE_MAX;
bool copied = env->copy_string_contents (env, args[1],
privkey_filename,
&bytes_copied);
if (copied)
{
gcry_error_t err = otrl_privkey_read (*userstate, privkey_filename); /* TODO: handle error */
return t;
}
error_exit:
return nil; /* TODO: handle error */
}
EMACS_FUNCTION(otr_instag_read)
{
OtrlUserState *userstate = env->get_user_ptr (env, args[0]);
char instag_filename[STR_SIZE_MAX];
size_t bytes_copied = STR_SIZE_MAX;
bool copied = env->copy_string_contents (env, args[1],
instag_filename,
&bytes_copied);
if (copied)
{
gcry_error_t err = otrl_instag_read (*userstate, instag_filename); /* TODO: handle error */
return t;
}
error_exit:
return nil; /* TODO: handle error */
}
EMACS_FUNCTION(otr_privkey_read_fingerprints)
{
OtrlUserState *userstate = env->get_user_ptr (env, args[0]);
char fingerprint_filename[STR_SIZE_MAX];
size_t bytes_copied = STR_SIZE_MAX;
bool copied = env->copy_string_contents (env, args[1],
fingerprint_filename,
&bytes_copied);
if (copied)
{
gcry_error_t err = otrl_privkey_read_fingerprints (*userstate, fingerprint_filename,
NULL, NULL); /* TODO: handle error */
return t;
}
error_exit:
return nil; /* TODO: handle error */
}
int
emacs_module_init (struct emacs_runtime *ert)
{
emacs_env *env = ert->get_environment (ert);
/* Useful symbols */
t = env->intern (env, "t");
nil = env->intern (env, "nil");
/* Create function bindings */
bind_function (env, "otr-uiops-create",
env->make_function (env, 0, 0, Fotr_uiops_create, "doc", NULL));
bind_function (env, "otr-userstate-create",
env->make_function (env, 0, 0, Fotr_userstate_create, "doc", NULL));
bind_function (env, "otr-privkey-read",
env->make_function (env, 2, 2, Fotr_privkey_read, "doc", NULL));
bind_function (env, "otr-instag-read",
env->make_function (env, 2, 2, Fotr_instag_read, "doc", NULL));
bind_function (env, "otr-privkey-read-fingerprints",
env->make_function (env, 2, 2, Fotr_privkey_read_fingerprints, "doc", NULL));
provide (env, "otr");
OTRL_INIT;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment