libpurple/protocols/silc10/buddy.c

changeset 32738
939244f1c90c
parent 32068
26af8696fb84
parent 32737
44c6902e2348
child 32739
53cff38459b1
equal deleted inserted replaced
32068:26af8696fb84 32738:939244f1c90c
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 int ret = g_mkdir(filename, 0755);
944 if (ret < 0)
945 return;
946 }
947 }
948 }
949
950 /* Save VCard */
951 g_snprintf(filename2, sizeof(filename2) - 1,
952 "%s" G_DIR_SEPARATOR_S "vcard", filename);
953 if (vcard.full_name) {
954 tmp = (char *)silc_vcard_encode(&vcard, &len);
955 silc_file_writefile(filename2, tmp, len);
956 silc_free(tmp);
957 }
958
959 /* Save status message */
960 if (message.mime) {
961 memset(filename2, 0, sizeof(filename2));
962 g_snprintf(filename2, sizeof(filename2) - 1,
963 "%s" G_DIR_SEPARATOR_S "status_message.mime",
964 filename);
965 silc_file_writefile(filename2, (char *)message.mime,
966 message.mime_len);
967 }
968
969 /* Save extension data */
970 if (extension.mime) {
971 memset(filename2, 0, sizeof(filename2));
972 g_snprintf(filename2, sizeof(filename2) - 1,
973 "%s" G_DIR_SEPARATOR_S "extension.mime",
974 filename);
975 silc_file_writefile(filename2, (char *)extension.mime,
976 extension.mime_len);
977 }
978
979 #ifdef SILC_ATTRIBUTE_USER_ICON
980 /* Save user icon */
981 if (usericon.mime) {
982 SilcMime m = silc_mime_decode(usericon.mime,
983 usericon.mime_len);
984 if (m) {
985 const char *type = silc_mime_get_field(m, "Content-Type");
986 if (!strcmp(type, "image/jpeg") ||
987 !strcmp(type, "image/gif") ||
988 !strcmp(type, "image/bmp") ||
989 !strcmp(type, "image/png")) {
990 const unsigned char *data;
991 SilcUInt32 data_len;
992 data = silc_mime_get_data(m, &data_len);
993 if (data) {
994 /* TODO: Check if SILC gives us something to use as the checksum instead */
995 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);
996 }
997 }
998 silc_mime_free(m);
999 }
1000 }
1001 #endif
1002 }
1003
1004 /* Save the public key path to buddy properties, as it is used
1005 to identify the buddy in the network (and not the nickname). */
1006 memset(filename, 0, sizeof(filename));
1007 g_snprintf(filename, sizeof(filename) - 1,
1008 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
1009 silcpurple_silcdir(), fingerprint);
1010 purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename);
1011
1012 /* Update online status */
1013 purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
1014
1015 /* Finally, start watching this user so we receive its status
1016 changes from the server */
1017 g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
1018 silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
1019 filename2, NULL);
1020
1021 silc_free(fingerprint);
1022 silc_free(r);
1023 }
1024
1025 static void
1026 silcpurple_add_buddy_ask_import(void *user_data, const char *name)
1027 {
1028 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
1029 SilcPublicKey public_key;
1030
1031 /* Load the public key */
1032 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
1033 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
1034 silcpurple_add_buddy_ask_pk_cb(r, 0);
1035 purple_notify_error(r->client->application,
1036 _("Add Buddy"), _("Could not load public key"), NULL);
1037 return;
1038 }
1039
1040 /* Now verify the public key */
1041 r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
1042 silcpurple_verify_public_key(r->client, r->conn, r->b->name,
1043 SILC_SOCKET_TYPE_CLIENT,
1044 r->offline_pk, r->offline_pk_len,
1045 SILC_SKE_PK_TYPE_SILC,
1046 silcpurple_add_buddy_save, r);
1047 }
1048
1049 static void
1050 silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name)
1051 {
1052 SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data;
1053
1054 /* The user did not import public key. The buddy is unusable. */
1055 silcpurple_add_buddy_pk_no(r);
1056 silc_free(r);
1057 }
1058
1059 static void
1060 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id)
1061 {
1062 if (id != 0) {
1063 /* The user did not import public key. The buddy is unusable. */
1064 silcpurple_add_buddy_pk_no(r);
1065 silc_free(r);
1066 return;
1067 }
1068
1069 /* Open file selector to select the public key. */
1070 purple_request_file(r->client->application, _("Open..."), NULL, FALSE,
1071 G_CALLBACK(silcpurple_add_buddy_ask_import),
1072 G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel),
1073 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
1074
1075 }
1076
1077 static void
1078 silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r)
1079 {
1080 char tmp[512];
1081 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
1082 r->b->name);
1083 purple_request_action(r->client->application, _("Add Buddy"), tmp,
1084 _("To add the buddy you must import his/her public key. "
1085 "Press Import to import a public key."), 0,
1086 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2,
1087 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb),
1088 _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb));
1089 }
1090
1091 static void
1092 silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r,
1093 SilcClientCommandReplyContext cmd)
1094 {
1095 SilcClientEntry client_entry;
1096 unsigned char *pk;
1097 SilcUInt32 pk_len;
1098
1099 /* Get the client entry. */
1100 client_entry = silc_client_get_client_by_id(r->client, r->conn,
1101 &r->client_id);
1102 if (!client_entry || !client_entry->public_key) {
1103 /* The buddy is offline/nonexistent. We will require user
1104 to associate a public key with the buddy or the buddy
1105 cannot be added. */
1106 r->offline = TRUE;
1107 silcpurple_add_buddy_ask_pk(r);
1108 return;
1109 }
1110
1111 /* Now verify the public key */
1112 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1113 silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname,
1114 SILC_SOCKET_TYPE_CLIENT,
1115 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1116 silcpurple_add_buddy_save, r);
1117 silc_free(pk);
1118 }
1119
1120 static void
1121 silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
1122 {
1123 PurpleRequestField *f;
1124 GList *list;
1125 SilcClientEntry client_entry;
1126
1127 f = purple_request_fields_get_field(fields, "list");
1128 list = purple_request_field_list_get_selected(f);
1129 if (!list) {
1130 /* The user did not select any user. */
1131 silcpurple_add_buddy_pk_no(r);
1132 silc_free(r);
1133 return;
1134 }
1135
1136 client_entry = purple_request_field_list_get_data(f, list->data);
1137 silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
1138 }
1139
1140 static void
1141 silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields)
1142 {
1143 /* The user did not select any user. */
1144 silcpurple_add_buddy_pk_no(r);
1145 silc_free(r);
1146 }
1147
1148 static void
1149 silcpurple_add_buddy_select(SilcPurpleBuddyRes r,
1150 SilcClientEntry *clients,
1151 SilcUInt32 clients_count)
1152 {
1153 PurpleRequestFields *fields;
1154 PurpleRequestFieldGroup *g;
1155 PurpleRequestField *f;
1156 char tmp[512], tmp2[128];
1157 int i;
1158 char *fingerprint;
1159
1160 fields = purple_request_fields_new();
1161 g = purple_request_field_group_new(NULL);
1162 f = purple_request_field_list_new("list", NULL);
1163 purple_request_field_group_add_field(g, f);
1164 purple_request_field_list_set_multi_select(f, FALSE);
1165 purple_request_fields_add_group(fields, g);
1166
1167 for (i = 0; i < clients_count; i++) {
1168 fingerprint = NULL;
1169 if (clients[i]->fingerprint) {
1170 fingerprint = silc_fingerprint(clients[i]->fingerprint,
1171 clients[i]->fingerprint_len);
1172 g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
1173 }
1174 g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
1175 clients[i]->realname, clients[i]->nickname,
1176 clients[i]->username, clients[i]->hostname ?
1177 clients[i]->hostname : "",
1178 fingerprint ? tmp2 : "");
1179 purple_request_field_list_add_icon(f, tmp, NULL, clients[i]);
1180 silc_free(fingerprint);
1181 }
1182
1183 purple_request_fields(r->client->application, _("Add Buddy"),
1184 _("Select correct user"),
1185 r->pubkey_search
1186 ? _("More than one user was found with the same public key. Select "
1187 "the correct user from the list to add to the buddy list.")
1188 : _("More than one user was found with the same name. Select "
1189 "the correct user from the list to add to the buddy list."),
1190 fields,
1191 _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb),
1192 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel),
1193 purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r);
1194 }
1195
1196 static void
1197 silcpurple_add_buddy_resolved(SilcClient client,
1198 SilcClientConnection conn,
1199 SilcClientEntry *clients,
1200 SilcUInt32 clients_count,
1201 void *context)
1202 {
1203 SilcPurpleBuddyRes r = context;
1204 PurpleBuddy *b = r->b;
1205 SilcAttributePayload pub;
1206 SilcAttributeObjPk userpk;
1207 unsigned char *pk;
1208 SilcUInt32 pk_len;
1209 const char *filename;
1210
1211 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
1212
1213 /* If the buddy is offline/nonexistent, we will require user
1214 to associate a public key with the buddy or the buddy
1215 cannot be added. */
1216 if (!clients_count) {
1217 if (r->init) {
1218 silc_free(r);
1219 return;
1220 }
1221
1222 r->offline = TRUE;
1223 /* If the user has already associated a public key, try loading it
1224 * before prompting the user to load it again */
1225 if (filename != NULL)
1226 silcpurple_add_buddy_ask_import(r, filename);
1227 else
1228 silcpurple_add_buddy_ask_pk(r);
1229 return;
1230 }
1231
1232 /* If more than one client was found with nickname, we need to verify
1233 from user which one is the correct. */
1234 if (clients_count > 1 && !r->pubkey_search) {
1235 if (r->init) {
1236 silc_free(r);
1237 return;
1238 }
1239
1240 silcpurple_add_buddy_select(r, clients, clients_count);
1241 return;
1242 }
1243
1244 /* If we searched using public keys and more than one entry was found
1245 the same person is logged on multiple times. */
1246 if (clients_count > 1 && r->pubkey_search && b->name) {
1247 if (r->init) {
1248 /* Find the entry that closest matches to the
1249 buddy nickname. */
1250 int i;
1251 for (i = 0; i < clients_count; i++) {
1252 if (!g_ascii_strncasecmp(b->name, clients[i]->nickname,
1253 strlen(b->name))) {
1254 clients[0] = clients[i];
1255 break;
1256 }
1257 }
1258 } else {
1259 /* Verify from user which one is correct */
1260 silcpurple_add_buddy_select(r, clients, clients_count);
1261 return;
1262 }
1263 }
1264
1265 /* The client was found. Now get its public key and verify
1266 that before adding the buddy. */
1267 memset(&userpk, 0, sizeof(userpk));
1268 b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
1269 r->client_id = *clients[0]->id;
1270
1271 /* Get the public key from attributes, if not present then
1272 resolve it with GETKEY unless we have it cached already. */
1273 if (clients[0]->attrs && !clients[0]->public_key) {
1274 pub = silcpurple_get_attr(clients[0]->attrs,
1275 SILC_ATTRIBUTE_USER_PUBLIC_KEY);
1276 if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
1277 sizeof(userpk))) {
1278 /* Get public key with GETKEY */
1279 silc_client_command_call(client, conn, NULL,
1280 "GETKEY", clients[0]->nickname, NULL);
1281 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1282 conn->cmd_ident,
1283 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1284 r);
1285 return;
1286 }
1287 if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
1288 &clients[0]->public_key))
1289 return;
1290 silc_free(userpk.data);
1291 } else if (filename && !clients[0]->public_key) {
1292 if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1293 SILC_PKCS_FILE_PEM) &&
1294 !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1295 SILC_PKCS_FILE_BIN)) {
1296 /* Get public key with GETKEY */
1297 silc_client_command_call(client, conn, NULL,
1298 "GETKEY", clients[0]->nickname, NULL);
1299 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1300 conn->cmd_ident,
1301 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1302 r);
1303 return;
1304 }
1305 } else if (!clients[0]->public_key) {
1306 /* Get public key with GETKEY */
1307 silc_client_command_call(client, conn, NULL,
1308 "GETKEY", clients[0]->nickname, NULL);
1309 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1310 conn->cmd_ident,
1311 (SilcCommandCb)silcpurple_add_buddy_getkey_cb,
1312 r);
1313 return;
1314 }
1315
1316 /* We have the public key, verify it. */
1317 pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
1318 silcpurple_verify_public_key(client, conn, clients[0]->nickname,
1319 SILC_SOCKET_TYPE_CLIENT,
1320 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1321 silcpurple_add_buddy_save, r);
1322 silc_free(pk);
1323 }
1324
1325 static void
1326 silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init)
1327 {
1328 SilcPurple sg = gc->proto_data;
1329 SilcClient client = sg->client;
1330 SilcClientConnection conn = sg->conn;
1331 SilcPurpleBuddyRes r;
1332 SilcBuffer attrs;
1333 const char *filename, *name = b->name;
1334
1335 r = silc_calloc(1, sizeof(*r));
1336 if (!r)
1337 return;
1338 r->client = client;
1339 r->conn = conn;
1340 r->b = b;
1341 r->init = init;
1342
1343 /* See if we have this buddy's public key. If we do use that
1344 to search the details. */
1345 filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key");
1346 if (filename) {
1347 SilcPublicKey public_key;
1348 SilcAttributeObjPk userpk;
1349
1350 if (!silc_pkcs_load_public_key(filename, &public_key,
1351 SILC_PKCS_FILE_PEM) &&
1352 !silc_pkcs_load_public_key(filename, &public_key,
1353 SILC_PKCS_FILE_BIN))
1354 return;
1355
1356 /* Get all attributes, and use the public key to search user */
1357 name = NULL;
1358 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
1359 SILC_ATTRIBUTE_SERVICE,
1360 SILC_ATTRIBUTE_STATUS_MOOD,
1361 SILC_ATTRIBUTE_STATUS_FREETEXT,
1362 SILC_ATTRIBUTE_STATUS_MESSAGE,
1363 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
1364 SILC_ATTRIBUTE_PREFERRED_CONTACT,
1365 SILC_ATTRIBUTE_TIMEZONE,
1366 SILC_ATTRIBUTE_GEOLOCATION,
1367 #ifdef SILC_ATTRIBUTE_USER_ICON
1368 SILC_ATTRIBUTE_USER_ICON,
1369 #endif
1370 SILC_ATTRIBUTE_DEVICE_INFO, 0);
1371 userpk.type = "silc-rsa";
1372 userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
1373 attrs = silc_attribute_payload_encode(attrs,
1374 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
1375 SILC_ATTRIBUTE_FLAG_VALID,
1376 &userpk, sizeof(userpk));
1377 silc_free(userpk.data);
1378 silc_pkcs_public_key_free(public_key);
1379 r->pubkey_search = TRUE;
1380 } else {
1381 /* Get all attributes */
1382 attrs = silc_client_attributes_request(0);
1383 }
1384
1385 /* Resolve */
1386 silc_client_get_clients_whois(client, conn, name, NULL, attrs,
1387 silcpurple_add_buddy_resolved, r);
1388 silc_buffer_free(attrs);
1389 }
1390
1391 void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
1392 {
1393 /* Don't add if the buddy is already on the list.
1394 *
1395 * SILC doesn't have groups, so we don't need to do anything
1396 * for a move. */
1397 if (purple_buddy_get_protocol_data(buddy) == NULL)
1398 silcpurple_add_buddy_i(gc, buddy, FALSE);
1399 }
1400
1401 void silcpurple_send_buddylist(PurpleConnection *gc)
1402 {
1403 PurpleBuddyList *blist;
1404 PurpleBlistNode *gnode, *cnode, *bnode;
1405 PurpleBuddy *buddy;
1406 PurpleAccount *account;
1407
1408 account = purple_connection_get_account(gc);
1409
1410 if ((blist = purple_get_blist()) != NULL)
1411 {
1412 for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
1413 {
1414 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
1415 continue;
1416 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
1417 {
1418 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
1419 continue;
1420 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
1421 {
1422 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
1423 continue;
1424 buddy = (PurpleBuddy *)bnode;
1425 if (purple_buddy_get_account(buddy) == account)
1426 silcpurple_add_buddy_i(gc, buddy, TRUE);
1427 }
1428 }
1429 }
1430 }
1431 }
1432
1433 void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
1434 PurpleGroup *group)
1435 {
1436 silc_free(buddy->proto_data);
1437 }
1438
1439 void silcpurple_idle_set(PurpleConnection *gc, int idle)
1440
1441 {
1442 SilcPurple sg;
1443 SilcClient client;
1444 SilcClientConnection conn;
1445 SilcAttributeObjService service;
1446 const char *server;
1447 int port;
1448
1449 sg = gc->proto_data;
1450 if (sg == NULL)
1451 return;
1452
1453 client = sg->client;
1454 if (client == NULL)
1455 return;
1456
1457 conn = sg->conn;
1458 if (conn == NULL)
1459 return;
1460
1461 server = purple_account_get_string(sg->account, "server",
1462 "silc.silcnet.org");
1463 port = purple_account_get_int(sg->account, "port", 706),
1464
1465 memset(&service, 0, sizeof(service));
1466 silc_client_attribute_del(client, conn,
1467 SILC_ATTRIBUTE_SERVICE, NULL);
1468 service.port = port;
1469 g_snprintf(service.address, sizeof(service.address), "%s", server);
1470 service.idle = idle;
1471 silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
1472 &service, sizeof(service));
1473 }
1474
1475 char *silcpurple_status_text(PurpleBuddy *b)
1476 {
1477 SilcPurple sg = b->account->gc->proto_data;
1478 SilcClient client = sg->client;
1479 SilcClientConnection conn = sg->conn;
1480 SilcClientID *client_id = b->proto_data;
1481 SilcClientEntry client_entry;
1482 SilcAttributePayload attr;
1483 SilcAttributeMood mood = 0;
1484
1485 /* Get the client entry. */
1486 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1487 if (!client_entry)
1488 return NULL;
1489
1490 /* If user is online, we show the mood status, if available.
1491 If user is offline or away that status is indicated. */
1492
1493 if (client_entry->mode & SILC_UMODE_DETACHED)
1494 return g_strdup(_("Detached"));
1495 if (client_entry->mode & SILC_UMODE_GONE)
1496 return g_strdup(_("Away"));
1497 if (client_entry->mode & SILC_UMODE_INDISPOSED)
1498 return g_strdup(_("Indisposed"));
1499 if (client_entry->mode & SILC_UMODE_BUSY)
1500 return g_strdup(_("Busy"));
1501 if (client_entry->mode & SILC_UMODE_PAGE)
1502 return g_strdup(_("Wake Me Up"));
1503 if (client_entry->mode & SILC_UMODE_HYPER)
1504 return g_strdup(_("Hyper Active"));
1505 if (client_entry->mode & SILC_UMODE_ROBOT)
1506 return g_strdup(_("Robot"));
1507
1508 attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
1509 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
1510 /* The mood is a bit mask, so we could show multiple moods,
1511 but let's show only one for now. */
1512 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
1513 return g_strdup(_("Happy"));
1514 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
1515 return g_strdup(_("Sad"));
1516 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
1517 return g_strdup(_("Angry"));
1518 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
1519 return g_strdup(_("Jealous"));
1520 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
1521 return g_strdup(_("Ashamed"));
1522 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
1523 return g_strdup(_("Invincible"));
1524 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
1525 return g_strdup(_("In Love"));
1526 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
1527 return g_strdup(_("Sleepy"));
1528 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
1529 return g_strdup(_("Bored"));
1530 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
1531 return g_strdup(_("Excited"));
1532 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
1533 return g_strdup(_("Anxious"));
1534 }
1535
1536 return NULL;
1537 }
1538
1539 void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
1540 {
1541 SilcPurple sg = b->account->gc->proto_data;
1542 SilcClient client = sg->client;
1543 SilcClientConnection conn = sg->conn;
1544 SilcClientID *client_id = b->proto_data;
1545 SilcClientEntry client_entry;
1546 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1547 char tmp[256];
1548
1549 /* Get the client entry. */
1550 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1551 if (!client_entry)
1552 return;
1553
1554 if (client_entry->nickname)
1555 purple_notify_user_info_add_pair(user_info, _("Nickname"),
1556 client_entry->nickname);
1557 if (client_entry->username && client_entry->hostname) {
1558 g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
1559 purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
1560 }
1561 if (client_entry->mode) {
1562 memset(tmp, 0, sizeof(tmp));
1563 silcpurple_get_umode_string(client_entry->mode,
1564 tmp, sizeof(tmp) - strlen(tmp));
1565 purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1566 }
1567
1568 silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1569
1570 if (statusstr) {
1571 purple_notify_user_info_add_pair(user_info, _("Message"), statusstr);
1572 g_free(statusstr);
1573 }
1574
1575 if (full) {
1576 if (moodstr) {
1577 purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1578 g_free(moodstr);
1579 }
1580
1581 if (contactstr) {
1582 purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1583 g_free(contactstr);
1584 }
1585
1586 if (langstr) {
1587 purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1588 g_free(langstr);
1589 }
1590
1591 if (devicestr) {
1592 purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1593 g_free(devicestr);
1594 }
1595
1596 if (tzstr) {
1597 purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1598 g_free(tzstr);
1599 }
1600
1601 if (geostr) {
1602 purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1603 g_free(geostr);
1604 }
1605 }
1606 }
1607
1608 static void
1609 silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data)
1610 {
1611 PurpleBuddy *b;
1612 PurpleConnection *gc;
1613 SilcPurple sg;
1614
1615 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
1616
1617 b = (PurpleBuddy *) node;
1618 gc = purple_account_get_connection(b->account);
1619 sg = gc->proto_data;
1620
1621 /* Call KILL */
1622 silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
1623 b->name, "Killed by operator", NULL);
1624 }
1625
1626 typedef struct {
1627 SilcPurple sg;
1628 SilcClientEntry client_entry;
1629 } *SilcPurpleBuddyWb;
1630
1631 static void
1632 silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data)
1633 {
1634 SilcPurpleBuddyWb wb = data;
1635 silcpurple_wb_init(wb->sg, wb->client_entry);
1636 silc_free(wb);
1637 }
1638
1639 GList *silcpurple_buddy_menu(PurpleBuddy *buddy)
1640 {
1641 PurpleConnection *gc = purple_account_get_connection(buddy->account);
1642 SilcPurple sg = gc->proto_data;
1643 SilcClientConnection conn = sg->conn;
1644 const char *pkfile = NULL;
1645 SilcClientEntry client_entry = NULL;
1646 PurpleMenuAction *act;
1647 GList *m = NULL;
1648 SilcPurpleBuddyWb wb;
1649
1650 pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key");
1651 client_entry = silc_client_get_client_by_id(sg->client,
1652 sg->conn,
1653 buddy->proto_data);
1654
1655 if (client_entry && client_entry->send_key) {
1656 act = purple_menu_action_new(_("Reset IM Key"),
1657 PURPLE_CALLBACK(silcpurple_buddy_resetkey),
1658 NULL, NULL);
1659 m = g_list_append(m, act);
1660
1661 } else {
1662 act = purple_menu_action_new(_("IM with Key Exchange"),
1663 PURPLE_CALLBACK(silcpurple_buddy_keyagr),
1664 NULL, NULL);
1665 m = g_list_append(m, act);
1666
1667 act = purple_menu_action_new(_("IM with Password"),
1668 PURPLE_CALLBACK(silcpurple_buddy_privkey_menu),
1669 NULL, NULL);
1670 m = g_list_append(m, act);
1671 }
1672
1673 if (pkfile) {
1674 act = purple_menu_action_new(_("Show Public Key"),
1675 PURPLE_CALLBACK(silcpurple_buddy_showkey),
1676 NULL, NULL);
1677 m = g_list_append(m, act);
1678
1679 } else {
1680 act = purple_menu_action_new(_("Get Public Key..."),
1681 PURPLE_CALLBACK(silcpurple_buddy_getkey_menu),
1682 NULL, NULL);
1683 m = g_list_append(m, act);
1684 }
1685
1686 if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
1687 act = purple_menu_action_new(_("Kill User"),
1688 PURPLE_CALLBACK(silcpurple_buddy_kill),
1689 NULL, NULL);
1690 m = g_list_append(m, act);
1691 }
1692
1693 if (client_entry) {
1694 wb = silc_calloc(1, sizeof(*wb));
1695 wb->sg = sg;
1696 wb->client_entry = client_entry;
1697 act = purple_menu_action_new(_("Draw On Whiteboard"),
1698 PURPLE_CALLBACK(silcpurple_buddy_wb),
1699 (void *)wb, NULL);
1700 m = g_list_append(m, act);
1701 }
1702 return m;
1703 }
1704
1705 #ifdef SILC_ATTRIBUTE_USER_ICON
1706 void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
1707 {
1708 SilcPurple sg = gc->proto_data;
1709 SilcClient client = sg->client;
1710 SilcClientConnection conn = sg->conn;
1711 SilcMime mime;
1712 char type[32];
1713 unsigned char *icon;
1714 const char *t;
1715 SilcAttributeObjMime obj;
1716
1717 /* Remove */
1718 if (!img) {
1719 silc_client_attribute_del(client, conn,
1720 SILC_ATTRIBUTE_USER_ICON, NULL);
1721 return;
1722 }
1723
1724 /* Add */
1725 mime = silc_mime_alloc();
1726 if (!mime)
1727 return;
1728
1729 t = purple_imgstore_get_extension(img);
1730 if (!t || !strcmp(t, "icon")) {
1731 silc_mime_free(mime);
1732 return;
1733 }
1734 if (!strcmp(t, "jpg"))
1735 t = "jpeg";
1736 g_snprintf(type, sizeof(type), "image/%s", t);
1737 silc_mime_add_field(mime, "Content-Type", type);
1738 silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
1739
1740 obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
1741 if (obj.mime)
1742 silc_client_attribute_add(client, conn,
1743 SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
1744
1745 silc_free(icon);
1746 silc_mime_free(mime);
1747 }
1748 #endif

mercurial