summaryrefslogtreecommitdiff
path: root/src/otr/otr-ops.c
diff options
context:
space:
mode:
authorailin-nemui <ailin-nemui@users.noreply.github.com>2018-03-31 13:04:35 +0200
committerGitHub <noreply@github.com>2018-03-31 13:04:35 +0200
commit0c1db8f2aee4aa5040a16518adb71f57dc953985 (patch)
tree4c51838a1b7aa278727b9de7f3b1663120b75e62 /src/otr/otr-ops.c
parent9fa8c32b9e2f80e308d1ed780490f6d2dfd6bb05 (diff)
parent0b2f5b0a1e30932b07245c508a673eb0e9b6ab37 (diff)
downloadirssi-0c1db8f2aee4aa5040a16518adb71f57dc953985.zip
Merge pull request #854 from irssi/ahf/otr
OTR support, take 2
Diffstat (limited to 'src/otr/otr-ops.c')
-rw-r--r--src/otr/otr-ops.c362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/otr/otr-ops.c b/src/otr/otr-ops.c
new file mode 100644
index 00000000..4e8c4e81
--- /dev/null
+++ b/src/otr/otr-ops.c
@@ -0,0 +1,362 @@
+/*
+ * Off-the-Record Messaging (OTR) modules for IRC
+ * Copyright (C) 2008 Uli Meis <a.sporto+bee@gmail.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
+ */
+
+#include "common.h"
+
+#include "signals.h"
+#include "levels.h"
+#include "printtext.h"
+#include "fe-windows.h"
+
+#include "key.h"
+#include "module.h"
+#include "otr-formats.h"
+#include "irssi-otr.h"
+
+static OtrlPolicy OTR_DEFAULT_POLICY = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE;
+
+/*
+ * Return default policy for now.
+ */
+static OtrlPolicy ops_policy(void *opdata, ConnContext *context)
+{
+ return OTR_DEFAULT_POLICY;
+}
+
+/*
+ * Request for key generation.
+ *
+ * The lib actually expects us to be finished before the call returns. Since
+ * this can take more than an hour on some systems there isn't even a point in
+ * trying...
+ */
+static void ops_create_privkey(void *opdata, const char *accountname,
+ const char *protocol)
+{
+ key_gen_run(user_state_global, accountname);
+}
+
+/*
+ * Inject OTR message.
+ */
+static void ops_inject_msg(void *opdata, const char *accountname,
+ const char *protocol, const char *recipient, const char *message)
+{
+ SERVER_REC *server = opdata;
+
+ IRSSI_OTR_DEBUG("Inject msg:\n[%s]", message);
+ otr_send_message(server, recipient, message);
+}
+
+/*
+ * Gone secure.
+ */
+static void ops_secure(void *opdata, ConnContext *context)
+{
+ char ownfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN];
+ char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN];
+ SERVER_REC *server = opdata;
+ struct otr_peer_context *opc;
+
+ g_return_if_fail(context != NULL);
+ /* This should *really* not happened */
+ g_return_if_fail(context->msgstate == OTRL_MSGSTATE_ENCRYPTED);
+
+ printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_SECURE);
+ otr_status_change(server, context->username, OTR_STATUS_GONE_SECURE);
+
+ opc = context->app_data;
+ opc->active_fingerprint = context->active_fingerprint;
+
+ if (otrl_context_is_fingerprint_trusted(context->active_fingerprint)) {
+ /* Secure and trusted */
+ return;
+ }
+
+ /* Not authenticated. Let's print out the fingerprints for comparison. */
+ otrl_privkey_hash_to_human(peerfp, context->active_fingerprint->fingerprint);
+ otrl_privkey_fingerprint(user_state_global->otr_state, ownfp, context->accountname, OTR_PROTOCOL_ID);
+
+ printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_UNAUTHENTICATED_WARNING);
+ printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_FP_INFO, server->nick, ownfp);
+ printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_FP_INFO, context->username, peerfp);
+}
+
+/*
+ * Gone insecure.
+ */
+static void ops_insecure(void *opdata, ConnContext *context)
+{
+ SERVER_REC *server = opdata;
+
+ printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_INSECURE);
+ otr_status_change(server, context->username, OTR_STATUS_GONE_INSECURE);
+}
+
+/*
+ * Really critical with IRC. Unfortunately, we can't tell our peer which size
+ * to use.
+ */
+static int ops_max_msg(void *opdata, ConnContext *context)
+{
+ return OTR_MAX_MSG_SIZE;
+}
+
+static void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err)
+{
+ SERVER_REC *server = opdata;
+ char *username = context->username;
+
+ switch (msg_event) {
+ case OTRL_MSGEVENT_NONE:
+ break;
+ case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_REQUIRED);
+ break;
+ case OTRL_MSGEVENT_ENCRYPTION_ERROR:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_ERROR);
+ break;
+ case OTRL_MSGEVENT_CONNECTION_ENDED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_ENDED, username);
+ break;
+ case OTRL_MSGEVENT_SETUP_ERROR:
+ if (!err) {
+ err = GPG_ERR_INV_VALUE;
+ }
+ switch (err) {
+ case GPG_ERR_INV_VALUE:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_MALFORMED, username);
+ break;
+ default:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ERROR, gcry_strerror(err));
+ break;
+ }
+ break;
+ case OTRL_MSGEVENT_MSG_REFLECTED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_REFLECTED, username);
+ break;
+ case OTRL_MSGEVENT_MSG_RESENT:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_RESENT, username, message);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_NOT_IN_PRIVATE, username);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNREADABLE, username);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_MALFORMED, username);
+ break;
+ case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
+ IRSSI_OTR_DEBUG("Heartbeat received from %s.", username);
+ break;
+ case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
+ IRSSI_OTR_DEBUG("Heartbeat sent to %s.", username);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ERROR, message);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNENCRYPTED, username);
+
+ /*
+ * This is a hack I found to send the message in a private window of
+ * the username without creating an infinite loop since the 'message
+ * private' signal is hijacked in this module. If someone is able to
+ * clean this up with a more elegant solution, by all means PLEASE
+ * submit a patch or email me a better way.
+ */
+ signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
+ signal_emit("message private", 5, server, message, username, server->connrec->address, server->nick);
+ signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
+ printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNRECOGNIZED, username);
+ break;
+ case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
+ IRSSI_OTR_DEBUG("%s has sent a message for a different instance.", username);
+ break;
+ }
+}
+
+/*
+ * A context changed.
+ */
+static void ops_up_ctx_list(void *opdata)
+{
+ otr_status_change(opdata, NULL, OTR_STATUS_CTX_UPDATE);
+}
+
+/*
+ * Save fingerprint changes.
+ */
+static void ops_write_fingerprints(void *data)
+{
+ key_write_fingerprints(user_state_global);
+}
+
+static int ops_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient)
+{
+ int ret;
+ SERVER_REC *server = opdata;
+
+ /* Logged in? */
+ ret = server != NULL;
+
+ IRSSI_OTR_DEBUG("User %s %s logged in", accountname, ret ? "" : "not");
+
+ return ret;
+}
+
+static void ops_create_instag(void *opdata, const char *accountname,
+ const char *protocol)
+{
+ otrl_instag_generate(user_state_global->otr_state, "/dev/null", accountname, protocol);
+ key_write_instags(user_state_global);
+}
+
+static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event,
+ ConnContext *context, unsigned short progress_percent, char *question)
+{
+ SERVER_REC *server = opdata;
+ const char *from = context->username;
+ struct otr_peer_context *opc = context->app_data;
+
+ /*
+ * Without a peer context, we can't update the status bar. Code flow error
+ * if none is found. This context is created automatically by an otrl_*
+ * call or if non existent when returned from
+ * otrl_message_sending/receiving.
+ */
+ g_return_if_fail(opc != NULL);
+
+ opc->smp_event = smp_event;
+
+ switch (smp_event) {
+ case OTRL_SMPEVENT_ASK_FOR_SECRET:
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_SECRET_QUESTION, from);
+ opc->ask_secret = 1;
+ otr_status_change(server, from, OTR_STATUS_SMP_INCOMING);
+ break;
+ case OTRL_SMPEVENT_ASK_FOR_ANSWER:
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_HEADER, from);
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_QUESTION, question);
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_FOOTER);
+ opc->ask_secret = 1;
+ otr_status_change(server, from, OTR_STATUS_SMP_INCOMING);
+ break;
+ case OTRL_SMPEVENT_IN_PROGRESS:
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_IN_PROGRESS, from);
+ otr_status_change(server, from, OTR_STATUS_SMP_FINALIZE);
+ break;
+ case OTRL_SMPEVENT_SUCCESS:
+ printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_SUCCESS, from);
+ otr_status_change(server, from, OTR_STATUS_SMP_SUCCESS);
+ break;
+ case OTRL_SMPEVENT_ABORT:
+ otr_auth_abort(server, context->username);
+ otr_status_change(server, from, OTR_STATUS_SMP_ABORTED);
+ break;
+ case OTRL_SMPEVENT_FAILURE:
+ case OTRL_SMPEVENT_CHEATED:
+ case OTRL_SMPEVENT_ERROR:
+ printformat(server, from, MSGLEVEL_CLIENTERROR, TXT_OTR_SMP_FAILURE, from);
+ otr_status_change(server, from, OTR_STATUS_SMP_FAILED);
+ break;
+ default:
+ g_warning("Received unknown SMP event: %d", smp_event);
+ break;
+ }
+}
+
+/*
+ * timer_control callback.
+ */
+static void ops_timer_control(void *opdata, unsigned int interval)
+{
+ otr_control_timer(interval, opdata);
+}
+
+/*
+ * Handle otr error message.
+ */
+static const char *ops_otr_error_message(void *opdata, ConnContext *context,
+ OtrlErrorCode code)
+{
+ char *msg = NULL;
+
+ switch (code) {
+ case OTRL_ERRCODE_NONE:
+ break;
+ case OTRL_ERRCODE_ENCRYPTION_ERROR:
+ msg = strdup("Error occurred encrypting message.");
+ break;
+ case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE:
+ if (context) {
+ msg = strdup("You sent encrypted data which was unexpected");
+ }
+ break;
+ case OTRL_ERRCODE_MSG_UNREADABLE:
+ msg = strdup("You transmitted an unreadable encrypted message");
+ break;
+ case OTRL_ERRCODE_MSG_MALFORMED:
+ msg = strdup("You transmitted a malformed data message.");
+ break;
+ }
+
+ return msg;
+}
+
+/*
+ * Free otr error message callback.
+ */
+static void ops_otr_error_message_free(void *opdata, const char *err_msg)
+{
+ g_free_not_null((char *)err_msg);
+}
+
+/*
+ * Assign OTR message operations.
+ */
+OtrlMessageAppOps otr_ops = {
+ ops_policy,
+ ops_create_privkey,
+ ops_is_logged_in,
+ ops_inject_msg,
+ ops_up_ctx_list,
+ NULL, /* new_fingerprint */
+ ops_write_fingerprints,
+ ops_secure,
+ ops_insecure,
+ NULL, /* still_secure */
+ ops_max_msg,
+ NULL, /* account_name */
+ NULL, /* account_name_free */
+ NULL, /* received_symkey */
+ ops_otr_error_message,
+ ops_otr_error_message_free,
+ NULL, /* resent_msg_prefix */
+ NULL, /* resent_msg_prefix_free */
+ ops_smp_event,
+ ops_handle_msg_event,
+ ops_create_instag,
+ NULL, /* convert_msg */
+ NULL, /* convert_free */
+ ops_timer_control,
+};