~ubuntu-branches/ubuntu/natty/pidgin-otr/natty-updates

« back to all changes in this revision

Viewing changes to otr-plugin.c

  • Committer: Bazaar Package Importer
  • Author(s): Thibaut VARENE
  • Date: 2007-05-08 17:21:54 UTC
  • Revision ID: james.westby@ubuntu.com-20070508172154-rl73hykhkfbbvhzm
Tags: upstream-3.0.0+cvs20070508
ImportĀ upstreamĀ versionĀ 3.0.0+cvs20070508

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Off-the-Record Messaging plugin for pidgin
 
3
 *  Copyright (C) 2004-2005  Nikita Borisov and Ian Goldberg
 
4
 *                           <otr@cypherpunks.ca>
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of version 2 of the GNU General Public License as
 
8
 *  published by the Free Software Foundation.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 */
 
19
 
 
20
/* config.h */
 
21
#ifdef HAVE_CONFIG_H
 
22
#include "config.h"
 
23
#endif
 
24
 
 
25
/* system headers */
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <assert.h>
 
29
 
 
30
/* libgcrypt headers */
 
31
#include <gcrypt.h>
 
32
 
 
33
/* purple headers */
 
34
#include "pidgin.h"
 
35
#include "notify.h"
 
36
#include "version.h"
 
37
#include "util.h"
 
38
#include "debug.h"
 
39
#include "core.h"
 
40
 
 
41
#ifdef USING_GTK
 
42
/* purple GTK headers */
 
43
#include "gtkplugin.h"
 
44
#endif
 
45
 
 
46
/* libotr headers */
 
47
#include <libotr/privkey.h>
 
48
#include <libotr/proto.h>
 
49
#include <libotr/message.h>
 
50
#include <libotr/userstate.h>
 
51
 
 
52
/* purple-otr headers */
 
53
#include "ui.h"
 
54
#include "dialogs.h"
 
55
#include "otr-plugin.h"
 
56
 
 
57
#ifdef USING_GTK
 
58
/* purple-otr GTK headers */
 
59
#include "gtk-ui.h"
 
60
#include "gtk-dialog.h"
 
61
#endif
 
62
 
 
63
/* If we're using glib on Windows, we need to use g_fopen to open files.
 
64
 * On other platforms, it's also safe to use it.  If we're not using
 
65
 * glib, just use fopen. */
 
66
#ifdef USING_GTK
 
67
/* If we're cross-compiling, this might be wrong, so fix it. */
 
68
#ifdef WIN32
 
69
#undef G_OS_UNIX
 
70
#define G_OS_WIN32
 
71
#endif
 
72
#include <glib/gstdio.h>
 
73
#else
 
74
#define g_fopen fopen
 
75
#endif
 
76
 
 
77
PurplePlugin *otrg_plugin_handle;
 
78
 
 
79
/* We'll only use the one OtrlUserState. */
 
80
OtrlUserState otrg_plugin_userstate = NULL;
 
81
 
 
82
/* Send an IM from the given account to the given recipient.  Display an
 
83
 * error dialog if that account isn't currently logged in. */
 
84
void otrg_plugin_inject_message(PurpleAccount *account, const char *recipient,
 
85
        const char *message)
 
86
{
 
87
    PurpleConnection *connection;
 
88
 
 
89
    connection = purple_account_get_connection(account);
 
90
    if (!connection) {
 
91
        const char *protocol = purple_account_get_protocol_id(account);
 
92
        const char *accountname = purple_account_get_username(account);
 
93
        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");
 
97
        otrg_dialog_notify_error(accountname, protocol, recipient,
 
98
                "Not connected", msg, NULL);
 
99
        g_free(msg);
 
100
        return;
 
101
    }
 
102
    serv_send_im(connection, recipient, message, 0);
 
103
}
 
104
 
 
105
static OtrlPolicy policy_cb(void *opdata, ConnContext *context)
 
106
{
 
107
    PurpleAccount *account;
 
108
    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
 
109
 
 
110
    if (!context) return policy;
 
111
 
 
112
    account = purple_accounts_find(context->accountname, context->protocol);
 
113
    if (!account) return policy;
 
114
 
 
115
    return otrg_ui_find_policy(account, context->username);
 
116
}
 
117
 
 
118
static const char *protocol_name_cb(void *opdata, const char *protocol)
 
119
{
 
120
    PurplePlugin *p = purple_find_prpl(protocol);
 
121
    if (!p) return NULL;
 
122
    return p->info->name;
 
123
}
 
124
 
 
125
static void protocol_name_free_cb(void *opdata, const char *protocol_name)
 
126
{
 
127
    /* Do nothing, since we didn't actually allocate any memory in
 
128
     * protocol_name_cb. */
 
129
}
 
130
 
 
131
/* Generate a private key for the given accountname/protocol */
 
132
void otrg_plugin_create_privkey(const char *accountname,
 
133
        const char *protocol)
 
134
{
 
135
    OtrgDialogWaitHandle waithandle;
 
136
    FILE *privf;
 
137
 
 
138
    gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME, NULL);
 
139
    if (!privkeyfile) {
 
140
        fprintf(stderr, "Out of memory building filenames!\n");
 
141
        return;
 
142
    }
 
143
    privf = g_fopen(privkeyfile, "w+b");
 
144
    g_free(privkeyfile);
 
145
    if (!privf) {
 
146
        fprintf(stderr, "Could not write private key file\n");
 
147
        return;
 
148
    }
 
149
 
 
150
    waithandle = otrg_dialog_private_key_wait_start(accountname, protocol);
 
151
 
 
152
    /* Generate the key */
 
153
    otrl_privkey_generate_FILEp(otrg_plugin_userstate, privf,
 
154
            accountname, protocol);
 
155
    fclose(privf);
 
156
    otrg_ui_update_fingerprint();
 
157
 
 
158
    /* Mark the dialog as done. */
 
159
    otrg_dialog_private_key_wait_done(waithandle);
 
160
}
 
161
 
 
162
static void create_privkey_cb(void *opdata, const char *accountname,
 
163
        const char *protocol)
 
164
{
 
165
    otrg_plugin_create_privkey(accountname, protocol);
 
166
}
 
167
 
 
168
static int is_logged_in_cb(void *opdata, const char *accountname,
 
169
        const char *protocol, const char *recipient)
 
170
{
 
171
    PurpleAccount *account;
 
172
    PurpleBuddy *buddy;
 
173
 
 
174
    account = purple_accounts_find(accountname, protocol);
 
175
    if (!account) return -1;
 
176
 
 
177
    buddy = purple_find_buddy(account, recipient);
 
178
    if (!buddy) return -1;
 
179
 
 
180
    return (PURPLE_BUDDY_IS_ONLINE(buddy));
 
181
}
 
182
 
 
183
static void inject_message_cb(void *opdata, const char *accountname,
 
184
        const char *protocol, const char *recipient, const char *message)
 
185
{
 
186
    PurpleAccount *account = purple_accounts_find(accountname, protocol);
 
187
    if (!account) {
 
188
        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");
 
191
        otrg_dialog_notify_error(accountname, protocol, recipient,
 
192
                "Unknown account", msg, NULL);
 
193
        g_free(msg);
 
194
        return;
 
195
    }
 
196
    otrg_plugin_inject_message(account, recipient, message);
 
197
}
 
198
 
 
199
static void notify_cb(void *opdata, OtrlNotifyLevel level,
 
200
        const char *accountname, const char *protocol, const char *username,
 
201
        const char *title, const char *primary, const char *secondary)
 
202
{
 
203
    PurpleNotifyMsgType purplelevel = PURPLE_NOTIFY_MSG_ERROR;
 
204
 
 
205
    switch (level) {
 
206
        case OTRL_NOTIFY_ERROR:
 
207
            purplelevel = PURPLE_NOTIFY_MSG_ERROR;
 
208
            break;
 
209
        case OTRL_NOTIFY_WARNING:
 
210
            purplelevel = PURPLE_NOTIFY_MSG_WARNING;
 
211
            break;
 
212
        case OTRL_NOTIFY_INFO:
 
213
            purplelevel = PURPLE_NOTIFY_MSG_INFO;
 
214
            break;
 
215
    }
 
216
 
 
217
    otrg_dialog_notify_message(purplelevel, accountname, protocol,
 
218
            username, title, primary, secondary);
 
219
}
 
220
 
 
221
static int display_otr_message_cb(void *opdata, const char *accountname,
 
222
        const char *protocol, const char *username, const char *msg)
 
223
{
 
224
    return otrg_dialog_display_otr_message(accountname, protocol,
 
225
            username, msg);
 
226
}
 
227
 
 
228
static void update_context_list_cb(void *opdata)
 
229
{
 
230
    otrg_ui_update_keylist();
 
231
}
 
232
 
 
233
static void confirm_fingerprint_cb(void *opdata, OtrlUserState us,
 
234
        const char *accountname, const char *protocol, const char *username,
 
235
        unsigned char fingerprint[20])
 
236
{
 
237
    otrg_dialog_unknown_fingerprint(us, accountname, protocol, username,
 
238
            fingerprint);
 
239
}
 
240
 
 
241
static void write_fingerprints_cb(void *opdata)
 
242
{
 
243
    otrg_plugin_write_fingerprints();
 
244
}
 
245
 
 
246
static void gone_secure_cb(void *opdata, ConnContext *context)
 
247
{
 
248
    otrg_dialog_connected(context);
 
249
}
 
250
 
 
251
static void gone_insecure_cb(void *opdata, ConnContext *context)
 
252
{
 
253
    otrg_dialog_disconnected(context);
 
254
}
 
255
 
 
256
static void still_secure_cb(void *opdata, ConnContext *context, int is_reply)
 
257
{
 
258
    if (is_reply == 0) {
 
259
        otrg_dialog_stillconnected(context);
 
260
    }
 
261
}
 
262
 
 
263
static void log_message_cb(void *opdata, const char *message)
 
264
{
 
265
    purple_debug_info("otr", message);
 
266
}
 
267
 
 
268
static OtrlMessageAppOps ui_ops = {
 
269
    policy_cb,
 
270
    create_privkey_cb,
 
271
    is_logged_in_cb,
 
272
    inject_message_cb,
 
273
    notify_cb,
 
274
    display_otr_message_cb,
 
275
    update_context_list_cb,
 
276
    protocol_name_cb,
 
277
    protocol_name_free_cb,
 
278
    confirm_fingerprint_cb,
 
279
    write_fingerprints_cb,
 
280
    gone_secure_cb,
 
281
    gone_insecure_cb,
 
282
    still_secure_cb,
 
283
    log_message_cb
 
284
};
 
285
 
 
286
static void process_sending_im(PurpleAccount *account, char *who, char **message,
 
287
        void *m)
 
288
{
 
289
    char *newmessage = NULL;
 
290
    const char *accountname = purple_account_get_username(account);
 
291
    const char *protocol = purple_account_get_protocol_id(account);
 
292
    char *username;
 
293
    gcry_error_t err;
 
294
 
 
295
    if (!who || !message || !*message)
 
296
        return;
 
297
 
 
298
    username = strdup(purple_normalize(account, who));
 
299
 
 
300
    err = otrl_message_sending(otrg_plugin_userstate, &ui_ops, NULL,
 
301
            accountname, protocol, username, *message, NULL, &newmessage,
 
302
            NULL, NULL);
 
303
 
 
304
    if (err && newmessage == NULL) {
 
305
        /* Be *sure* not to send out plaintext */
 
306
        char *ourm = strdup("");
 
307
        free(*message);
 
308
        *message = ourm;
 
309
    } else if (newmessage) {
 
310
        char *ourm = malloc(strlen(newmessage) + 1);
 
311
        if (ourm) {
 
312
            strcpy(ourm, newmessage);
 
313
        }
 
314
        otrl_message_free(newmessage);
 
315
        free(*message);
 
316
        *message = ourm;
 
317
    }
 
318
    free(username);
 
319
}
 
320
 
 
321
/* Send the default OTR Query message to the correspondent of the given
 
322
 * context, from the given account.  [account is actually a
 
323
 * PurpleAccount*, but it's declared here as void* so this can be passed
 
324
 * as a callback.] */
 
325
void otrg_plugin_send_default_query(ConnContext *context, void *vaccount)
 
326
{
 
327
    PurpleAccount *account = vaccount;
 
328
    char *msg = otrl_proto_default_query_msg(context->accountname,
 
329
            otrg_ui_find_policy(account, context->username));
 
330
    otrg_plugin_inject_message(account, context->username,
 
331
            msg ? msg : "?OTRv2?");
 
332
    free(msg);
 
333
}
 
334
 
 
335
/* Send the default OTR Query message to the correspondent of the given
 
336
 * conversation. */
 
337
void otrg_plugin_send_default_query_conv(PurpleConversation *conv)
 
338
{
 
339
    PurpleAccount *account;
 
340
    const char *username, *accountname;
 
341
    char *msg;
 
342
    
 
343
    account = purple_conversation_get_account(conv);
 
344
    accountname = purple_account_get_username(account);
 
345
    username = purple_conversation_get_name(conv);
 
346
    
 
347
    msg = otrl_proto_default_query_msg(accountname,
 
348
            otrg_ui_find_policy(account, username));
 
349
    otrg_plugin_inject_message(account, username, msg ? msg : "?OTRv2?");
 
350
    free(msg);
 
351
}
 
352
 
 
353
static gboolean process_receiving_im(PurpleAccount *account, char **who, 
 
354
        char **message, int *flags, void *m)
 
355
{
 
356
    char *newmessage = NULL;
 
357
    OtrlTLV *tlvs = NULL;
 
358
    OtrlTLV *tlv = NULL;
 
359
    char *username;
 
360
    gboolean res;
 
361
    const char *accountname;
 
362
    const char *protocol;
 
363
 
 
364
    if (!who || !*who || !message || !*message)
 
365
        return 0;
 
366
 
 
367
    username = strdup(purple_normalize(account, *who));
 
368
    accountname = purple_account_get_username(account);
 
369
    protocol = purple_account_get_protocol_id(account);
 
370
 
 
371
    res = otrl_message_receiving(otrg_plugin_userstate, &ui_ops, NULL,
 
372
            accountname, protocol, username, *message,
 
373
            &newmessage, &tlvs, NULL, NULL);
 
374
 
 
375
    if (newmessage) {
 
376
        char *ourm = malloc(strlen(newmessage) + 1);
 
377
        if (ourm) {
 
378
            strcpy(ourm, newmessage);
 
379
        }
 
380
        otrl_message_free(newmessage);
 
381
        free(*message);
 
382
        *message = ourm;
 
383
    }
 
384
 
 
385
    tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
 
386
    if (tlv) {
 
387
        /* Notify the user that the other side disconnected. */
 
388
        otrg_dialog_finished(accountname, protocol, username);
 
389
        otrg_ui_update_keylist();
 
390
    }
 
391
    
 
392
    otrl_tlv_free(tlvs);
 
393
 
 
394
    free(username);
 
395
 
 
396
    /* If we're supposed to ignore this incoming message (because it's a
 
397
     * protocol message), set it to NULL, so that other plugins that
 
398
     * catch receiving-im-msg don't return 0, and cause it to be
 
399
     * displayed anyway. */
 
400
    if (res) {
 
401
        free(*message);
 
402
        *message = NULL;
 
403
    }
 
404
    return res;
 
405
}
 
406
 
 
407
static void process_conv_create(PurpleConversation *conv, void *data)
 
408
{
 
409
    if (conv) otrg_dialog_new_conv(conv);
 
410
}
 
411
 
 
412
static void process_connection_change(PurpleConnection *conn, void *data)
 
413
{
 
414
    /* If we log in or out of a connection, make sure all of the OTR
 
415
     * buttons are in the appropriate sensitive/insensitive state. */
 
416
    otrg_dialog_resensitize_all();
 
417
}
 
418
 
 
419
static void otr_options_cb(PurpleBlistNode *node, gpointer user_data)
 
420
{
 
421
    /* We've already checked PURPLE_BLIST_NODE_IS_BUDDY(node) */
 
422
    PurpleBuddy *buddy = (PurpleBuddy *)node;
 
423
 
 
424
    /* Modify the settings for this buddy */
 
425
    otrg_ui_config_buddy(buddy);
 
426
}
 
427
 
 
428
static void supply_extended_menu(PurpleBlistNode *node, GList **menu)
 
429
{
 
430
    PurpleMenuAction *act;
 
431
    PurpleBuddy *buddy;
 
432
    PurpleAccount *acct;
 
433
    const char *proto;
 
434
 
 
435
    if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return;
 
436
 
 
437
    /* Extract the account, and then the protocol, for this buddy */
 
438
    buddy = (PurpleBuddy *)node;
 
439
    acct = buddy->account;
 
440
    if (acct == NULL) return;
 
441
    proto = purple_account_get_protocol_id(acct);
 
442
    if (!otrg_plugin_proto_supports_otr(proto)) return;
 
443
 
 
444
    act = purple_menu_action_new("OTR Settings", (PurpleCallback)otr_options_cb,
 
445
            NULL, NULL);
 
446
    *menu = g_list_append(*menu, act);
 
447
}
 
448
 
 
449
/* Disconnect a context, sending a notice to the other side, if
 
450
 * appropriate. */
 
451
void otrg_plugin_disconnect(ConnContext *context)
 
452
{
 
453
    otrl_message_disconnect(otrg_plugin_userstate, &ui_ops, NULL,
 
454
            context->accountname, context->protocol, context->username);
 
455
}
 
456
 
 
457
/* Write the fingerprints to disk. */
 
458
void otrg_plugin_write_fingerprints(void)
 
459
{
 
460
    FILE *storef;
 
461
    gchar *storefile = g_build_filename(purple_user_dir(), STOREFNAME, NULL);
 
462
    storef = g_fopen(storefile, "wb");
 
463
    g_free(storefile);
 
464
    if (!storef) return;
 
465
    otrl_privkey_write_fingerprints_FILEp(otrg_plugin_userstate, storef);
 
466
    fclose(storef);
 
467
}
 
468
 
 
469
/* Find the ConnContext appropriate to a given PurpleConversation. */
 
470
ConnContext *otrg_plugin_conv_to_context(PurpleConversation *conv)
 
471
{
 
472
    PurpleAccount *account;
 
473
    char *username;
 
474
    const char *accountname, *proto;
 
475
    ConnContext *context;
 
476
 
 
477
    account = purple_conversation_get_account(conv);
 
478
    accountname = purple_account_get_username(account);
 
479
    proto = purple_account_get_protocol_id(account);
 
480
    username = g_strdup(
 
481
            purple_normalize(account, purple_conversation_get_name(conv)));
 
482
 
 
483
    context = otrl_context_find(otrg_plugin_userstate, username, accountname,
 
484
            proto, 0, NULL, NULL, NULL);
 
485
    g_free(username);
 
486
 
 
487
    return context;
 
488
}
 
489
 
 
490
/* Find the PurpleConversation appropriate to the given ConnContext.  If
 
491
 * one doesn't yet exist, create it if force_create is true. */
 
492
PurpleConversation *otrg_plugin_context_to_conv(ConnContext *context,
 
493
        int force_create)
 
494
{
 
495
    PurpleAccount *account;
 
496
    PurpleConversation *conv;
 
497
 
 
498
    account = purple_accounts_find(context->accountname, context->protocol);
 
499
    if (account == NULL) return NULL;
 
500
 
 
501
    conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, context->username, account);
 
502
    if (conv == NULL && force_create) {
 
503
        conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, context->username);
 
504
    }
 
505
 
 
506
    return conv;
 
507
}
 
508
 
 
509
/* What level of trust do we have in the privacy of this ConnContext? */
 
510
TrustLevel otrg_plugin_context_to_trust(ConnContext *context)
 
511
{
 
512
    TrustLevel level = TRUST_NOT_PRIVATE;
 
513
 
 
514
    if (context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
 
515
        if (context->active_fingerprint->trust &&
 
516
                context->active_fingerprint->trust[0] != '\0') {
 
517
            level = TRUST_PRIVATE;
 
518
        } else {
 
519
            level = TRUST_UNVERIFIED;
 
520
        }
 
521
    } else if (context && context->msgstate == OTRL_MSGSTATE_FINISHED) {
 
522
        level = TRUST_FINISHED;
 
523
    }
 
524
 
 
525
    return level;
 
526
}
 
527
 
 
528
/* Send the OTRL_TLV_DISCONNECTED packets when we're about to quit. */
 
529
static void process_quitting(void)
 
530
{
 
531
    ConnContext *context = otrg_plugin_userstate->context_root;
 
532
    while(context) {
 
533
        ConnContext *next = context->next;
 
534
        if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
 
535
                context->protocol_version > 1) {
 
536
            otrg_plugin_disconnect(context);
 
537
        }
 
538
        context = next;
 
539
    }
 
540
}
 
541
 
 
542
static gboolean otr_plugin_load(PurplePlugin *handle)
 
543
{
 
544
    gchar *privkeyfile = g_build_filename(purple_user_dir(), PRIVKEYFNAME,
 
545
            NULL);
 
546
    gchar *storefile = g_build_filename(purple_user_dir(), STOREFNAME, NULL);
 
547
    void *conv_handle = purple_conversations_get_handle();
 
548
    void *conn_handle = purple_connections_get_handle();
 
549
    void *blist_handle = purple_blist_get_handle();
 
550
    void *core_handle = purple_get_core();
 
551
    FILE *privf;
 
552
    FILE *storef;
 
553
 
 
554
    if (!privkeyfile || !storefile) {
 
555
        g_free(privkeyfile);
 
556
        g_free(storefile);
 
557
        return 0;
 
558
    }
 
559
 
 
560
    privf = g_fopen(privkeyfile, "rb");
 
561
    storef = g_fopen(storefile, "rb");
 
562
    g_free(privkeyfile);
 
563
    g_free(storefile);
 
564
 
 
565
    otrg_plugin_handle = handle;
 
566
 
 
567
    /* Make our OtrlUserState; we'll only use the one. */
 
568
    otrg_plugin_userstate = otrl_userstate_create();
 
569
 
 
570
    otrl_privkey_read_FILEp(otrg_plugin_userstate, privf);
 
571
    otrl_privkey_read_fingerprints_FILEp(otrg_plugin_userstate, storef,
 
572
            NULL, NULL);
 
573
    if (privf) fclose(privf);
 
574
    if (storef) fclose(storef);
 
575
 
 
576
    otrg_ui_update_fingerprint();
 
577
 
 
578
    purple_signal_connect(core_handle, "quitting", otrg_plugin_handle,
 
579
            PURPLE_CALLBACK(process_quitting), NULL);
 
580
    purple_signal_connect(conv_handle, "sending-im-msg", otrg_plugin_handle,
 
581
            PURPLE_CALLBACK(process_sending_im), NULL);
 
582
    purple_signal_connect(conv_handle, "receiving-im-msg", otrg_plugin_handle,
 
583
            PURPLE_CALLBACK(process_receiving_im), NULL);
 
584
    purple_signal_connect(conv_handle, "conversation-created",
 
585
            otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create), NULL);
 
586
    purple_signal_connect(conn_handle, "signed-on", otrg_plugin_handle,
 
587
            PURPLE_CALLBACK(process_connection_change), NULL);
 
588
    purple_signal_connect(conn_handle, "signed-off", otrg_plugin_handle,
 
589
            PURPLE_CALLBACK(process_connection_change), NULL);
 
590
    purple_signal_connect(blist_handle, "blist-node-extended-menu",
 
591
            otrg_plugin_handle, PURPLE_CALLBACK(supply_extended_menu), NULL);
 
592
 
 
593
    purple_conversation_foreach(otrg_dialog_new_conv);
 
594
 
 
595
    return 1;
 
596
}
 
597
 
 
598
static gboolean otr_plugin_unload(PurplePlugin *handle)
 
599
{
 
600
    void *conv_handle = purple_conversations_get_handle();
 
601
    void *conn_handle = purple_connections_get_handle();
 
602
    void *blist_handle = purple_blist_get_handle();
 
603
    void *core_handle = purple_get_core();
 
604
 
 
605
    /* Clean up all of our state. */
 
606
    otrl_userstate_free(otrg_plugin_userstate);
 
607
    otrg_plugin_userstate = NULL;
 
608
 
 
609
    purple_signal_disconnect(core_handle, "quitting", otrg_plugin_handle,
 
610
            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));
 
615
    purple_signal_disconnect(conv_handle, "conversation-created",
 
616
            otrg_plugin_handle, PURPLE_CALLBACK(process_conv_create));
 
617
    purple_signal_disconnect(conn_handle, "signed-on", otrg_plugin_handle,
 
618
            PURPLE_CALLBACK(process_connection_change));
 
619
    purple_signal_disconnect(conn_handle, "signed-off", otrg_plugin_handle,
 
620
            PURPLE_CALLBACK(process_connection_change));
 
621
    purple_signal_disconnect(blist_handle, "blist-node-extended-menu",
 
622
            otrg_plugin_handle, PURPLE_CALLBACK(supply_extended_menu));
 
623
 
 
624
    purple_conversation_foreach(otrg_dialog_remove_conv);
 
625
 
 
626
    return 1;
 
627
}
 
628
 
 
629
/* Return 1 if the given protocol supports OTR, 0 otherwise. */
 
630
int otrg_plugin_proto_supports_otr(const char *proto)
 
631
{
 
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")) {
 
635
        return 0;
 
636
    }
 
637
    return 1;
 
638
}
 
639
 
 
640
#ifdef USING_GTK
 
641
 
 
642
static PurplePluginUiInfo ui_info =
 
643
{
 
644
        otrg_gtk_ui_make_widget
 
645
};
 
646
 
 
647
#define UI_INFO &ui_info
 
648
#define PLUGIN_TYPE PIDGIN_PLUGIN_TYPE
 
649
 
 
650
#else
 
651
 
 
652
#define UI_INFO NULL
 
653
#define PLUGIN_TYPE ""
 
654
 
 
655
#endif
 
656
 
 
657
static PurplePluginInfo info =
 
658
{
 
659
        PURPLE_PLUGIN_MAGIC,
 
660
 
 
661
        /* Use the 2.0.x API */
 
662
        2,                                                /* major version  */
 
663
        0,                                                /* minor version  */
 
664
 
 
665
        PURPLE_PLUGIN_STANDARD,                             /* type           */
 
666
        PLUGIN_TYPE,                                      /* ui_requirement */
 
667
        0,                                                /* flags          */
 
668
        NULL,                                             /* dependencies   */
 
669
        PURPLE_PRIORITY_DEFAULT,                            /* priority       */
 
670
        "otr",                                            /* id             */
 
671
        "Off-the-Record Messaging",                       /* name           */
 
672
        PIDGIN_OTR_VERSION,                                 /* version        */
 
673
                                                          /* summary        */
 
674
        "Provides private and secure conversations",
 
675
                                                          /* description    */
 
676
        "Preserves the privacy of IM communications by providing "
 
677
            "encryption, authentication, deniability, and perfect "
 
678
            "forward secrecy.",
 
679
                                                          /* author         */
 
680
        "Nikita Borisov and Ian Goldberg\n\t\t\t<otr@cypherpunks.ca>",
 
681
        "http://www.cypherpunks.ca/otr/",                 /* homepage       */
 
682
 
 
683
        otr_plugin_load,                                 /* load           */
 
684
        otr_plugin_unload,                               /* unload         */
 
685
        NULL,                                             /* destroy        */
 
686
 
 
687
        UI_INFO,                                          /* ui_info        */
 
688
        NULL,                                             /* extra_info     */
 
689
        NULL,                                             /* prefs_info     */
 
690
        NULL                                              /* actions        */
 
691
};
 
692
 
 
693
static void
 
694
__init_plugin(PurplePlugin *plugin)
 
695
{
 
696
    /* Set up the UI ops */
 
697
#ifdef USING_GTK
 
698
    otrg_ui_set_ui_ops(otrg_gtk_ui_get_ui_ops());
 
699
    otrg_dialog_set_ui_ops(otrg_gtk_dialog_get_ui_ops());
 
700
#endif
 
701
 
 
702
    /* Initialize the OTR library */
 
703
    OTRL_INIT;
 
704
}
 
705
 
 
706
PURPLE_INIT_PLUGIN(otr, __init_plugin, info)