comparison libpurple/protocols/silc10/buddy.c @ 17567:ba1b50f114f6

Duplicate the current SILC prpl as silc10 for backwards compatibility with SILC Toolkit 1.0
author Stu Tomlinson <stu@nosnilmot.com>
date Sat, 09 Jun 2007 16:39:00 +0000
parents
children ab6d2763b8d8
comparison
equal deleted inserted replaced
17566:016eee704a96 17567:ba1b50f114f6
1 /*
2
3 silcpurple_buddy.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2004 Pekka Riikonen
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 */
19
20 #include "silcincludes.h"
21 #include "silcclient.h"
22 #include "silcpurple.h"
23 #include "wb.h"
24
25 /***************************** Key Agreement *********************************/
26
27 static void
28 silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data);
29
30 static void
31 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
32 gboolean force_local);
33
34 typedef struct {
35 char *nick;
36 PurpleConnection *gc;
37 } *SilcPurpleResolve;
38
39 static void
40 silcpurple_buddy_keyagr_resolved(SilcClient client,
41 SilcClientConnection conn,
42 SilcClientEntry *clients,
43 SilcUInt32 clients_count,
44 void *context)
45 {
46 PurpleConnection *gc = client->application;
47 SilcPurpleResolve r = context;
48 char tmp[256];
49
50 if (!clients) {
51 g_snprintf(tmp, sizeof(tmp),
52 _("User %s is not present in the network"), r->nick);
53 purple_notify_error(gc, _("Key Agreement"),
54 _("Cannot perform the key agreement"), tmp);
55 silc_free(r->nick);
56 silc_free(r);
57 return;
58 }
59
60 silcpurple_buddy_keyagr_do(gc, r->nick, FALSE);
61 silc_free(r->nick);
62 silc_free(r);
63 }
64
65 typedef struct {
66 gboolean responder;
67 } *SilcPurpleKeyAgr;
68
69 static void
70 silcpurple_buddy_keyagr_cb(SilcClient client,
71 SilcClientConnection conn,
72 SilcClientEntry client_entry,
73 SilcKeyAgreementStatus status,
74 SilcSKEKeyMaterial *key,
75 void *context)
76 {
77 PurpleConnection *gc = client->application;
78 SilcPurple sg = gc->proto_data;
79 SilcPurpleKeyAgr a = context;
80
81 if (!sg->conn)
82 return;
83
84 switch (status) {
85 case SILC_KEY_AGREEMENT_OK:
86 {
87 PurpleConversation *convo;
88 char tmp[128];
89
90 /* Set the private key for this client */
91 silc_client_del_private_message_key(client, conn, client_entry);
92 silc_client_add_private_message_key_ske(client, conn, client_entry,
93 NULL, NULL, key, a->responder);
94 silc_ske_free_key_material(key);
95
96
97 /* Open IM window */
98 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
99 client_entry->nickname, sg->account);
100 if (convo) {
101 /* we don't have windows in the core anymore...but we may want to
102 * provide some method for asking the UI to show the window
103 purple_conv_window_show(purple_conversation_get_window(convo));
104 */
105 } else {
106 convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account,
107 client_entry->nickname);
108 }
109 g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
110 purple_conversation_set_title(convo, tmp);
111 }
112 break;
113
114 case SILC_KEY_AGREEMENT_ERROR:
115 purple_notify_error(gc, _("Key Agreement"),
116 _("Error occurred during key agreement"), NULL);
117 break;
118
119 case SILC_KEY_AGREEMENT_FAILURE:
120 purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
121 break;
122
123 case SILC_KEY_AGREEMENT_TIMEOUT:
124 purple_notify_error(gc, _("Key Agreement"),
125 _("Timeout during key agreement"), NULL);
126 break;
127
128 case SILC_KEY_AGREEMENT_ABORTED:
129 purple_notify_error(gc, _("Key Agreement"),
130 _("Key agreement was aborted"), NULL);
131 break;
132
133 case SILC_KEY_AGREEMENT_ALREADY_STARTED:
134 purple_notify_error(gc, _("Key Agreement"),
135 _("Key agreement is already started"), NULL);
136 break;
137
138 case SILC_KEY_AGREEMENT_SELF_DENIED:
139 purple_notify_error(gc, _("Key Agreement"),
140 _("Key agreement cannot be started with yourself"),
141 NULL);
142 break;
143
144 default:
145 break;
146 }
147
148 silc_free(a);
149 }
150
151 static void
152 silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name,
153 gboolean force_local)
154 {
155 SilcPurple sg = gc->proto_data;
156 SilcClientEntry *clients;
157 SilcUInt32 clients_count;
158 char *local_ip = NULL, *remote_ip = NULL;
159 gboolean local = TRUE;
160 char *nickname;
161 SilcPurpleKeyAgr a;
162
163 if (!sg->conn || !name)
164 return;
165
166 if (!silc_parse_userfqdn(name, &nickname, NULL))
167 return;
168
169 /* Find client entry */
170 clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
171 &clients_count);
172 if (!clients) {
173 /* Resolve unknown user */
174 SilcPurpleResolve r = silc_calloc(1, sizeof(*r));
175 if (!r)
176 return;
177 r->nick = g_strdup(name);
178 r->gc = gc;
179 silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
180 silcpurple_buddy_keyagr_resolved, r);
181 silc_free(nickname);
182 return;
183 }
184
185 /* Resolve the local IP from the outgoing socket connection. We resolve
186 it to check whether we have a private range IP address or public IP
187 address. If we have public then we will assume that we are not behind
188 NAT and will provide automatically the point of connection to the
189 agreement. If we have private range address we assume that we are
190 behind NAT and we let the responder provide the point of connection.
191
192 The algorithm also checks the remote IP address of server connection.
193 If it is private range address and we have private range address we
194 assume that we are chatting in LAN and will provide the point of
195 connection.
196
197 Naturally this algorithm does not always get things right. */
198
199 if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
200 /* Check if the IP is private */
201 if (!force_local && silcpurple_ip_is_private(local_ip)) {
202 local = FALSE;
203
204 /* Local IP is private, resolve the remote server IP to see whether
205 we are talking to Internet or just on LAN. */
206 if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
207 &remote_ip))
208 if (silcpurple_ip_is_private(remote_ip))
209 /* We assume we are in LAN. Let's provide
210 the connection point. */
211 local = TRUE;
212 }
213 }
214
215 if (force_local)
216 local = TRUE;
217
218 if (local && !local_ip)
219 local_ip = silc_net_localip();
220
221 a = silc_calloc(1, sizeof(*a));
222 if (!a)
223 return;
224 a->responder = local;
225
226 /* Send the key agreement request */
227 silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
228 local ? local_ip : NULL, NULL, 0, 60,
229 silcpurple_buddy_keyagr_cb, a);
230
231 silc_free(local_ip);
232 silc_free(remote_ip);
233 silc_free(clients);
234 }
235
236 typedef struct {
237 SilcClient client;
238 SilcClientConnection conn;
239 SilcClientID client_id;
240 char *hostname;
241 SilcUInt16 port;
242 } *SilcPurpleKeyAgrAsk;
243
244 static void
245 silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id)
246 {
247 SilcPurpleKeyAgr ai;
248 SilcClientEntry client_entry;
249
250 if (id != 1)
251 goto out;
252
253 /* Get the client entry. */
254 client_entry = silc_client_get_client_by_id(a->client, a->conn,
255 &a->client_id);
256 if (!client_entry) {
257 purple_notify_error(a->client->application, _("Key Agreement"),
258 _("The remote user is not present in the network any more"),
259 NULL);
260 goto out;
261 }
262
263 /* If the hostname was provided by the requestor perform the key agreement
264 now. Otherwise, we will send him a request to connect to us. */
265 if (a->hostname) {
266 ai = silc_calloc(1, sizeof(*ai));
267 if (!ai)
268 goto out;
269 ai->responder = FALSE;
270 silc_client_perform_key_agreement(a->client, a->conn, client_entry,
271 a->hostname, a->port,
272 silcpurple_buddy_keyagr_cb, ai);
273 } else {
274 /* Send request. Force us as the point of connection since requestor
275 did not provide the point of connection. */
276 silcpurple_buddy_keyagr_do(a->client->application,
277 client_entry->nickname, TRUE);
278 }
279
280 out:
281 silc_free(a->hostname);
282 silc_free(a);
283 }
284
285 void silcpurple_buddy_keyagr_request(SilcClient client,
286 SilcClientConnection conn,
287 SilcClientEntry client_entry,
288 const char *hostname, SilcUInt16 port)
289 {
290 char tmp[128], tmp2[128];
291 SilcPurpleKeyAgrAsk a;
292 PurpleConnection *gc = client->application;
293
294 g_snprintf(tmp, sizeof(tmp),
295 _("Key agreement request received from %s. Would you like to "
296 "perform the key agreement?"), client_entry->nickname);
297 if (hostname)
298 g_snprintf(tmp2, sizeof(tmp2),
299 _("The remote user is waiting key agreement on:\n"
300 "Remote host: %s\nRemote port: %d"), hostname, port);
301
302 a = silc_calloc(1, sizeof(*a));
303 if (!a)
304 return;
305 a->client = client;
306 a->conn = conn;
307 a->client_id = *client_entry->id;
308 if (hostname)
309 a->hostname = strdup(hostname);
310 a->port = port;
311
312 purple_request_action(client->application, _("Key Agreement Request"), tmp,
313 hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname,
314 NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb),
315 _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb));
316 }
317
318 static void
319 silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data)
320 {
321 PurpleBuddy *buddy;
322
323 buddy = (PurpleBuddy *)node;
324 silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE);
325 }
326
327
328 /**************************** Static IM Key **********************************/
329
330 static void
331 silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data)
332 {
333 PurpleBuddy *b;
334 PurpleConnection *gc;
335 SilcPurple sg;
336 char *nickname;
337 SilcClientEntry *clients;
338 SilcUInt32 clients_count;
339
340 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
341
342 b = (PurpleBuddy *) node;
343 gc = purple_account_get_connection(b->account);
344 sg = gc->proto_data;
345
346 if (!silc_parse_userfqdn(b->name, &nickname, NULL))
347 return;
348
349 /* Find client entry */
350 clients = silc_client_get_clients_local(sg->client, sg->conn,
351 nickname, b->name,
352 &clients_count);
353 if (!clients) {
354 silc_free(nickname);
355 return;
356 }
357
358 clients[0]->prv_resp = FALSE;
359 silc_client_del_private_message_key(sg->client, sg->conn,
360 clients[0]);
361 silc_free(clients);
362 silc_free(nickname);
363 }
364
365 typedef struct {
366 SilcClient client;
367 SilcClientConnection conn;
368 SilcClientID client_id;
369 } *SilcPurplePrivkey;
370
371 static void
372 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name);
373
374 static void
375 silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase)
376 {
377 SilcClientEntry client_entry;
378
379 if (!passphrase || !(*passphrase)) {
380 silc_free(p);
381 return;
382 }
383
384 /* Get the client entry. */
385 client_entry = silc_client_get_client_by_id(p->client, p->conn,
386 &p->client_id);
387 if (!client_entry) {
388 purple_notify_error(p->client->application, _("IM With Password"),
389 _("The remote user is not present in the network any more"),
390 NULL);
391 silc_free(p);
392 return;
393 }
394
395 /* Set the private message key */
396 silc_client_del_private_message_key(p->client, p->conn,
397 client_entry);
398 silc_client_add_private_message_key(p->client, p->conn,
399 client_entry, NULL, NULL,
400 (unsigned char *)passphrase,
401 strlen(passphrase), FALSE,
402 client_entry->prv_resp);
403 if (!client_entry->prv_resp)
404 silc_client_send_private_message_key_request(p->client,
405 p->conn,
406 client_entry);
407 silc_free(p);
408 }
409
410 static void
411 silcpurple_buddy_privkey_resolved(SilcClient client,
412 SilcClientConnection conn,
413 SilcClientEntry *clients,
414 SilcUInt32 clients_count,
415 void *context)
416 {
417 char tmp[256];
418
419 if (!clients) {
420 g_snprintf(tmp, sizeof(tmp),
421 _("User %s is not present in the network"),
422 (const char *)context);
423 purple_notify_error(client->application, _("IM With Password"),
424 _("Cannot set IM key"), tmp);
425 g_free(context);
426 return;
427 }
428
429 silcpurple_buddy_privkey(client->application, context);
430 silc_free(context);
431 }
432
433 static void
434 silcpurple_buddy_privkey(PurpleConnection *gc, const char *name)
435 {
436 SilcPurple sg = gc->proto_data;
437 char *nickname;
438 SilcPurplePrivkey p;
439 SilcClientEntry *clients;
440 SilcUInt32 clients_count;
441
442 if (!name)
443 return;
444 if (!silc_parse_userfqdn(name, &nickname, NULL))
445 return;
446
447 /* Find client entry */
448 clients = silc_client_get_clients_local(sg->client, sg->conn,
449 nickname, name,
450 &clients_count);
451 if (!clients) {
452 silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
453 silcpurple_buddy_privkey_resolved,
454 g_strdup(name));
455 silc_free(nickname);
456 return;
457 }
458
459 p = silc_calloc(1, sizeof(*p));
460 if (!p)
461 return;
462 p->client = sg->client;
463 p->conn = sg->conn;
464 p->client_id = *clients[0]->id;
465 purple_request_input(gc, _("IM With Password"), NULL,
466 _("Set IM Password"), NULL, FALSE, TRUE, NULL,
467 _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb),
468 _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb),
469 gc->account, NULL, NULL, p);
470
471 silc_free(clients);
472 silc_free(nickname);
473 }
474
475 static void
476 silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data)
477 {
478 PurpleBuddy *buddy;
479 PurpleConnection *gc;
480
481 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
482
483 buddy = (PurpleBuddy *) node;
484 gc = purple_account_get_connection(buddy->account);
485
486 silcpurple_buddy_privkey(gc, buddy->name);
487 }
488
489
490 /**************************** Get Public Key *********************************/
491
492 typedef struct {
493 SilcClient client;
494 SilcClientConnection conn;
495 SilcClientID client_id;
496 } *SilcPurpleBuddyGetkey;
497
498 static void
499 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name);
500
501 static void
502 silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g,
503 SilcClientCommandReplyContext cmd)
504 {
505 SilcClientEntry client_entry;
506 unsigned char *pk;
507 SilcUInt32 pk_len;
508
509 /* Get the client entry. */
510 client_entry = silc_client_get_client_by_id(g->client, g->conn,
511 &g->client_id);
512 if (!client_entry) {
513 purple_notify_error(g->client->application, _("Get Public Key"),
514 _("The remote user is not present in the network any more"),
515 NULL);
516 silc_free(g);
517 return;
518 }
519
520 if (!client_entry->public_key) {
521 silc_free(g);
522 return;
523 }
524
525 /* Now verify the public key */
526 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
527 silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname,
528 SILC_SOCKET_TYPE_CLIENT,
529 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
530 NULL, NULL);
531 silc_free(pk);
532 silc_free(g);
533 }
534
535 static void
536 silcpurple_buddy_getkey_resolved(SilcClient client,
537 SilcClientConnection conn,
538 SilcClientEntry *clients,
539 SilcUInt32 clients_count,
540 void *context)
541 {
542 char tmp[256];
543
544 if (!clients) {
545 g_snprintf(tmp, sizeof(tmp),
546 _("User %s is not present in the network"),
547 (const char *)context);
548 purple_notify_error(client->application, _("Get Public Key"),
549 _("Cannot fetch the public key"), tmp);
550 g_free(context);
551 return;
552 }
553
554 silcpurple_buddy_getkey(client->application, context);
555 silc_free(context);
556 }
557
558 static void
559 silcpurple_buddy_getkey(PurpleConnection *gc, const char *name)
560 {
561 SilcPurple sg = gc->proto_data;
562 SilcClient client = sg->client;
563 SilcClientConnection conn = sg->conn;
564 SilcClientEntry *clients;
565 SilcUInt32 clients_count;
566 SilcPurpleBuddyGetkey g;
567 char *nickname;
568
569 if (!name)
570 return;
571
572 if (!silc_parse_userfqdn(name, &nickname, NULL))
573 return;
574
575 /* Find client entry */
576 clients = silc_client_get_clients_local(client, conn, nickname, name,
577 &clients_count);
578 if (!clients) {
579 silc_client_get_clients(client, conn, nickname, NULL,
580 silcpurple_buddy_getkey_resolved,
581 g_strdup(name));
582 silc_free(nickname);
583 return;
584 }
585
586 /* Call GETKEY */
587 g = silc_calloc(1, sizeof(*g));
588 if (!g)
589 return;
590 g->client = client;
591 g->conn = conn;
592 g->client_id = *clients[0]->id;
593 silc_client_command_call(client, conn, NULL, "GETKEY",
594 clients[0]->nickname, NULL);
595 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
596 conn->cmd_ident,
597 (SilcCommandCb)silcpurple_buddy_getkey_cb, g);
598 silc_free(clients);
599 silc_free(nickname);
600 }
601
602 static void
603 silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data)
604 {
605 PurpleBuddy *buddy;
606 PurpleConnection *gc;
607
608 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
609
610 buddy = (PurpleBuddy *) node;
611 gc = purple_account_get_connection(buddy->account);
612
613 silcpurple_buddy_getkey(gc, buddy->name);
614 }
615
616 static void
617 silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data)
618 {
619 PurpleBuddy *b;
620 PurpleConnection *gc;
621 SilcPurple sg;
622 SilcPublicKey public_key;
623 const char *pkfile;
624
625 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
626
627 b = (PurpleBuddy *) node;
628 gc = purple_account_get_connection(b->account);
629 sg = gc->proto_data;
630
631 pkfile = purple_blist_node_get_string(node, "public-key");
632 if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
633 !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
634 purple_notify_error(gc,
635 _("Show Public Key"),
636 _("Could not load public key"), NULL);
637 return;
638 }
639
640 silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL);
641 silc_pkcs_public_key_free(public_key);
642 }
643
644
645 /**************************** Buddy routines *********************************/
646
647 /* The buddies are implemented by using the WHOIS and WATCH commands that
648 can be used to search users by their public key. Since nicknames aren't
649 unique in SILC we cannot trust the buddy list using their nickname. We
650 associate public keys to buddies and use those to search and watch
651 in the network.
652
653 The problem is that Purple does not return PurpleBuddy contexts to the
654 callbacks but the buddy names. Naturally, this is not going to work
655 with SILC. But, for now, we have to do what we can... */
656
657 typedef struct {
658 SilcClient client;
659 SilcClientConnection conn;
660 SilcClientID client_id;
661 PurpleBuddy *b;
662 unsigned char *offline_pk;
663 SilcUInt32 offline_pk_len;
664 unsigned int offline : 1;
665 unsigned int pubkey_search : 1;
666 unsigned int init : 1;
667 } *SilcPurpleBuddyRes;
668
669 static void
670 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id);
671 static void
672 silcpurple_add_buddy_resolved(SilcClient client,
673 SilcClientConnection conn,
674 SilcClientEntry *clients,
675 SilcUInt32 clients_count,
676 void *context);
677
678 void silcpurple_get_info(PurpleConnection *gc, const char *who)
679 {
680 SilcPurple sg = gc->proto_data;
681 SilcClient client = sg->client;
682 SilcClientConnection conn = sg->conn;
683 SilcClientEntry client_entry;
684 PurpleBuddy *b;
685 const char *filename, *nick = who;
686 char tmp[256];
687
688 if (!who)
689 return;
690 if (strlen(who) > 1 && who[0] == '@')
691 nick = who + 1;
692 if (strlen(who) > 1 && who[0] == '*')
693 nick = who + 1;
694 if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
695 nick = who + 2;
696
697 b = purple_find_buddy(gc->account, nick);
698 if (b) {
699 /* See if we have this buddy's public key. If we do use that
700 to search the details. */
701 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
702 if (filename) {
703 /* Call WHOIS. The user info is displayed in the WHOIS
704 command reply. */
705 silc_client_command_call(client, conn, NULL, "WHOIS",
706 "-details", "-pubkey", filename, NULL);
707 return;
708 }
709
710 if (!b->proto_data) {
711 g_snprintf(tmp, sizeof(tmp),
712 _("User %s is not present in the network"), b->name);
713 purple_notify_error(gc, _("User Information"),
714 _("Cannot get user information"), tmp);
715 return;
716 }
717
718 client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
719 if (client_entry) {
720 /* Call WHOIS. The user info is displayed in the WHOIS
721 command reply. */
722 silc_client_command_call(client, conn, NULL, "WHOIS",
723 client_entry->nickname, "-details", NULL);
724 }
725 } else {
726 /* Call WHOIS just with nickname. */
727 silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL);
728 }
729 }
730
731 static void
732 silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r)
733 {
734 char tmp[512];
735 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
736 r->b->name);
737 purple_notify_error(r->client->application, _("Add Buddy"), tmp,
738 _("You cannot receive buddy notifications until you "
739 "import his/her public key. You can use the Get Public Key "
740 "command to get the public key."));
741 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
742 }
743
744 static void
745 silcpurple_add_buddy_save(bool success, void *context)
746 {
747 SilcPurpleBuddyRes r = context;
748 PurpleBuddy *b = r->b;
749 SilcClient client = r->client;
750 SilcClientEntry client_entry;
751 SilcAttributePayload attr;
752 SilcAttribute attribute;
753 SilcVCardStruct vcard;
754 SilcAttributeObjMime message, extension;
755 #ifdef SILC_ATTRIBUTE_USER_ICON
756 SilcAttributeObjMime usericon;
757 #endif
758 SilcAttributeObjPk serverpk, usersign, serversign;
759 gboolean usign_success = TRUE, ssign_success = TRUE;
760 char filename[512], filename2[512], *fingerprint = NULL, *tmp;
761 SilcUInt32 len;
762 int i;
763
764 if (!success) {
765 /* The user did not trust the public key. */
766 silcpurple_add_buddy_pk_no(r);
767 silc_free(r);
768 return;
769 }
770
771 if (r->offline) {
772 /* User is offline. Associate the imported public key with
773 this user. */
774 fingerprint = silc_hash_fingerprint(NULL, r->offline_pk,
775 r->offline_pk_len);
776 for (i = 0; i < strlen(fingerprint); i++)
777 if (fingerprint[i] == ' ')
778 fingerprint[i] = '_';
779 g_snprintf(filename, sizeof(filename) - 1,
780 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
781 silcpurple_silcdir(), fingerprint);
782 purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
783 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
784 silc_free(fingerprint);
785 silc_free(r->offline_pk);
786 silc_free(r);
787 return;
788 }
789
790 /* Get the client entry. */
791 client_entry = silc_client_get_client_by_id(r->client, r->conn,
792 &r->client_id);
793 if (!client_entry) {
794 silc_free(r);
795 return;
796 }
797
798 memset(&vcard, 0, sizeof(vcard));
799 memset(&message, 0, sizeof(message));
800 memset(&extension, 0, sizeof(extension));
801 #ifdef SILC_ATTRIBUTE_USER_ICON
802 memset(&usericon, 0, sizeof(usericon));
803 #endif
804 memset(&serverpk, 0, sizeof(serverpk));
805 memset(&usersign, 0, sizeof(usersign));
806 memset(&serversign, 0, sizeof(serversign));
807
808 /* Now that we have the public key and we trust it now we
809 save the attributes of the buddy and update its status. */
810
811 if (client_entry->attrs) {
812 silc_dlist_start(client_entry->attrs);
813 while ((attr = silc_dlist_get(client_entry->attrs))
814 != SILC_LIST_END) {
815 attribute = silc_attribute_get_attribute(attr);
816
817 switch (attribute) {
818 case SILC_ATTRIBUTE_USER_INFO:
819 if (!silc_attribute_get_object(attr, (void *)&vcard,
820 sizeof(vcard)))
821 continue;
822 break;
823
824 case SILC_ATTRIBUTE_STATUS_MESSAGE:
825 if (!silc_attribute_get_object(attr, (void *)&message,
826 sizeof(message)))
827 continue;
828 break;
829
830 case SILC_ATTRIBUTE_EXTENSION:
831 if (!silc_attribute_get_object(attr, (void *)&extension,
832 sizeof(extension)))
833 continue;
834 break;
835
836 #ifdef SILC_ATTRIBUTE_USER_ICON
837 case SILC_ATTRIBUTE_USER_ICON:
838 if (!silc_attribute_get_object(attr, (void *)&usericon,
839 sizeof(usericon)))
840 continue;
841 break;
842 #endif
843
844 case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
845 if (serverpk.type)
846 continue;
847 if (!silc_attribute_get_object(attr, (void *)&serverpk,
848 sizeof(serverpk)))
849 continue;
850 break;
851
852 case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
853 if (usersign.data)
854 continue;
855 if (!silc_attribute_get_object(attr, (void *)&usersign,
856 sizeof(usersign)))
857 continue;
858 break;
859
860 case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
861 if (serversign.data)
862 continue;
863 if (!silc_attribute_get_object(attr, (void *)&serversign,
864 sizeof(serversign)))
865 continue;
866 break;
867
868 default:
869 break;
870 }
871 }
872 }
873
874 /* Verify the attribute signatures */
875
876 if (usersign.data) {
877 SilcPKCS pkcs;
878 unsigned char *verifyd;
879 SilcUInt32 verify_len;
880
881 silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
882 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
883 FALSE, &verify_len);
884 if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
885 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
886 usersign.data,
887 usersign.data_len,
888 verifyd, verify_len))
889 usign_success = FALSE;
890 }
891 silc_free(verifyd);
892 }
893
894 if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
895 SilcPublicKey public_key;
896 SilcPKCS pkcs;
897 unsigned char *verifyd;
898 SilcUInt32 verify_len;
899
900 if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
901 &public_key)) {
902 silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
903 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
904 TRUE, &verify_len);
905 if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
906 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
907 serversign.data,
908 serversign.data_len,
909 verifyd, verify_len))
910 ssign_success = FALSE;
911 }
912 silc_pkcs_public_key_free(public_key);
913 silc_free(verifyd);
914 }
915 }
916
917 fingerprint = silc_fingerprint(client_entry->fingerprint,
918 client_entry->fingerprint_len);
919 for (i = 0; i < strlen(fingerprint); i++)
920 if (fingerprint[i] == ' ')
921 fingerprint[i] = '_';
922
923 if (usign_success || ssign_success) {
924 struct passwd *pw;
925 struct stat st;
926
927 memset(filename2, 0, sizeof(filename2));
928
929 /* Filename for dir */
930 tmp = fingerprint + strlen(fingerprint) - 9;
931 g_snprintf(filename, sizeof(filename) - 1,
932 "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s",
933 silcpurple_silcdir(), tmp);
934
935 pw = getpwuid(getuid());
936 if (!pw)
937 return;
938
939 /* Create dir if it doesn't exist */
940 if ((g_stat(filename, &st)) == -1) {
941 if (errno == ENOENT) {
942 if (pw->pw_uid == geteuid())
943 g_mkdir(filename, 0755);
944 }
945 }
946
947 /* Save VCard */
948 g_snprintf(filename2, sizeof(filename2) - 1,
949 "%s" G_DIR_SEPARATOR_S "vcard", filename);
950 if (vcard.full_name) {
951 tmp = (char *)silc_vcard_encode(&vcard, &len);
952 silc_file_writefile(filename2, tmp, len);
953 silc_free(tmp);
954 }
955
956 /* Save status message */
957 if (message.mime) {
958 memset(filename2, 0, sizeof(filename2));
959 g_snprintf(filename2, sizeof(filename2) - 1,
960 "%s" G_DIR_SEPARATOR_S "status_message.mime",
961 filename);
962 silc_file_writefile(filename2, (char *)message.mime,
963 message.mime_len);
964 }
965
966 /* Save extension data */
967 if (extension.mime) {
968 memset(filename2, 0, sizeof(filename2));
969 g_snprintf(filename2, sizeof(filename2) - 1,
970 "%s" G_DIR_SEPARATOR_S "extension.mime",
971 filename);
972 silc_file_writefile(filename2, (char *)extension.mime,
973 extension.mime_len);
974 }
975
976 #ifdef SILC_ATTRIBUTE_USER_ICON
977 /* Save user icon */
978 if (usericon.mime) {
979 SilcMime m = silc_mime_decode(usericon.mime,
980 usericon.mime_len);
981 if (m) {
982 const char *type = silc_mime_get_field(m, "Content-Type");
983 if (!strcmp(type, "image/jpeg") ||
984 !strcmp(type, "image/gif") ||
985 !strcmp(type, "image/bmp") ||
986 !strcmp(type, "image/png")) {
987 const unsigned char *data;
988 SilcUInt32 data_len;
989 data = silc_mime_get_data(m, &data_len);
990 if (data) {
991 /* TODO: Check if SILC gives us something to use as the checksum instead */
992 purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup(data, data_len), data_len, NULL);
993 }
994 }
995 silc_mime_free(m);
996 }
997 }
998 #endif
999 }
1000
1001 /* Save the public key path to buddy properties, as it is used
1002 to identify the buddy in the network (and not the nickname). */
1003 memset(filename, 0, sizeof(filename));
1004 g_snprintf(filename, sizeof(filename) - 1,
1005 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
1006 silcpurple_silcdir(), fingerprint);
1007 purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
1008
1009 /* Update online status */
1010 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
1011
1012 /* Finally, start watching this user so we receive its status
1013 changes from the server */
1014 g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
1015 silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
1016 filename2, NULL);
1017
1018 silc_free(fingerprint);
1019 silc_free(r);
1020 }
1021
1022 static void
1023 silcpurple_add_buddy_ask_import(void *user_data, const char *name)
1024 {
1025 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
1026 SilcPublicKey public_key;
1027
1028 /* Load the public key */
1029 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
1030 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
1031 silcpurple_add_buddy_ask_pk_cb(r, 0);
1032 purple_notify_error(r->client->application,
1033 _("Add Buddy"), _("Could not load public key"), NULL);
1034 return;
1035 }
1036
1037 /* Now verify the public key */
1038 r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
1039 silcpurple_verify_public_key(r->client, r->conn, r->b->name,
1040 SILC_SOCKET_TYPE_CLIENT,
1041 r->offline_pk, r->offline_pk_len,
1042 SILC_SKE_PK_TYPE_SILC,
1043 silcpurple_add_buddy_save, r);
1044 }
1045
1046 static void
1047 silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name)
1048 {
1049 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
1050
1051 /* The user did not import public key. The buddy is unusable. */
1052 silcpurple_add_buddy_pk_no(r);
1053 silc_free(r);
1054 }
1055
1056 static void
1057 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
1058 {
1059 if (id != 0) {
1060 /* The user did not import public key. The buddy is unusable. */
1061 silcpurple_add_buddy_pk_no(r);
1062 silc_free(r);
1063 return;
1064 }
1065
1066 /* Open file selector to select the public key. */
1067 purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
1068 G_CALLBACK(silcpurple_add_buddy_ask_import),
1069 G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
1070 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
1071
1072 }
1073
1074 static void
1075 silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
1076 {
1077 char tmp[512];
1078 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
1079 r->b->name);
1080 purple_request_action(r->client->application, _("Add Buddy"), tmp,
1081 _("To add the buddy you must import his/her public key. "
1082 "Press Import to import a public key."), 0,
1083 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
1084 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
1085 _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
1086 }
1087
1088 static void
1089 silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
1090 SilcClientCommandReplyContext cmd)
1091 {
1092 SilcClientEntry client_entry;
1093 unsigned char *pk;
1094 SilcUInt32 pk_len;
1095
1096 /* Get the client entry. */
1097 client_entry = silc_client_get_client_by_id(r->client, r->conn,
1098 &r->client_id);
1099 if (!client_entry || !client_entry->public_key) {
1100 /* The buddy is offline/nonexistent. We will require user
1101 to associate a public key with the buddy or the buddy
1102 cannot be added. */
1103 r->offline = TRUE;
1104 silcpurple_add_buddy_ask_pk(r);
1105 return;
1106 }
1107
1108 /* Now verify the public key */
1109 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1110 silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
1111 SILC_SOCKET_TYPE_CLIENT,
1112 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1113 silcpurple_add_buddy_save, r);
1114 silc_free(pk);
1115 }
1116
1117 static void
1118 silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
1119 {
1120 PurpleRequestField *f;
1121 const GList *list;
1122 SilcClientEntry client_entry;
1123
1124 f = purple_request_fields_get_field(fields, "list");
1125 list = purple_request_field_list_get_selected(f);
1126 if (!list) {
1127 /* The user did not select any user. */
1128 silcpurple_add_buddy_pk_no(r);
1129 silc_free(r);
1130 return;
1131 }
1132
1133 client_entry = purple_request_field_list_get_data(f, list->data);
1134 silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
1135 }
1136
1137 static void
1138 silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
1139 {
1140 /* The user did not select any user. */
1141 silcpurple_add_buddy_pk_no(r);
1142 silc_free(r);
1143 }
1144
1145 static void
1146 silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
1147 SilcClientEntry *clients,
1148 SilcUInt32 clients_count)
1149 {
1150 PurpleRequestFields *fields;
1151 PurpleRequestFieldGroup *g;
1152 PurpleRequestField *f;
1153 char tmp[512], tmp2[128];
1154 int i;
1155 char *fingerprint;
1156
1157 fields = purple_request_fields_new();
1158 g = purple_request_field_group_new(NULL);
1159 f = purple_request_field_list_new("list", NULL);
1160 purple_request_field_group_add_field(g, f);
1161 purple_request_field_list_set_multi_select(f, FALSE);
1162 purple_request_fields_add_group(fields, g);
1163
1164 for (i = 0; i < clients_count; i++) {
1165 fingerprint = NULL;
1166 if (clients[i]->fingerprint) {
1167 fingerprint = silc_fingerprint(clients[i]->fingerprint,
1168 clients[i]->fingerprint_len);
1169 g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
1170 }
1171 g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
1172 clients[i]->realname, clients[i]->nickname,
1173 clients[i]->username, clients[i]->hostname ?
1174 clients[i]->hostname : "",
1175 fingerprint ? tmp2 : "");
1176 purple_request_field_list_add(f, tmp, clients[i]);
1177 silc_free(fingerprint);
1178 }
1179
1180 purple_request_fields(r->client->application, _("Add Buddy"),
1181 _("Select correct user"),
1182 r->pubkey_search
1183 ? _("More than one user was found with the same public key. Select "
1184 "the correct user from the list to add to the buddy list.")
1185 : _("More than one user was found with the same name. Select "
1186 "the correct user from the list to add to the buddy list."),
1187 fields,
1188 _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
1189 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
1190 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
1191 }
1192
1193 static void
1194 silcpurple_add_buddy_resolved(SilcClient client,
1195 SilcClientConnection conn,
1196 SilcClientEntry *clients,
1197 SilcUInt32 clients_count,
1198 void *context)
1199 {
1200 SilcPurpleBuddyRes r = context;
1201 PurpleBuddy *b = r->b;
1202 SilcAttributePayload pub;
1203 SilcAttributeObjPk userpk;
1204 unsigned char *pk;
1205 SilcUInt32 pk_len;
1206 const char *filename;
1207
1208 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
1209
1210 /* If the buddy is offline/nonexistent, we will require user
1211 to associate a public key with the buddy or the buddy
1212 cannot be added. */
1213 if (!clients_count) {
1214 if (r->init) {
1215 silc_free(r);
1216 return;
1217 }
1218
1219 r->offline = TRUE;
1220 /* If the user has already associated a public key, try loading it
1221 * before prompting the user to load it again */
1222 if (filename != NULL)
1223 silcpurple_add_buddy_ask_import(r, filename);
1224 else
1225 silcpurple_add_buddy_ask_pk(r);
1226 return;
1227 }
1228
1229 /* If more than one client was found with nickname, we need to verify
1230 from user which one is the correct. */
1231 if (clients_count > 1 && !r->pubkey_search) {
1232 if (r->init) {
1233 silc_free(r);
1234 return;
1235 }
1236
1237 silcpurple_add_buddy_select(r, clients, clients_count);
1238 return;
1239 }
1240
1241 /* If we searched using public keys and more than one entry was found
1242 the same person is logged on multiple times. */
1243 if (clients_count > 1 && r->pubkey_search && b->name) {
1244 if (r->init) {
1245 /* Find the entry that closest matches to the
1246 buddy nickname. */
1247 int i;
1248 for (i = 0; i < clients_count; i++) {
1249 if (!strncasecmp(b->name, clients[i]->nickname,
1250 strlen(b->name))) {
1251 clients[0] = clients[i];
1252 break;
1253 }
1254 }
1255 } else {
1256 /* Verify from user which one is correct */
1257 silcpurple_add_buddy_select(r, clients, clients_count);
1258 return;
1259 }
1260 }
1261
1262 /* The client was found. Now get its public key and verify
1263 that before adding the buddy. */
1264 memset(&userpk, 0, sizeof(userpk));
1265 b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
1266 r->client_id = *clients[0]->id;
1267
1268 /* Get the public key from attributes, if not present then
1269 resolve it with GETKEY unless we have it cached already. */
1270 if (clients[0]->attrs && !clients[0]->public_key) {
1271 pub = silcpurple_get_attr(clients[0]->attrs,
1272 SILC_ATTRIBUTE_USER_PUBLIC_KEY);
1273 if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
1274 sizeof(userpk))) {
1275 /* Get public key with GETKEY */
1276 silc_client_command_call(client, conn, NULL,
1277 "GETKEY", clients[0]->nickname, NULL);
1278 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1279 conn->cmd_ident,
1280 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1281 r);
1282 return;
1283 }
1284 if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
1285 &clients[0]->public_key))
1286 return;
1287 silc_free(userpk.data);
1288 } else if (filename && !clients[0]->public_key) {
1289 if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1290 SILC_PKCS_FILE_PEM) &&
1291 !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1292 SILC_PKCS_FILE_BIN)) {
1293 /* Get public key with GETKEY */
1294 silc_client_command_call(client, conn, NULL,
1295 "GETKEY", clients[0]->nickname, NULL);
1296 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1297 conn->cmd_ident,
1298 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1299 r);
1300 return;
1301 }
1302 } else if (!clients[0]->public_key) {
1303 /* Get public key with GETKEY */
1304 silc_client_command_call(client, conn, NULL,
1305 "GETKEY", clients[0]->nickname, NULL);
1306 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1307 conn->cmd_ident,
1308 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1309 r);
1310 return;
1311 }
1312
1313 /* We have the public key, verify it. */
1314 pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
1315 silcpurple_verify_public_key(client, conn, clients[0]->nickname,
1316 SILC_SOCKET_TYPE_CLIENT,
1317 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1318 silcpurple_add_buddy_save, r);
1319 silc_free(pk);
1320 }
1321
1322 static void
1323 silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
1324 {
1325 SilcPurple sg = gc->proto_data;
1326 SilcClient client = sg->client;
1327 SilcClientConnection conn = sg->conn;
1328 SilcPurpleBuddyRes r;
1329 SilcBuffer attrs;
1330 const char *filename, *name = b->name;
1331
1332 r = silc_calloc(1, sizeof(*r));
1333 if (!r)
1334 return;
1335 r->client = client;
1336 r->conn = conn;
1337 r->b = b;
1338 r->init = init;
1339
1340 /* See if we have this buddy's public key. If we do use that
1341 to search the details. */
1342 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
1343 if (filename) {
1344 SilcPublicKey public_key;
1345 SilcAttributeObjPk userpk;
1346
1347 if (!silc_pkcs_load_public_key(filename, &public_key,
1348 SILC_PKCS_FILE_PEM) &&
1349 !silc_pkcs_load_public_key(filename, &public_key,
1350 SILC_PKCS_FILE_BIN))
1351 return;
1352
1353 /* Get all attributes, and use the public key to search user */
1354 name = NULL;
1355 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
1356 SILC_ATTRIBUTE_SERVICE,
1357 SILC_ATTRIBUTE_STATUS_MOOD,
1358 SILC_ATTRIBUTE_STATUS_FREETEXT,
1359 SILC_ATTRIBUTE_STATUS_MESSAGE,
1360 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
1361 SILC_ATTRIBUTE_PREFERRED_CONTACT,
1362 SILC_ATTRIBUTE_TIMEZONE,
1363 SILC_ATTRIBUTE_GEOLOCATION,
1364 #ifdef SILC_ATTRIBUTE_USER_ICON
1365 SILC_ATTRIBUTE_USER_ICON,
1366 #endif
1367 SILC_ATTRIBUTE_DEVICE_INFO, 0);
1368 userpk.type = "silc-rsa";
1369 userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
1370 attrs = silc_attribute_payload_encode(attrs,
1371 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
1372 SILC_ATTRIBUTE_FLAG_VALID,
1373 &userpk, sizeof(userpk));
1374 silc_free(userpk.data);
1375 silc_pkcs_public_key_free(public_key);
1376 r->pubkey_search = TRUE;
1377 } else {
1378 /* Get all attributes */
1379 attrs = silc_client_attributes_request(0);
1380 }
1381
1382 /* Resolve */
1383 silc_client_get_clients_whois(client, conn, name, NULL, attrs,
1384 silcpurple_add_buddy_resolved, r);
1385 silc_buffer_free(attrs);
1386 }
1387
1388 void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
1389 {
1390 silcpurple_add_buddy_i(gc, buddy, FALSE);
1391 }
1392
1393 void silcpurple_send_buddylist(PurpleConnection *gc)
1394 {
1395 PurpleBuddyList *blist;
1396 PurpleBlistNode *gnode, *cnode, *bnode;
1397 PurpleBuddy *buddy;
1398 PurpleAccount *account;
1399
1400 account = purple_connection_get_account(gc);
1401
1402 if ((blist = purple_get_blist()) != NULL)
1403 {
1404 for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
1405 {
1406 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
1407 continue;
1408 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
1409 {
1410 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
1411 continue;
1412 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
1413 {
1414 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
1415 continue;
1416 buddy = (PurpleBuddy *)bnode;
1417 if (purple_buddy_get_account(buddy) == account)
1418 silcpurple_add_buddy_i(gc, buddy, TRUE);
1419 }
1420 }
1421 }
1422 }
1423 }
1424
1425 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
1426 PurpleGroup *group)
1427 {
1428 silc_free(buddy->proto_data);
1429 }
1430
1431 void silcpurple_idle_set(PurpleConnection *gc, int idle)
1432
1433 {
1434 SilcPurple sg = gc->proto_data;
1435 SilcClient client = sg->client;
1436 SilcClientConnection conn = sg->conn;
1437 SilcAttributeObjService service;
1438 const char *server;
1439 int port;
1440
1441 server = purple_account_get_string(sg->account, "server",
1442 "silc.silcnet.org");
1443 port = purple_account_get_int(sg->account, "port", 706),
1444
1445 memset(&service, 0, sizeof(service));
1446 silc_client_attribute_del(client, conn,
1447 SILC_ATTRIBUTE_SERVICE, NULL);
1448 service.port = port;
1449 g_snprintf(service.address, sizeof(service.address), "%s", server);
1450 service.idle = idle;
1451 silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
1452 &service, sizeof(service));
1453 }
1454
1455 char *silcpurple_status_text(PurpleBuddy *b)
1456 {
1457 SilcPurple sg = b->account->gc->proto_data;
1458 SilcClient client = sg->client;
1459 SilcClientConnection conn = sg->conn;
1460 SilcClientID *client_id = b->proto_data;
1461 SilcClientEntry client_entry;
1462 SilcAttributePayload attr;
1463 SilcAttributeMood mood = 0;
1464
1465 /* Get the client entry. */
1466 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1467 if (!client_entry)
1468 return NULL;
1469
1470 /* If user is online, we show the mood status, if available.
1471 If user is offline or away that status is indicated. */
1472
1473 if (client_entry->mode & SILC_UMODE_DETACHED)
1474 return g_strdup(_("Detached"));
1475 if (client_entry->mode & SILC_UMODE_GONE)
1476 return g_strdup(_("Away"));
1477 if (client_entry->mode & SILC_UMODE_INDISPOSED)
1478 return g_strdup(_("Indisposed"));
1479 if (client_entry->mode & SILC_UMODE_BUSY)
1480 return g_strdup(_("Busy"));
1481 if (client_entry->mode & SILC_UMODE_PAGE)
1482 return g_strdup(_("Wake Me Up"));
1483 if (client_entry->mode & SILC_UMODE_HYPER)
1484 return g_strdup(_("Hyper Active"));
1485 if (client_entry->mode & SILC_UMODE_ROBOT)
1486 return g_strdup(_("Robot"));
1487
1488 attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
1489 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
1490 /* The mood is a bit mask, so we could show multiple moods,
1491 but let's show only one for now. */
1492 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
1493 return g_strdup(_("Happy"));
1494 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
1495 return g_strdup(_("Sad"));
1496 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
1497 return g_strdup(_("Angry"));
1498 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
1499 return g_strdup(_("Jealous"));
1500 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
1501 return g_strdup(_("Ashamed"));
1502 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
1503 return g_strdup(_("Invincible"));
1504 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
1505 return g_strdup(_("In Love"));
1506 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
1507 return g_strdup(_("Sleepy"));
1508 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
1509 return g_strdup(_("Bored"));
1510 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
1511 return g_strdup(_("Excited"));
1512 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
1513 return g_strdup(_("Anxious"));
1514 }
1515
1516 return NULL;
1517 }
1518
1519 void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
1520 {
1521 SilcPurple sg = b->account->gc->proto_data;
1522 SilcClient client = sg->client;
1523 SilcClientConnection conn = sg->conn;
1524 SilcClientID *client_id = b->proto_data;
1525 SilcClientEntry client_entry;
1526 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1527 char tmp[256];
1528
1529 /* Get the client entry. */
1530 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1531 if (!client_entry)
1532 return;
1533
1534 if (client_entry->nickname)
1535 purple_notify_user_info_add_pair(user_info, _("Nickname"),
1536 client_entry->nickname);
1537 if (client_entry->username && client_entry->hostname) {
1538 g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
1539 purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
1540 }
1541 if (client_entry->mode) {
1542 memset(tmp, 0, sizeof(tmp));
1543 silcpurple_get_umode_string(client_entry->mode,
1544 tmp, sizeof(tmp) - strlen(tmp));
1545 purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1546 }
1547
1548 silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1549
1550 if (statusstr) {
1551 purple_notify_user_info_add_pair(user_info, _("Message"), statusstr);
1552 g_free(statusstr);
1553 }
1554
1555 if (full) {
1556 if (moodstr) {
1557 purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1558 g_free(moodstr);
1559 }
1560
1561 if (contactstr) {
1562 purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1563 g_free(contactstr);
1564 }
1565
1566 if (langstr) {
1567 purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1568 g_free(langstr);
1569 }
1570
1571 if (devicestr) {
1572 purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1573 g_free(devicestr);
1574 }
1575
1576 if (tzstr) {
1577 purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1578 g_free(tzstr);
1579 }
1580
1581 if (geostr) {
1582 purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1583 g_free(geostr);
1584 }
1585 }
1586 }
1587
1588 static void
1589 silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data)
1590 {
1591 PurpleBuddy *b;
1592 PurpleConnection *gc;
1593 SilcPurple sg;
1594
1595 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
1596
1597 b = (PurpleBuddy *) node;
1598 gc = purple_account_get_connection(b->account);
1599 sg = gc->proto_data;
1600
1601 /* Call KILL */
1602 silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
1603 b->name, "Killed by operator", NULL);
1604 }
1605
1606 typedef struct {
1607 SilcPurple sg;
1608 SilcClientEntry client_entry;
1609 } *SilcPurpleBuddyWb;
1610
1611 static void
1612 silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data)
1613 {
1614 SilcPurpleBuddyWb wb = data;
1615 silcpurple_wb_init(wb->sg, wb->client_entry);
1616 silc_free(wb);
1617 }
1618
1619 GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
1620 {
1621 PurpleConnection *gc = purple_account_get_connection(buddy->account);
1622 SilcPurple sg = gc->proto_data;
1623 SilcClientConnection conn = sg->conn;
1624 const char *pkfile = NULL;
1625 SilcClientEntry client_entry = NULL;
1626 PurpleMenuAction *act;
1627 GList *m = NULL;
1628 SilcPurpleBuddyWb wb;
1629
1630 pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key");
1631 client_entry = silc_client_get_client_by_id(sg->client,
1632 sg->conn,
1633 buddy->proto_data);
1634
1635 if (client_entry && client_entry->send_key) {
1636 act = purple_menu_action_new(_("Reset IM Key"),
1637 PURPLE_CALLBACK(silcpurple_buddy_resetkey),
1638 NULL, NULL);
1639 m = g_list_append(m, act);
1640
1641 } else {
1642 act = purple_menu_action_new(_("IM with Key Exchange"),
1643 PURPLE_CALLBACK(silcpurple_buddy_keyagr),
1644 NULL, NULL);
1645 m = g_list_append(m, act);
1646
1647 act = purple_menu_action_new(_("IM with Password"),
1648 PURPLE_CALLBACK(silcpurple_buddy_privkey_menu),
1649 NULL, NULL);
1650 m = g_list_append(m, act);
1651 }
1652
1653 if (pkfile) {
1654 act = purple_menu_action_new(_("Show Public Key"),
1655 PURPLE_CALLBACK(silcpurple_buddy_showkey),
1656 NULL, NULL);
1657 m = g_list_append(m, act);
1658
1659 } else {
1660 act = purple_menu_action_new(_("Get Public Key..."),
1661 PURPLE_CALLBACK(silcpurple_buddy_getkey_menu),
1662 NULL, NULL);
1663 m = g_list_append(m, act);
1664 }
1665
1666 if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
1667 act = purple_menu_action_new(_("Kill User"),
1668 PURPLE_CALLBACK(silcpurple_buddy_kill),
1669 NULL, NULL);
1670 m = g_list_append(m, act);
1671 }
1672
1673 if (client_entry) {
1674 wb = silc_calloc(1, sizeof(*wb));
1675 wb->sg = sg;
1676 wb->client_entry = client_entry;
1677 act = purple_menu_action_new(_("Draw On Whiteboard"),
1678 PURPLE_CALLBACK(silcpurple_buddy_wb),
1679 (void *)wb, NULL);
1680 m = g_list_append(m, act);
1681 }
1682 return m;
1683 }
1684
1685 #ifdef SILC_ATTRIBUTE_USER_ICON
1686 void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
1687 {
1688 SilcPurple sg = gc->proto_data;
1689 SilcClient client = sg->client;
1690 SilcClientConnection conn = sg->conn;
1691 SilcMime mime;
1692 char type[32];
1693 unsigned char *icon;
1694 const char *t;
1695 SilcAttributeObjMime obj;
1696
1697 /* Remove */
1698 if (!img) {
1699 silc_client_attribute_del(client, conn,
1700 SILC_ATTRIBUTE_USER_ICON, NULL);
1701 return;
1702 }
1703
1704 /* Add */
1705 mime = silc_mime_alloc();
1706 if (!mime)
1707 return;
1708
1709 t = purple_imgstore_get_extension(img);
1710 if (!t || !strcmp(t, "icon")) {
1711 silc_mime_free(mime);
1712 return;
1713 }
1714 if (!strcmp(t, "jpg"))
1715 t = "jpeg";
1716 g_snprintf(type, sizeof(type), "image/%s", t);
1717 silc_mime_add_field(mime, "Content-Type", type);
1718 silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
1719
1720 obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
1721 if (obj.mime)
1722 silc_client_attribute_add(client, conn,
1723 SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
1724
1725 silc_free(icon);
1726 silc_mime_free(mime);
1727 }
1728 #endif