43
44
#include "gtkplugin.h"
50
/* On Win32, include win32dep.h from pidgin for correct definition
55
/* internationalisation header */
56
#include <glib/gi18n-lib.h>
58
#endif /* ENABLE_NLS */
46
60
/* libotr headers */
47
61
#include <libotr/privkey.h>
48
62
#include <libotr/proto.h>
63
#include <libotr/tlv.h>
49
64
#include <libotr/message.h>
50
65
#include <libotr/userstate.h>
79
95
/* We'll only use the one OtrlUserState. */
80
96
OtrlUserState otrg_plugin_userstate = NULL;
98
/* GLib HashTable for storing the maximum message size for various
100
GHashTable* mms_table;
82
102
/* Send an IM from the given account to the given recipient. Display an
83
103
* error dialog if that account isn't currently logged in. */
84
104
void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
91
111
const char *protocol = purple_account_get_protocol_id(account);
92
112
const char *accountname = purple_account_get_username(account);
93
113
PurplePlugin *p = purple_find_prpl(protocol);
94
char *msg = g_strdup_printf("You are not currently connected to "
95
"account %s (%s).", accountname,
96
(p && p->info->name) ? p->info->name : "Unknown");
114
char *msg = g_strdup_printf(_("You are not currently connected to "
115
"account %s (%s)."), accountname,
116
(p && p->info->name) ? p->info->name : _("Unknown"));
97
117
otrg_dialog_notify_error(accountname, protocol, recipient,
98
"Not connected", msg, NULL);
118
_("Not connected"), msg, NULL);
107
127
PurpleAccount *account;
108
128
OtrlPolicy policy = OTRL_POLICY_DEFAULT;
110
131
if (!context) return policy;
112
133
account = purple_accounts_find(context->accountname, context->protocol);
113
134
if (!account) return policy;
115
return otrg_ui_find_policy(account, context->username);
136
otrg_ui_get_prefs(&prefs, account, context->username);
118
140
static const char *protocol_name_cb(void *opdata, const char *protocol)
133
155
const char *protocol)
135
157
OtrgDialogWaitHandle waithandle;
138
163
gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME, NULL);
139
164
if (!privkeyfile) {
140
fprintf(stderr, "Out of memory building filenames!\n");
165
fprintf(stderr, _("Out of memory building filenames!\n"));
143
171
privf = g_fopen(privkeyfile, "w+b");
144
175
g_free(privkeyfile);
146
fprintf(stderr, "Could not write private key file\n");
177
fprintf(stderr, _("Could not write private key file\n"));
186
217
PurpleAccount *account = purple_accounts_find(accountname, protocol);
188
219
PurplePlugin *p = purple_find_prpl(protocol);
189
char *msg = g_strdup_printf("Unknown account %s (%s).", accountname,
190
(p && p->info->name) ? p->info->name : "Unknown");
220
char *msg = g_strdup_printf(_("Unknown account %s (%s)."),
222
(p && p->info->name) ? p->info->name : _("Unknown"));
191
223
otrg_dialog_notify_error(accountname, protocol, recipient,
192
"Unknown account", msg, NULL);
224
_("Unknown account"), msg, NULL);
309
355
} else if (newmessage) {
310
char *ourm = malloc(strlen(newmessage) + 1);
312
strcpy(ourm, newmessage);
356
/* Fragment the message if necessary, and send all but the last
357
* fragment over the network. Pidgin will send the last
358
* fragment for us. */
359
ConnContext *context = otrl_context_find(otrg_plugin_userstate,
360
username, accountname, protocol, 0, NULL, NULL, NULL);
363
err = otrl_message_fragment_and_send(&ui_ops, NULL, context,
364
newmessage, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, message);
314
365
otrl_message_free(newmessage);
370
/* Abort the SMP protocol. Used when malformed or unexpected messages
372
void otrg_plugin_abort_smp(ConnContext *context)
374
otrl_message_abort_smp(otrg_plugin_userstate, &ui_ops, NULL, context);
377
/* Start the Socialist Millionaires' Protocol over the current connection,
378
* using the given initial secret, and optionally a question to pass to
380
void otrg_plugin_start_smp(ConnContext *context, const char *question,
381
const unsigned char *secret, size_t secretlen)
383
otrl_message_initiate_smp_q(otrg_plugin_userstate, &ui_ops, NULL,
384
context, question, secret, secretlen);
387
/* Continue the Socialist Millionaires' Protocol over the current connection,
388
* using the given initial secret (ie finish step 2). */
389
void otrg_plugin_continue_smp(ConnContext *context,
390
const unsigned char *secret, size_t secretlen)
392
otrl_message_respond_smp(otrg_plugin_userstate, &ui_ops, NULL,
393
context, secret, secretlen);
321
396
/* Send the default OTR Query message to the correspondent of the given
322
397
* context, from the given account. [account is actually a
323
398
* PurpleAccount*, but it's declared here as void* so this can be passed
325
400
void otrg_plugin_send_default_query(ConnContext *context, void *vaccount)
327
402
PurpleAccount *account = vaccount;
328
char *msg = otrl_proto_default_query_msg(context->accountname,
329
otrg_ui_find_policy(account, context->username));
406
otrg_ui_get_prefs(&prefs, account, context->username);
407
msg = otrl_proto_default_query_msg(context->accountname,
330
409
otrg_plugin_inject_message(account, context->username,
331
410
msg ? msg : "?OTRv2?");
339
418
PurpleAccount *account;
340
419
const char *username, *accountname;
343
423
account = purple_conversation_get_account(conv);
344
424
accountname = purple_account_get_username(account);
345
425
username = purple_conversation_get_name(conv);
347
msg = otrl_proto_default_query_msg(accountname,
348
otrg_ui_find_policy(account, username));
427
otrg_ui_get_prefs(&prefs, account, username);
428
msg = otrl_proto_default_query_msg(accountname, prefs.policy);
349
429
otrg_plugin_inject_message(account, username, msg ? msg : "?OTRv2?");
388
470
otrg_dialog_finished(accountname, protocol, username);
389
471
otrg_ui_update_keylist();
474
/* Keep track of our current progress in the Socialist Millionaires'
476
context = otrl_context_find(otrg_plugin_userstate, username,
477
accountname, protocol, 0, NULL, NULL, NULL);
479
nextMsg = context->smstate->nextExpected;
481
if (context->smstate->sm_prog_state == OTRL_SMP_PROG_CHEATED) {
482
otrg_plugin_abort_smp(context);
483
otrg_dialog_update_smp(context, 0.0);
484
context->smstate->nextExpected = OTRL_SMP_EXPECT1;
485
context->smstate->sm_prog_state = OTRL_SMP_PROG_OK;
487
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
489
if (nextMsg != OTRL_SMP_EXPECT1)
490
otrg_plugin_abort_smp(context);
492
char *question = (char *)tlv->data;
493
char *eoq = memchr(question, '\0', tlv->len);
495
otrg_dialog_socialist_millionaires_q(context,
500
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
502
if (nextMsg != OTRL_SMP_EXPECT1)
503
otrg_plugin_abort_smp(context);
505
otrg_dialog_socialist_millionaires(context);
508
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
510
if (nextMsg != OTRL_SMP_EXPECT2)
511
otrg_plugin_abort_smp(context);
513
otrg_dialog_update_smp(context, 0.6);
514
context->smstate->nextExpected = OTRL_SMP_EXPECT4;
517
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
519
if (nextMsg != OTRL_SMP_EXPECT3)
520
otrg_plugin_abort_smp(context);
522
otrg_dialog_update_smp(context, 1.0);
523
context->smstate->nextExpected = OTRL_SMP_EXPECT1;
526
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
528
if (nextMsg != OTRL_SMP_EXPECT4)
529
otrg_plugin_abort_smp(context);
531
otrg_dialog_update_smp(context, 1.0);
532
context->smstate->nextExpected = OTRL_SMP_EXPECT1;
535
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
537
otrg_dialog_update_smp(context, 0.0);
538
context->smstate->nextExpected = OTRL_SMP_EXPECT1;
392
543
otrl_tlv_free(tlvs);
409
560
if (conv) otrg_dialog_new_conv(conv);
563
static void process_conv_updated(PurpleConversation *conv,
564
PurpleConvUpdateType type, void *data)
566
/* See if someone's trying to turn logging on for this conversation,
567
* and we don't want them to. */
568
if (type == PURPLE_CONV_UPDATE_LOGGING) {
569
ConnContext *context;
571
PurpleAccount *account = purple_conversation_get_account(conv);
572
otrg_ui_get_prefs(&prefs, account, purple_conversation_get_name(conv));
574
context = otrg_plugin_conv_to_context(conv);
575
if (context && prefs.avoid_logging_otr &&
576
context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
577
conv->logging == TRUE) {
578
purple_conversation_set_logging(conv, FALSE);
412
583
static void process_connection_change(PurpleConnection *conn, void *data)
414
585
/* If we log in or out of a connection, make sure all of the OTR
441
612
proto = purple_account_get_protocol_id(acct);
442
613
if (!otrg_plugin_proto_supports_otr(proto)) return;
444
act = purple_menu_action_new("OTR Settings", (PurpleCallback)otr_options_cb,
615
act = purple_menu_action_new(_("OTR Settings"),
616
(PurpleCallback)otr_options_cb, NULL, NULL);
446
617
*menu = g_list_append(*menu, act);
490
/* Find the PurpleConversation appropriate to the given ConnContext. If
670
/* Find the PurpleConversation appropriate to the given userinfo. If
491
671
* one doesn't yet exist, create it if force_create is true. */
492
PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
672
PurpleConversation *otrg_plugin_userinfo_to_conv(const char *accountname,
673
const char *protocol, const char *username, int force_create)
495
675
PurpleAccount *account;
496
676
PurpleConversation *conv;
498
account = purple_accounts_find(context->accountname, context->protocol);
678
account = purple_accounts_find(accountname, protocol);
499
679
if (account == NULL) return NULL;
501
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, context->username, account);
681
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
502
683
if (conv == NULL && force_create) {
503
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, context->username);
684
conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username);
690
/* Find the PurpleConversation appropriate to the given ConnContext. If
691
* one doesn't yet exist, create it if force_create is true. */
692
PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
695
return otrg_plugin_userinfo_to_conv(context->accountname,
696
context->protocol, context->username, force_create);
509
699
/* What level of trust do we have in the privacy of this ConnContext? */
510
700
TrustLevel otrg_plugin_context_to_trust(ConnContext *context)
732
/* Read the maxmsgsizes from a FILE* into the given GHashTable.
733
* The FILE* must be open for reading. */
734
static void mms_read_FILEp(FILE *mmsf, GHashTable *ght)
737
size_t maxsize = sizeof(storeline);
741
while(fgets(storeline, maxsize, mmsf)) {
748
/* Parse the line, which should be of the form:
749
* protocol\tmaxmsgsize\n */
750
protocol = storeline;
751
tab = strchr(protocol, '\t');
756
tab = strchr(mms, '\t');
758
eol = strchr(mms, '\r');
759
if (!eol) eol = strchr(mms, '\n');
763
prot_in_table = strdup(protocol);
764
mms_in_table = malloc(sizeof(int));
765
*mms_in_table = atoi(mms);
766
g_hash_table_insert(ght, prot_in_table, mms_in_table);
770
static void otrg_str_free(gpointer data)
775
static void otrg_int_free(gpointer data)
780
static void otrg_init_mms_table()
782
/* Hardcoded defaults for maximum message sizes for various
783
* protocols. These can be overridden in the user's MAXMSGSIZEFNAME
785
static const struct s_OtrgIdProtPair {
788
} mmsPairs[8] = {{"prpl-msn", 1409}, {"prpl-icq", 2346},
789
{"prpl-aim", 2343}, {"prpl-yahoo", 832}, {"prpl-gg", 1999},
790
{"prpl-irc", 417}, {"prpl-oscar", 2343}, {NULL, 0}};
792
gchar *maxmsgsizefile;
795
mms_table = g_hash_table_new_full(g_str_hash, g_str_equal,
796
otrg_str_free, otrg_int_free);
798
for (i=0; mmsPairs[i].protid != NULL; i++) {
799
char* nextprot = g_strdup(mmsPairs[i].protid);
800
int* nextsize = g_malloc(sizeof(int));
801
*nextsize = mmsPairs[i].maxmsgsize;
802
g_hash_table_insert(mms_table, nextprot, nextsize);
805
maxmsgsizefile = g_build_filename(purple_user_dir(),
806
MAXMSGSIZEFNAME, NULL);
808
if (maxmsgsizefile) {
809
mmsf = g_fopen(maxmsgsizefile, "rt");
810
/* Actually read the file here */
812
mms_read_FILEp(mmsf, mms_table);
815
g_free(maxmsgsizefile);
819
static void otrg_free_mms_table()
821
g_hash_table_destroy(mms_table);
542
824
static gboolean otr_plugin_load(PurplePlugin *handle)
544
826
gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME,
581
865
PURPLE_CALLBACK(process_sending_im), NULL);
582
866
purple_signal_connect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
583
867
PURPLE_CALLBACK(process_receiving_im), NULL);
868
purple_signal_connect(conv_handle, "conversation-updated",
869
otrg_plugin_handle, PURPLE_CALLBACK(process_conv_updated), NULL);
584
870
purple_signal_connect(conv_handle, "conversation-created",
585
871
otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create), NULL);
586
872
purple_signal_connect(conn_handle, "signed-on", otrg_plugin_handle,
606
895
otrl_userstate_free(otrg_plugin_userstate);
607
896
otrg_plugin_userstate = NULL;
898
otrg_free_mms_table();
609
900
purple_signal_disconnect(core_handle, "quitting", otrg_plugin_handle,
610
901
PURPLE_CALLBACK(process_quitting));
611
purple_signal_disconnect(conv_handle, "sending-im-msg", otrg_plugin_handle,
612
PURPLE_CALLBACK(process_sending_im));
613
purple_signal_disconnect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
614
PURPLE_CALLBACK(process_receiving_im));
902
purple_signal_disconnect(conv_handle, "sending-im-msg",
903
otrg_plugin_handle, PURPLE_CALLBACK(process_sending_im));
904
purple_signal_disconnect(conv_handle, "receiving-im-msg",
905
otrg_plugin_handle, PURPLE_CALLBACK(process_receiving_im));
906
purple_signal_disconnect(conv_handle, "conversation-updated",
907
otrg_plugin_handle, PURPLE_CALLBACK(process_conv_updated));
615
908
purple_signal_disconnect(conv_handle, "conversation-created",
616
909
otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create));
617
910
purple_signal_disconnect(conn_handle, "signed-on", otrg_plugin_handle,
624
917
purple_conversation_foreach(otrg_dialog_remove_conv);
919
otrg_dialog_cleanup();
629
925
/* Return 1 if the given protocol supports OTR, 0 otherwise. */
630
926
int otrg_plugin_proto_supports_otr(const char *proto)
632
/* IRC is the only protocol we know of that OTR doesn't work on (its
633
* maximum message size is too small to fit a Key Exchange Message). */
634
if (proto && !strcmp(proto, "prpl-irc")) {
928
/* Right now, OTR should work on all protocols, possibly
929
* with the help of fragmentation. */
642
static PurplePluginUiInfo ui_info =
935
static PidginPluginUiInfo ui_info =
644
937
otrg_gtk_ui_make_widget
662
955
2, /* major version */
663
956
0, /* minor version */
665
PURPLE_PLUGIN_STANDARD, /* type */
958
PURPLE_PLUGIN_STANDARD, /* type */
666
959
PLUGIN_TYPE, /* ui_requirement */
668
961
NULL, /* dependencies */
669
PURPLE_PRIORITY_DEFAULT, /* priority */
962
PURPLE_PRIORITY_DEFAULT, /* priority */
671
"Off-the-Record Messaging", /* name */
672
PIDGIN_OTR_VERSION, /* version */
674
"Provides private and secure conversations",
676
"Preserves the privacy of IM communications by providing "
677
"encryption, authentication, deniability, and perfect "
965
PIDGIN_OTR_VERSION, /* version */
967
NULL, /* description */
680
"Nikita Borisov and Ian Goldberg\n\t\t\t<otr@cypherpunks.ca>",
681
"http://www.cypherpunks.ca/otr/", /* homepage */
969
"Ian Goldberg, Rob Smits,\n"
970
"\t\t\tChris Alexander, Nikita Borisov\n"
971
"\t\t\t<otr@cypherpunks.ca>",
972
"http://otr.cypherpunks.ca/", /* homepage */
683
otr_plugin_load, /* load */
684
otr_plugin_unload, /* unload */
974
otr_plugin_load, /* load */
975
otr_plugin_unload, /* unload */
685
976
NULL, /* destroy */
687
978
UI_INFO, /* ui_info */
699
990
otrg_dialog_set_ui_ops(otrg_gtk_dialog_get_ui_ops());
994
/* Make key generation use /dev/urandom instead of /dev/random */
995
gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0);
702
998
/* Initialize the OTR library */
1002
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
1003
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1006
info.name = _("Off-the-Record Messaging");
1007
info.summary = _("Provides private and secure conversations");
1008
info.description = _("Preserves the privacy of IM communications "
1009
"by providing encryption, authentication, "
1010
"deniability, and perfect forward secrecy.");
706
1013
PURPLE_INIT_PLUGIN(otr, __init_plugin, info)