comparison libpurple/protocols/silc/buddy.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2
3 silcgaim_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 "silcgaim.h"
23 #include "wb.h"
24
25 /***************************** Key Agreement *********************************/
26
27 static void
28 silcgaim_buddy_keyagr(GaimBlistNode *node, gpointer data);
29
30 static void
31 silcgaim_buddy_keyagr_do(GaimConnection *gc, const char *name,
32 gboolean force_local);
33
34 typedef struct {
35 char *nick;
36 GaimConnection *gc;
37 } *SilcGaimResolve;
38
39 static void
40 silcgaim_buddy_keyagr_resolved(SilcClient client,
41 SilcClientConnection conn,
42 SilcClientEntry *clients,
43 SilcUInt32 clients_count,
44 void *context)
45 {
46 GaimConnection *gc = client->application;
47 SilcGaimResolve 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 gaim_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 silcgaim_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 } *SilcGaimKeyAgr;
68
69 static void
70 silcgaim_buddy_keyagr_cb(SilcClient client,
71 SilcClientConnection conn,
72 SilcClientEntry client_entry,
73 SilcKeyAgreementStatus status,
74 SilcSKEKeyMaterial *key,
75 void *context)
76 {
77 GaimConnection *gc = client->application;
78 SilcGaim sg = gc->proto_data;
79 SilcGaimKeyAgr a = context;
80
81 if (!sg->conn)
82 return;
83
84 switch (status) {
85 case SILC_KEY_AGREEMENT_OK:
86 {
87 GaimConversation *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 = gaim_find_conversation_with_account(GAIM_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 gaim_conv_window_show(gaim_conversation_get_window(convo));
104 */
105 } else {
106 convo = gaim_conversation_new(GAIM_CONV_TYPE_IM, sg->account,
107 client_entry->nickname);
108 }
109 g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
110 gaim_conversation_set_title(convo, tmp);
111 }
112 break;
113
114 case SILC_KEY_AGREEMENT_ERROR:
115 gaim_notify_error(gc, _("Key Agreement"),
116 _("Error occurred during key agreement"), NULL);
117 break;
118
119 case SILC_KEY_AGREEMENT_FAILURE:
120 gaim_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
121 break;
122
123 case SILC_KEY_AGREEMENT_TIMEOUT:
124 gaim_notify_error(gc, _("Key Agreement"),
125 _("Timeout during key agreement"), NULL);
126 break;
127
128 case SILC_KEY_AGREEMENT_ABORTED:
129 gaim_notify_error(gc, _("Key Agreement"),
130 _("Key agreement was aborted"), NULL);
131 break;
132
133 case SILC_KEY_AGREEMENT_ALREADY_STARTED:
134 gaim_notify_error(gc, _("Key Agreement"),
135 _("Key agreement is already started"), NULL);
136 break;
137
138 case SILC_KEY_AGREEMENT_SELF_DENIED:
139 gaim_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 silcgaim_buddy_keyagr_do(GaimConnection *gc, const char *name,
153 gboolean force_local)
154 {
155 SilcGaim 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 SilcGaimKeyAgr 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 SilcGaimResolve 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 silcgaim_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 && silcgaim_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 (silcgaim_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 silcgaim_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 } *SilcGaimKeyAgrAsk;
243
244 static void
245 silcgaim_buddy_keyagr_request_cb(SilcGaimKeyAgrAsk a, gint id)
246 {
247 SilcGaimKeyAgr 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 gaim_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 silcgaim_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 silcgaim_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 silcgaim_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 SilcGaimKeyAgrAsk a;
292
293 g_snprintf(tmp, sizeof(tmp),
294 _("Key agreement request received from %s. Would you like to "
295 "perform the key agreement?"), client_entry->nickname);
296 if (hostname)
297 g_snprintf(tmp2, sizeof(tmp2),
298 _("The remote user is waiting key agreement on:\n"
299 "Remote host: %s\nRemote port: %d"), hostname, port);
300
301 a = silc_calloc(1, sizeof(*a));
302 if (!a)
303 return;
304 a->client = client;
305 a->conn = conn;
306 a->client_id = *client_entry->id;
307 if (hostname)
308 a->hostname = strdup(hostname);
309 a->port = port;
310
311 gaim_request_action(client->application, _("Key Agreement Request"), tmp,
312 hostname ? tmp2 : NULL, 1, a, 2,
313 _("Yes"), G_CALLBACK(silcgaim_buddy_keyagr_request_cb),
314 _("No"), G_CALLBACK(silcgaim_buddy_keyagr_request_cb));
315 }
316
317 static void
318 silcgaim_buddy_keyagr(GaimBlistNode *node, gpointer data)
319 {
320 GaimBuddy *buddy;
321
322 buddy = (GaimBuddy *)node;
323 silcgaim_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE);
324 }
325
326
327 /**************************** Static IM Key **********************************/
328
329 static void
330 silcgaim_buddy_resetkey(GaimBlistNode *node, gpointer data)
331 {
332 GaimBuddy *b;
333 GaimConnection *gc;
334 SilcGaim sg;
335 char *nickname;
336 SilcClientEntry *clients;
337 SilcUInt32 clients_count;
338
339 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
340
341 b = (GaimBuddy *) node;
342 gc = gaim_account_get_connection(b->account);
343 sg = gc->proto_data;
344
345 if (!silc_parse_userfqdn(b->name, &nickname, NULL))
346 return;
347
348 /* Find client entry */
349 clients = silc_client_get_clients_local(sg->client, sg->conn,
350 nickname, b->name,
351 &clients_count);
352 if (!clients) {
353 silc_free(nickname);
354 return;
355 }
356
357 clients[0]->prv_resp = FALSE;
358 silc_client_del_private_message_key(sg->client, sg->conn,
359 clients[0]);
360 silc_free(clients);
361 silc_free(nickname);
362 }
363
364 typedef struct {
365 SilcClient client;
366 SilcClientConnection conn;
367 SilcClientID client_id;
368 } *SilcGaimPrivkey;
369
370 static void
371 silcgaim_buddy_privkey(GaimConnection *gc, const char *name);
372
373 static void
374 silcgaim_buddy_privkey_cb(SilcGaimPrivkey p, const char *passphrase)
375 {
376 SilcClientEntry client_entry;
377
378 if (!passphrase || !(*passphrase)) {
379 silc_free(p);
380 return;
381 }
382
383 /* Get the client entry. */
384 client_entry = silc_client_get_client_by_id(p->client, p->conn,
385 &p->client_id);
386 if (!client_entry) {
387 gaim_notify_error(p->client->application, _("IM With Password"),
388 _("The remote user is not present in the network any more"),
389 NULL);
390 silc_free(p);
391 return;
392 }
393
394 /* Set the private message key */
395 silc_client_del_private_message_key(p->client, p->conn,
396 client_entry);
397 silc_client_add_private_message_key(p->client, p->conn,
398 client_entry, NULL, NULL,
399 (unsigned char *)passphrase,
400 strlen(passphrase), FALSE,
401 client_entry->prv_resp);
402 if (!client_entry->prv_resp)
403 silc_client_send_private_message_key_request(p->client,
404 p->conn,
405 client_entry);
406 silc_free(p);
407 }
408
409 static void
410 silcgaim_buddy_privkey_resolved(SilcClient client,
411 SilcClientConnection conn,
412 SilcClientEntry *clients,
413 SilcUInt32 clients_count,
414 void *context)
415 {
416 char tmp[256];
417
418 if (!clients) {
419 g_snprintf(tmp, sizeof(tmp),
420 _("User %s is not present in the network"),
421 (const char *)context);
422 gaim_notify_error(client->application, _("IM With Password"),
423 _("Cannot set IM key"), tmp);
424 g_free(context);
425 return;
426 }
427
428 silcgaim_buddy_privkey(client->application, context);
429 silc_free(context);
430 }
431
432 static void
433 silcgaim_buddy_privkey(GaimConnection *gc, const char *name)
434 {
435 SilcGaim sg = gc->proto_data;
436 char *nickname;
437 SilcGaimPrivkey p;
438 SilcClientEntry *clients;
439 SilcUInt32 clients_count;
440
441 if (!name)
442 return;
443 if (!silc_parse_userfqdn(name, &nickname, NULL))
444 return;
445
446 /* Find client entry */
447 clients = silc_client_get_clients_local(sg->client, sg->conn,
448 nickname, name,
449 &clients_count);
450 if (!clients) {
451 silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
452 silcgaim_buddy_privkey_resolved,
453 g_strdup(name));
454 silc_free(nickname);
455 return;
456 }
457
458 p = silc_calloc(1, sizeof(*p));
459 if (!p)
460 return;
461 p->client = sg->client;
462 p->conn = sg->conn;
463 p->client_id = *clients[0]->id;
464 gaim_request_input(gc, _("IM With Password"), NULL,
465 _("Set IM Password"), NULL, FALSE, TRUE, NULL,
466 _("OK"), G_CALLBACK(silcgaim_buddy_privkey_cb),
467 _("Cancel"), G_CALLBACK(silcgaim_buddy_privkey_cb),
468 p);
469
470 silc_free(clients);
471 silc_free(nickname);
472 }
473
474 static void
475 silcgaim_buddy_privkey_menu(GaimBlistNode *node, gpointer data)
476 {
477 GaimBuddy *buddy;
478 GaimConnection *gc;
479
480 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
481
482 buddy = (GaimBuddy *) node;
483 gc = gaim_account_get_connection(buddy->account);
484
485 silcgaim_buddy_privkey(gc, buddy->name);
486 }
487
488
489 /**************************** Get Public Key *********************************/
490
491 typedef struct {
492 SilcClient client;
493 SilcClientConnection conn;
494 SilcClientID client_id;
495 } *SilcGaimBuddyGetkey;
496
497 static void
498 silcgaim_buddy_getkey(GaimConnection *gc, const char *name);
499
500 static void
501 silcgaim_buddy_getkey_cb(SilcGaimBuddyGetkey g,
502 SilcClientCommandReplyContext cmd)
503 {
504 SilcClientEntry client_entry;
505 unsigned char *pk;
506 SilcUInt32 pk_len;
507
508 /* Get the client entry. */
509 client_entry = silc_client_get_client_by_id(g->client, g->conn,
510 &g->client_id);
511 if (!client_entry) {
512 gaim_notify_error(g->client->application, _("Get Public Key"),
513 _("The remote user is not present in the network any more"),
514 NULL);
515 silc_free(g);
516 return;
517 }
518
519 if (!client_entry->public_key) {
520 silc_free(g);
521 return;
522 }
523
524 /* Now verify the public key */
525 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
526 silcgaim_verify_public_key(g->client, g->conn, client_entry->nickname,
527 SILC_SOCKET_TYPE_CLIENT,
528 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
529 NULL, NULL);
530 silc_free(pk);
531 silc_free(g);
532 }
533
534 static void
535 silcgaim_buddy_getkey_resolved(SilcClient client,
536 SilcClientConnection conn,
537 SilcClientEntry *clients,
538 SilcUInt32 clients_count,
539 void *context)
540 {
541 char tmp[256];
542
543 if (!clients) {
544 g_snprintf(tmp, sizeof(tmp),
545 _("User %s is not present in the network"),
546 (const char *)context);
547 gaim_notify_error(client->application, _("Get Public Key"),
548 _("Cannot fetch the public key"), tmp);
549 g_free(context);
550 return;
551 }
552
553 silcgaim_buddy_getkey(client->application, context);
554 silc_free(context);
555 }
556
557 static void
558 silcgaim_buddy_getkey(GaimConnection *gc, const char *name)
559 {
560 SilcGaim sg = gc->proto_data;
561 SilcClient client = sg->client;
562 SilcClientConnection conn = sg->conn;
563 SilcClientEntry *clients;
564 SilcUInt32 clients_count;
565 SilcGaimBuddyGetkey g;
566 char *nickname;
567
568 if (!name)
569 return;
570
571 if (!silc_parse_userfqdn(name, &nickname, NULL))
572 return;
573
574 /* Find client entry */
575 clients = silc_client_get_clients_local(client, conn, nickname, name,
576 &clients_count);
577 if (!clients) {
578 silc_client_get_clients(client, conn, nickname, NULL,
579 silcgaim_buddy_getkey_resolved,
580 g_strdup(name));
581 silc_free(nickname);
582 return;
583 }
584
585 /* Call GETKEY */
586 g = silc_calloc(1, sizeof(*g));
587 if (!g)
588 return;
589 g->client = client;
590 g->conn = conn;
591 g->client_id = *clients[0]->id;
592 silc_client_command_call(client, conn, NULL, "GETKEY",
593 clients[0]->nickname, NULL);
594 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
595 conn->cmd_ident,
596 (SilcCommandCb)silcgaim_buddy_getkey_cb, g);
597 silc_free(clients);
598 silc_free(nickname);
599 }
600
601 static void
602 silcgaim_buddy_getkey_menu(GaimBlistNode *node, gpointer data)
603 {
604 GaimBuddy *buddy;
605 GaimConnection *gc;
606
607 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
608
609 buddy = (GaimBuddy *) node;
610 gc = gaim_account_get_connection(buddy->account);
611
612 silcgaim_buddy_getkey(gc, buddy->name);
613 }
614
615 static void
616 silcgaim_buddy_showkey(GaimBlistNode *node, gpointer data)
617 {
618 GaimBuddy *b;
619 GaimConnection *gc;
620 SilcGaim sg;
621 SilcPublicKey public_key;
622 const char *pkfile;
623
624 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
625
626 b = (GaimBuddy *) node;
627 gc = gaim_account_get_connection(b->account);
628 sg = gc->proto_data;
629
630 pkfile = gaim_blist_node_get_string(node, "public-key");
631 if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
632 !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
633 gaim_notify_error(gc,
634 _("Show Public Key"),
635 _("Could not load public key"), NULL);
636 return;
637 }
638
639 silcgaim_show_public_key(sg, b->name, public_key, NULL, NULL);
640 silc_pkcs_public_key_free(public_key);
641 }
642
643
644 /**************************** Buddy routines *********************************/
645
646 /* The buddies are implemented by using the WHOIS and WATCH commands that
647 can be used to search users by their public key. Since nicknames aren't
648 unique in SILC we cannot trust the buddy list using their nickname. We
649 associate public keys to buddies and use those to search and watch
650 in the network.
651
652 The problem is that Gaim does not return GaimBuddy contexts to the
653 callbacks but the buddy names. Naturally, this is not going to work
654 with SILC. But, for now, we have to do what we can... */
655
656 typedef struct {
657 SilcClient client;
658 SilcClientConnection conn;
659 SilcClientID client_id;
660 GaimBuddy *b;
661 unsigned char *offline_pk;
662 SilcUInt32 offline_pk_len;
663 unsigned int offline : 1;
664 unsigned int pubkey_search : 1;
665 unsigned int init : 1;
666 } *SilcGaimBuddyRes;
667
668 static void
669 silcgaim_add_buddy_ask_pk_cb(SilcGaimBuddyRes r, gint id);
670 static void
671 silcgaim_add_buddy_resolved(SilcClient client,
672 SilcClientConnection conn,
673 SilcClientEntry *clients,
674 SilcUInt32 clients_count,
675 void *context);
676
677 void silcgaim_get_info(GaimConnection *gc, const char *who)
678 {
679 SilcGaim sg = gc->proto_data;
680 SilcClient client = sg->client;
681 SilcClientConnection conn = sg->conn;
682 SilcClientEntry client_entry;
683 GaimBuddy *b;
684 const char *filename, *nick = who;
685 char tmp[256];
686
687 if (!who)
688 return;
689 if (strlen(who) > 1 && who[0] == '@')
690 nick = who + 1;
691 if (strlen(who) > 1 && who[0] == '*')
692 nick = who + 1;
693 if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
694 nick = who + 2;
695
696 b = gaim_find_buddy(gc->account, nick);
697 if (b) {
698 /* See if we have this buddy's public key. If we do use that
699 to search the details. */
700 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
701 if (filename) {
702 /* Call WHOIS. The user info is displayed in the WHOIS
703 command reply. */
704 silc_client_command_call(client, conn, NULL, "WHOIS",
705 "-details", "-pubkey", filename, NULL);
706 return;
707 }
708
709 if (!b->proto_data) {
710 g_snprintf(tmp, sizeof(tmp),
711 _("User %s is not present in the network"), b->name);
712 gaim_notify_error(gc, _("User Information"),
713 _("Cannot get user information"), tmp);
714 return;
715 }
716
717 client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
718 if (client_entry) {
719 /* Call WHOIS. The user info is displayed in the WHOIS
720 command reply. */
721 silc_client_command_call(client, conn, NULL, "WHOIS",
722 client_entry->nickname, "-details", NULL);
723 }
724 } else {
725 /* Call WHOIS just with nickname. */
726 silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL);
727 }
728 }
729
730 static void
731 silcgaim_add_buddy_pk_no(SilcGaimBuddyRes r)
732 {
733 char tmp[512];
734 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
735 r->b->name);
736 gaim_notify_error(r->client->application, _("Add Buddy"), tmp,
737 _("You cannot receive buddy notifications until you "
738 "import his/her public key. You can use the Get Public Key "
739 "command to get the public key."));
740 gaim_prpl_got_user_status(gaim_buddy_get_account(r->b), gaim_buddy_get_name(r->b), SILCGAIM_STATUS_ID_OFFLINE, NULL);
741 }
742
743 static void
744 silcgaim_add_buddy_save(bool success, void *context)
745 {
746 SilcGaimBuddyRes r = context;
747 GaimBuddy *b = r->b;
748 SilcClient client = r->client;
749 SilcClientEntry client_entry;
750 SilcAttributePayload attr;
751 SilcAttribute attribute;
752 SilcVCardStruct vcard;
753 SilcAttributeObjMime message, extension;
754 #ifdef SILC_ATTRIBUTE_USER_ICON
755 SilcAttributeObjMime usericon;
756 #endif
757 SilcAttributeObjPk serverpk, usersign, serversign;
758 gboolean usign_success = TRUE, ssign_success = TRUE;
759 char filename[512], filename2[512], *fingerprint = NULL, *tmp;
760 SilcUInt32 len;
761 int i;
762
763 if (!success) {
764 /* The user did not trust the public key. */
765 silcgaim_add_buddy_pk_no(r);
766 silc_free(r);
767 return;
768 }
769
770 if (r->offline) {
771 /* User is offline. Associate the imported public key with
772 this user. */
773 fingerprint = silc_hash_fingerprint(NULL, r->offline_pk,
774 r->offline_pk_len);
775 for (i = 0; i < strlen(fingerprint); i++)
776 if (fingerprint[i] == ' ')
777 fingerprint[i] = '_';
778 g_snprintf(filename, sizeof(filename) - 1,
779 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
780 silcgaim_silcdir(), fingerprint);
781 gaim_blist_node_set_string((GaimBlistNode *)b, "public-key", filename);
782 gaim_prpl_got_user_status(gaim_buddy_get_account(r->b), gaim_buddy_get_name(r->b), SILCGAIM_STATUS_ID_OFFLINE, NULL);
783 silc_free(fingerprint);
784 silc_free(r->offline_pk);
785 silc_free(r);
786 return;
787 }
788
789 /* Get the client entry. */
790 client_entry = silc_client_get_client_by_id(r->client, r->conn,
791 &r->client_id);
792 if (!client_entry) {
793 silc_free(r);
794 return;
795 }
796
797 memset(&vcard, 0, sizeof(vcard));
798 memset(&message, 0, sizeof(message));
799 memset(&extension, 0, sizeof(extension));
800 #ifdef SILC_ATTRIBUTE_USER_ICON
801 memset(&usericon, 0, sizeof(usericon));
802 #endif
803 memset(&serverpk, 0, sizeof(serverpk));
804 memset(&usersign, 0, sizeof(usersign));
805 memset(&serversign, 0, sizeof(serversign));
806
807 /* Now that we have the public key and we trust it now we
808 save the attributes of the buddy and update its status. */
809
810 if (client_entry->attrs) {
811 silc_dlist_start(client_entry->attrs);
812 while ((attr = silc_dlist_get(client_entry->attrs))
813 != SILC_LIST_END) {
814 attribute = silc_attribute_get_attribute(attr);
815
816 switch (attribute) {
817 case SILC_ATTRIBUTE_USER_INFO:
818 if (!silc_attribute_get_object(attr, (void *)&vcard,
819 sizeof(vcard)))
820 continue;
821 break;
822
823 case SILC_ATTRIBUTE_STATUS_MESSAGE:
824 if (!silc_attribute_get_object(attr, (void *)&message,
825 sizeof(message)))
826 continue;
827 break;
828
829 case SILC_ATTRIBUTE_EXTENSION:
830 if (!silc_attribute_get_object(attr, (void *)&extension,
831 sizeof(extension)))
832 continue;
833 break;
834
835 #ifdef SILC_ATTRIBUTE_USER_ICON
836 case SILC_ATTRIBUTE_USER_ICON:
837 if (!silc_attribute_get_object(attr, (void *)&usericon,
838 sizeof(usericon)))
839 continue;
840 break;
841 #endif
842
843 case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
844 if (serverpk.type)
845 continue;
846 if (!silc_attribute_get_object(attr, (void *)&serverpk,
847 sizeof(serverpk)))
848 continue;
849 break;
850
851 case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
852 if (usersign.data)
853 continue;
854 if (!silc_attribute_get_object(attr, (void *)&usersign,
855 sizeof(usersign)))
856 continue;
857 break;
858
859 case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
860 if (serversign.data)
861 continue;
862 if (!silc_attribute_get_object(attr, (void *)&serversign,
863 sizeof(serversign)))
864 continue;
865 break;
866
867 default:
868 break;
869 }
870 }
871 }
872
873 /* Verify the attribute signatures */
874
875 if (usersign.data) {
876 SilcPKCS pkcs;
877 unsigned char *verifyd;
878 SilcUInt32 verify_len;
879
880 silc_pkcs_alloc((unsigned char*)"rsa", &pkcs);
881 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
882 FALSE, &verify_len);
883 if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
884 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
885 usersign.data,
886 usersign.data_len,
887 verifyd, verify_len))
888 usign_success = FALSE;
889 }
890 silc_free(verifyd);
891 }
892
893 if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
894 SilcPublicKey public_key;
895 SilcPKCS pkcs;
896 unsigned char *verifyd;
897 SilcUInt32 verify_len;
898
899 if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
900 &public_key)) {
901 silc_pkcs_alloc((unsigned char *)"rsa", &pkcs);
902 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
903 TRUE, &verify_len);
904 if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
905 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
906 serversign.data,
907 serversign.data_len,
908 verifyd, verify_len))
909 ssign_success = FALSE;
910 }
911 silc_pkcs_public_key_free(public_key);
912 silc_free(verifyd);
913 }
914 }
915
916 fingerprint = silc_fingerprint(client_entry->fingerprint,
917 client_entry->fingerprint_len);
918 for (i = 0; i < strlen(fingerprint); i++)
919 if (fingerprint[i] == ' ')
920 fingerprint[i] = '_';
921
922 if (usign_success || ssign_success) {
923 struct passwd *pw;
924 struct stat st;
925
926 memset(filename2, 0, sizeof(filename2));
927
928 /* Filename for dir */
929 tmp = fingerprint + strlen(fingerprint) - 9;
930 g_snprintf(filename, sizeof(filename) - 1,
931 "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s",
932 silcgaim_silcdir(), tmp);
933
934 pw = getpwuid(getuid());
935 if (!pw)
936 return;
937
938 /* Create dir if it doesn't exist */
939 if ((g_stat(filename, &st)) == -1) {
940 if (errno == ENOENT) {
941 if (pw->pw_uid == geteuid())
942 g_mkdir(filename, 0755);
943 }
944 }
945
946 /* Save VCard */
947 g_snprintf(filename2, sizeof(filename2) - 1,
948 "%s" G_DIR_SEPARATOR_S "vcard", filename);
949 if (vcard.full_name) {
950 tmp = (char *)silc_vcard_encode(&vcard, &len);
951 silc_file_writefile(filename2, tmp, len);
952 silc_free(tmp);
953 }
954
955 /* Save status message */
956 if (message.mime) {
957 memset(filename2, 0, sizeof(filename2));
958 g_snprintf(filename2, sizeof(filename2) - 1,
959 "%s" G_DIR_SEPARATOR_S "status_message.mime",
960 filename);
961 silc_file_writefile(filename2, (char *)message.mime,
962 message.mime_len);
963 }
964
965 /* Save extension data */
966 if (extension.mime) {
967 memset(filename2, 0, sizeof(filename2));
968 g_snprintf(filename2, sizeof(filename2) - 1,
969 "%s" G_DIR_SEPARATOR_S "extension.mime",
970 filename);
971 silc_file_writefile(filename2, (char *)extension.mime,
972 extension.mime_len);
973 }
974
975 #ifdef SILC_ATTRIBUTE_USER_ICON
976 /* Save user icon */
977 if (usericon.mime) {
978 SilcMime m = silc_mime_decode(usericon.mime,
979 usericon.mime_len);
980 if (m) {
981 const char *type = silc_mime_get_field(m, "Content-Type");
982 if (!strcmp(type, "image/jpeg") ||
983 !strcmp(type, "image/gif") ||
984 !strcmp(type, "image/bmp") ||
985 !strcmp(type, "image/png")) {
986 const unsigned char *data;
987 SilcUInt32 data_len;
988 data = silc_mime_get_data(m, &data_len);
989 if (data)
990 gaim_buddy_icons_set_for_user(gaim_buddy_get_account(r->b), gaim_buddy_get_name(r->b), (void *)data, data_len);
991 }
992 silc_mime_free(m);
993 }
994 }
995 #endif
996 }
997
998 /* Save the public key path to buddy properties, as it is used
999 to identify the buddy in the network (and not the nickname). */
1000 memset(filename, 0, sizeof(filename));
1001 g_snprintf(filename, sizeof(filename) - 1,
1002 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
1003 silcgaim_silcdir(), fingerprint);
1004 gaim_blist_node_set_string((GaimBlistNode *)b, "public-key", filename);
1005
1006 /* Update online status */
1007 gaim_prpl_got_user_status(gaim_buddy_get_account(r->b), gaim_buddy_get_name(r->b), SILCGAIM_STATUS_ID_AVAILABLE, NULL);
1008
1009 /* Finally, start watching this user so we receive its status
1010 changes from the server */
1011 g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
1012 silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
1013 filename2, NULL);
1014
1015 silc_free(fingerprint);
1016 silc_free(r);
1017 }
1018
1019 static void
1020 silcgaim_add_buddy_ask_import(void *user_data, const char *name)
1021 {
1022 SilcGaimBuddyRes r = (SilcGaimBuddyRes)user_data;
1023 SilcPublicKey public_key;
1024
1025 /* Load the public key */
1026 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
1027 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
1028 silcgaim_add_buddy_ask_pk_cb(r, 0);
1029 gaim_notify_error(r->client->application,
1030 _("Add Buddy"), _("Could not load public key"), NULL);
1031 return;
1032 }
1033
1034 /* Now verify the public key */
1035 r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
1036 silcgaim_verify_public_key(r->client, r->conn, r->b->name,
1037 SILC_SOCKET_TYPE_CLIENT,
1038 r->offline_pk, r->offline_pk_len,
1039 SILC_SKE_PK_TYPE_SILC,
1040 silcgaim_add_buddy_save, r);
1041 }
1042
1043 static void
1044 silcgaim_add_buddy_ask_pk_cancel(void *user_data, const char *name)
1045 {
1046 SilcGaimBuddyRes r = (SilcGaimBuddyRes)user_data;
1047
1048 /* The user did not import public key. The buddy is unusable. */
1049 silcgaim_add_buddy_pk_no(r);
1050 silc_free(r);
1051 }
1052
1053 static void
1054 silcgaim_add_buddy_ask_pk_cb(SilcGaimBuddyRes r, gint id)
1055 {
1056 if (id != 0) {
1057 /* The user did not import public key. The buddy is unusable. */
1058 silcgaim_add_buddy_pk_no(r);
1059 silc_free(r);
1060 return;
1061 }
1062
1063 /* Open file selector to select the public key. */
1064 gaim_request_file(r->client->application, _("Open..."), NULL, FALSE,
1065 G_CALLBACK(silcgaim_add_buddy_ask_import),
1066 G_CALLBACK(silcgaim_add_buddy_ask_pk_cancel), r);
1067 }
1068
1069 static void
1070 silcgaim_add_buddy_ask_pk(SilcGaimBuddyRes r)
1071 {
1072 char tmp[512];
1073 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
1074 r->b->name);
1075 gaim_request_action(r->client->application, _("Add Buddy"), tmp,
1076 _("To add the buddy you must import his/her public key. "
1077 "Press Import to import a public key."), 0, r, 2,
1078 _("Cancel"), G_CALLBACK(silcgaim_add_buddy_ask_pk_cb),
1079 _("_Import..."), G_CALLBACK(silcgaim_add_buddy_ask_pk_cb));
1080 }
1081
1082 static void
1083 silcgaim_add_buddy_getkey_cb(SilcGaimBuddyRes r,
1084 SilcClientCommandReplyContext cmd)
1085 {
1086 SilcClientEntry client_entry;
1087 unsigned char *pk;
1088 SilcUInt32 pk_len;
1089
1090 /* Get the client entry. */
1091 client_entry = silc_client_get_client_by_id(r->client, r->conn,
1092 &r->client_id);
1093 if (!client_entry || !client_entry->public_key) {
1094 /* The buddy is offline/nonexistent. We will require user
1095 to associate a public key with the buddy or the buddy
1096 cannot be added. */
1097 r->offline = TRUE;
1098 silcgaim_add_buddy_ask_pk(r);
1099 return;
1100 }
1101
1102 /* Now verify the public key */
1103 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1104 silcgaim_verify_public_key(r->client, r->conn, client_entry->nickname,
1105 SILC_SOCKET_TYPE_CLIENT,
1106 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1107 silcgaim_add_buddy_save, r);
1108 silc_free(pk);
1109 }
1110
1111 static void
1112 silcgaim_add_buddy_select_cb(SilcGaimBuddyRes r, GaimRequestFields *fields)
1113 {
1114 GaimRequestField *f;
1115 const GList *list;
1116 SilcClientEntry client_entry;
1117
1118 f = gaim_request_fields_get_field(fields, "list");
1119 list = gaim_request_field_list_get_selected(f);
1120 if (!list) {
1121 /* The user did not select any user. */
1122 silcgaim_add_buddy_pk_no(r);
1123 silc_free(r);
1124 return;
1125 }
1126
1127 client_entry = gaim_request_field_list_get_data(f, list->data);
1128 silcgaim_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
1129 }
1130
1131 static void
1132 silcgaim_add_buddy_select_cancel(SilcGaimBuddyRes r, GaimRequestFields *fields)
1133 {
1134 /* The user did not select any user. */
1135 silcgaim_add_buddy_pk_no(r);
1136 silc_free(r);
1137 }
1138
1139 static void
1140 silcgaim_add_buddy_select(SilcGaimBuddyRes r,
1141 SilcClientEntry *clients,
1142 SilcUInt32 clients_count)
1143 {
1144 GaimRequestFields *fields;
1145 GaimRequestFieldGroup *g;
1146 GaimRequestField *f;
1147 char tmp[512], tmp2[128];
1148 int i;
1149 char *fingerprint;
1150
1151 fields = gaim_request_fields_new();
1152 g = gaim_request_field_group_new(NULL);
1153 f = gaim_request_field_list_new("list", NULL);
1154 gaim_request_field_group_add_field(g, f);
1155 gaim_request_field_list_set_multi_select(f, FALSE);
1156 gaim_request_fields_add_group(fields, g);
1157
1158 for (i = 0; i < clients_count; i++) {
1159 fingerprint = NULL;
1160 if (clients[i]->fingerprint) {
1161 fingerprint = silc_fingerprint(clients[i]->fingerprint,
1162 clients[i]->fingerprint_len);
1163 g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint);
1164 }
1165 g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s",
1166 clients[i]->realname, clients[i]->nickname,
1167 clients[i]->username, clients[i]->hostname ?
1168 clients[i]->hostname : "",
1169 fingerprint ? tmp2 : "");
1170 gaim_request_field_list_add(f, tmp, clients[i]);
1171 silc_free(fingerprint);
1172 }
1173
1174 gaim_request_fields(r->client->application, _("Add Buddy"),
1175 _("Select correct user"),
1176 r->pubkey_search
1177 ? _("More than one user was found with the same public key. Select "
1178 "the correct user from the list to add to the buddy list.")
1179 : _("More than one user was found with the same name. Select "
1180 "the correct user from the list to add to the buddy list."),
1181 fields,
1182 _("OK"), G_CALLBACK(silcgaim_add_buddy_select_cb),
1183 _("Cancel"), G_CALLBACK(silcgaim_add_buddy_select_cancel), r);
1184 }
1185
1186 static void
1187 silcgaim_add_buddy_resolved(SilcClient client,
1188 SilcClientConnection conn,
1189 SilcClientEntry *clients,
1190 SilcUInt32 clients_count,
1191 void *context)
1192 {
1193 SilcGaimBuddyRes r = context;
1194 GaimBuddy *b = r->b;
1195 SilcAttributePayload pub;
1196 SilcAttributeObjPk userpk;
1197 unsigned char *pk;
1198 SilcUInt32 pk_len;
1199 const char *filename;
1200
1201 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
1202
1203 /* If the buddy is offline/nonexistent, we will require user
1204 to associate a public key with the buddy or the buddy
1205 cannot be added. */
1206 if (!clients_count) {
1207 if (r->init) {
1208 silc_free(r);
1209 return;
1210 }
1211
1212 r->offline = TRUE;
1213 /* If the user has already associated a public key, try loading it
1214 * before prompting the user to load it again */
1215 if (filename != NULL)
1216 silcgaim_add_buddy_ask_import(r, filename);
1217 else
1218 silcgaim_add_buddy_ask_pk(r);
1219 return;
1220 }
1221
1222 /* If more than one client was found with nickname, we need to verify
1223 from user which one is the correct. */
1224 if (clients_count > 1 && !r->pubkey_search) {
1225 if (r->init) {
1226 silc_free(r);
1227 return;
1228 }
1229
1230 silcgaim_add_buddy_select(r, clients, clients_count);
1231 return;
1232 }
1233
1234 /* If we searched using public keys and more than one entry was found
1235 the same person is logged on multiple times. */
1236 if (clients_count > 1 && r->pubkey_search && b->name) {
1237 if (r->init) {
1238 /* Find the entry that closest matches to the
1239 buddy nickname. */
1240 int i;
1241 for (i = 0; i < clients_count; i++) {
1242 if (!strncasecmp(b->name, clients[i]->nickname,
1243 strlen(b->name))) {
1244 clients[0] = clients[i];
1245 break;
1246 }
1247 }
1248 } else {
1249 /* Verify from user which one is correct */
1250 silcgaim_add_buddy_select(r, clients, clients_count);
1251 return;
1252 }
1253 }
1254
1255 /* The client was found. Now get its public key and verify
1256 that before adding the buddy. */
1257 memset(&userpk, 0, sizeof(userpk));
1258 b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
1259 r->client_id = *clients[0]->id;
1260
1261 /* Get the public key from attributes, if not present then
1262 resolve it with GETKEY unless we have it cached already. */
1263 if (clients[0]->attrs && !clients[0]->public_key) {
1264 pub = silcgaim_get_attr(clients[0]->attrs,
1265 SILC_ATTRIBUTE_USER_PUBLIC_KEY);
1266 if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
1267 sizeof(userpk))) {
1268 /* Get public key with GETKEY */
1269 silc_client_command_call(client, conn, NULL,
1270 "GETKEY", clients[0]->nickname, NULL);
1271 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1272 conn->cmd_ident,
1273 (SilcCommandCb)silcgaim_add_buddy_getkey_cb,
1274 r);
1275 return;
1276 }
1277 if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
1278 &clients[0]->public_key))
1279 return;
1280 silc_free(userpk.data);
1281 } else if (filename && !clients[0]->public_key) {
1282 if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1283 SILC_PKCS_FILE_PEM) &&
1284 !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1285 SILC_PKCS_FILE_BIN)) {
1286 /* Get public key with GETKEY */
1287 silc_client_command_call(client, conn, NULL,
1288 "GETKEY", clients[0]->nickname, NULL);
1289 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1290 conn->cmd_ident,
1291 (SilcCommandCb)silcgaim_add_buddy_getkey_cb,
1292 r);
1293 return;
1294 }
1295 } else if (!clients[0]->public_key) {
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)silcgaim_add_buddy_getkey_cb,
1302 r);
1303 return;
1304 }
1305
1306 /* We have the public key, verify it. */
1307 pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
1308 silcgaim_verify_public_key(client, conn, clients[0]->nickname,
1309 SILC_SOCKET_TYPE_CLIENT,
1310 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1311 silcgaim_add_buddy_save, r);
1312 silc_free(pk);
1313 }
1314
1315 static void
1316 silcgaim_add_buddy_i(GaimConnection *gc, GaimBuddy *b, gboolean init)
1317 {
1318 SilcGaim sg = gc->proto_data;
1319 SilcClient client = sg->client;
1320 SilcClientConnection conn = sg->conn;
1321 SilcGaimBuddyRes r;
1322 SilcBuffer attrs;
1323 const char *filename, *name = b->name;
1324
1325 r = silc_calloc(1, sizeof(*r));
1326 if (!r)
1327 return;
1328 r->client = client;
1329 r->conn = conn;
1330 r->b = b;
1331 r->init = init;
1332
1333 /* See if we have this buddy's public key. If we do use that
1334 to search the details. */
1335 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
1336 if (filename) {
1337 SilcPublicKey public_key;
1338 SilcAttributeObjPk userpk;
1339
1340 if (!silc_pkcs_load_public_key(filename, &public_key,
1341 SILC_PKCS_FILE_PEM) &&
1342 !silc_pkcs_load_public_key(filename, &public_key,
1343 SILC_PKCS_FILE_BIN))
1344 return;
1345
1346 /* Get all attributes, and use the public key to search user */
1347 name = NULL;
1348 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
1349 SILC_ATTRIBUTE_SERVICE,
1350 SILC_ATTRIBUTE_STATUS_MOOD,
1351 SILC_ATTRIBUTE_STATUS_FREETEXT,
1352 SILC_ATTRIBUTE_STATUS_MESSAGE,
1353 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
1354 SILC_ATTRIBUTE_PREFERRED_CONTACT,
1355 SILC_ATTRIBUTE_TIMEZONE,
1356 SILC_ATTRIBUTE_GEOLOCATION,
1357 #ifdef SILC_ATTRIBUTE_USER_ICON
1358 SILC_ATTRIBUTE_USER_ICON,
1359 #endif
1360 SILC_ATTRIBUTE_DEVICE_INFO, 0);
1361 userpk.type = "silc-rsa";
1362 userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
1363 attrs = silc_attribute_payload_encode(attrs,
1364 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
1365 SILC_ATTRIBUTE_FLAG_VALID,
1366 &userpk, sizeof(userpk));
1367 silc_free(userpk.data);
1368 silc_pkcs_public_key_free(public_key);
1369 r->pubkey_search = TRUE;
1370 } else {
1371 /* Get all attributes */
1372 attrs = silc_client_attributes_request(0);
1373 }
1374
1375 /* Resolve */
1376 silc_client_get_clients_whois(client, conn, name, NULL, attrs,
1377 silcgaim_add_buddy_resolved, r);
1378 silc_buffer_free(attrs);
1379 }
1380
1381 void silcgaim_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
1382 {
1383 silcgaim_add_buddy_i(gc, buddy, FALSE);
1384 }
1385
1386 void silcgaim_send_buddylist(GaimConnection *gc)
1387 {
1388 GaimBuddyList *blist;
1389 GaimBlistNode *gnode, *cnode, *bnode;
1390 GaimBuddy *buddy;
1391 GaimAccount *account;
1392
1393 account = gaim_connection_get_account(gc);
1394
1395 if ((blist = gaim_get_blist()) != NULL)
1396 {
1397 for (gnode = blist->root; gnode != NULL; gnode = gnode->next)
1398 {
1399 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
1400 continue;
1401 for (cnode = gnode->child; cnode != NULL; cnode = cnode->next)
1402 {
1403 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
1404 continue;
1405 for (bnode = cnode->child; bnode != NULL; bnode = bnode->next)
1406 {
1407 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
1408 continue;
1409 buddy = (GaimBuddy *)bnode;
1410 if (gaim_buddy_get_account(buddy) == account)
1411 silcgaim_add_buddy_i(gc, buddy, TRUE);
1412 }
1413 }
1414 }
1415 }
1416 }
1417
1418 void silcgaim_remove_buddy(GaimConnection *gc, GaimBuddy *buddy,
1419 GaimGroup *group)
1420 {
1421 silc_free(buddy->proto_data);
1422 }
1423
1424 void silcgaim_idle_set(GaimConnection *gc, int idle)
1425
1426 {
1427 SilcGaim sg = gc->proto_data;
1428 SilcClient client = sg->client;
1429 SilcClientConnection conn = sg->conn;
1430 SilcAttributeObjService service;
1431 const char *server;
1432 int port;
1433
1434 server = gaim_account_get_string(sg->account, "server",
1435 "silc.silcnet.org");
1436 port = gaim_account_get_int(sg->account, "port", 706),
1437
1438 memset(&service, 0, sizeof(service));
1439 silc_client_attribute_del(client, conn,
1440 SILC_ATTRIBUTE_SERVICE, NULL);
1441 service.port = port;
1442 g_snprintf(service.address, sizeof(service.address), "%s", server);
1443 service.idle = idle;
1444 silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
1445 &service, sizeof(service));
1446 }
1447
1448 char *silcgaim_status_text(GaimBuddy *b)
1449 {
1450 SilcGaim sg = b->account->gc->proto_data;
1451 SilcClient client = sg->client;
1452 SilcClientConnection conn = sg->conn;
1453 SilcClientID *client_id = b->proto_data;
1454 SilcClientEntry client_entry;
1455 SilcAttributePayload attr;
1456 SilcAttributeMood mood = 0;
1457
1458 /* Get the client entry. */
1459 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1460 if (!client_entry)
1461 return NULL;
1462
1463 /* If user is online, we show the mood status, if available.
1464 If user is offline or away that status is indicated. */
1465
1466 if (client_entry->mode & SILC_UMODE_DETACHED)
1467 return g_strdup(_("Detached"));
1468 if (client_entry->mode & SILC_UMODE_GONE)
1469 return g_strdup(_("Away"));
1470 if (client_entry->mode & SILC_UMODE_INDISPOSED)
1471 return g_strdup(_("Indisposed"));
1472 if (client_entry->mode & SILC_UMODE_BUSY)
1473 return g_strdup(_("Busy"));
1474 if (client_entry->mode & SILC_UMODE_PAGE)
1475 return g_strdup(_("Wake Me Up"));
1476 if (client_entry->mode & SILC_UMODE_HYPER)
1477 return g_strdup(_("Hyper Active"));
1478 if (client_entry->mode & SILC_UMODE_ROBOT)
1479 return g_strdup(_("Robot"));
1480
1481 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
1482 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
1483 /* The mood is a bit mask, so we could show multiple moods,
1484 but let's show only one for now. */
1485 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
1486 return g_strdup(_("Happy"));
1487 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
1488 return g_strdup(_("Sad"));
1489 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
1490 return g_strdup(_("Angry"));
1491 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
1492 return g_strdup(_("Jealous"));
1493 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
1494 return g_strdup(_("Ashamed"));
1495 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
1496 return g_strdup(_("Invincible"));
1497 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
1498 return g_strdup(_("In Love"));
1499 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
1500 return g_strdup(_("Sleepy"));
1501 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
1502 return g_strdup(_("Bored"));
1503 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
1504 return g_strdup(_("Excited"));
1505 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
1506 return g_strdup(_("Anxious"));
1507 }
1508
1509 return NULL;
1510 }
1511
1512 void silcgaim_tooltip_text(GaimBuddy *b, GaimNotifyUserInfo *user_info, gboolean full)
1513 {
1514 SilcGaim sg = b->account->gc->proto_data;
1515 SilcClient client = sg->client;
1516 SilcClientConnection conn = sg->conn;
1517 SilcClientID *client_id = b->proto_data;
1518 SilcClientEntry client_entry;
1519 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1520 char tmp[256];
1521
1522 /* Get the client entry. */
1523 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1524 if (!client_entry)
1525 return;
1526
1527 if (client_entry->nickname)
1528 gaim_notify_user_info_add_pair(user_info, _("Nickname"),
1529 client_entry->nickname);
1530 if (client_entry->username && client_entry->hostname) {
1531 g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname);
1532 gaim_notify_user_info_add_pair(user_info, _("Username"), tmp);
1533 }
1534 if (client_entry->mode) {
1535 memset(tmp, 0, sizeof(tmp));
1536 silcgaim_get_umode_string(client_entry->mode,
1537 tmp, sizeof(tmp) - strlen(tmp));
1538 gaim_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1539 }
1540
1541 silcgaim_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1542
1543 if (statusstr) {
1544 gaim_notify_user_info_add_pair(user_info, _("Message"), statusstr);
1545 g_free(statusstr);
1546 }
1547
1548 if (full) {
1549 if (moodstr) {
1550 gaim_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1551 g_free(moodstr);
1552 }
1553
1554 if (contactstr) {
1555 gaim_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1556 g_free(contactstr);
1557 }
1558
1559 if (langstr) {
1560 gaim_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1561 g_free(langstr);
1562 }
1563
1564 if (devicestr) {
1565 gaim_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1566 g_free(devicestr);
1567 }
1568
1569 if (tzstr) {
1570 gaim_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1571 g_free(tzstr);
1572 }
1573
1574 if (geostr) {
1575 gaim_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1576 g_free(geostr);
1577 }
1578 }
1579 }
1580
1581 static void
1582 silcgaim_buddy_kill(GaimBlistNode *node, gpointer data)
1583 {
1584 GaimBuddy *b;
1585 GaimConnection *gc;
1586 SilcGaim sg;
1587
1588 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node));
1589
1590 b = (GaimBuddy *) node;
1591 gc = gaim_account_get_connection(b->account);
1592 sg = gc->proto_data;
1593
1594 /* Call KILL */
1595 silc_client_command_call(sg->client, sg->conn, NULL, "KILL",
1596 b->name, "Killed by operator", NULL);
1597 }
1598
1599 typedef struct {
1600 SilcGaim sg;
1601 SilcClientEntry client_entry;
1602 } *SilcGaimBuddyWb;
1603
1604 static void
1605 silcgaim_buddy_wb(GaimBlistNode *node, gpointer data)
1606 {
1607 SilcGaimBuddyWb wb = data;
1608 silcgaim_wb_init(wb->sg, wb->client_entry);
1609 silc_free(wb);
1610 }
1611
1612 GList *silcgaim_buddy_menu(GaimBuddy *buddy)
1613 {
1614 GaimConnection *gc = gaim_account_get_connection(buddy->account);
1615 SilcGaim sg = gc->proto_data;
1616 SilcClientConnection conn = sg->conn;
1617 const char *pkfile = NULL;
1618 SilcClientEntry client_entry = NULL;
1619 GaimMenuAction *act;
1620 GList *m = NULL;
1621 SilcGaimBuddyWb wb;
1622
1623 pkfile = gaim_blist_node_get_string((GaimBlistNode *) buddy, "public-key");
1624 client_entry = silc_client_get_client_by_id(sg->client,
1625 sg->conn,
1626 buddy->proto_data);
1627
1628 if (client_entry && client_entry->send_key) {
1629 act = gaim_menu_action_new(_("Reset IM Key"),
1630 GAIM_CALLBACK(silcgaim_buddy_resetkey),
1631 NULL, NULL);
1632 m = g_list_append(m, act);
1633
1634 } else {
1635 act = gaim_menu_action_new(_("IM with Key Exchange"),
1636 GAIM_CALLBACK(silcgaim_buddy_keyagr),
1637 NULL, NULL);
1638 m = g_list_append(m, act);
1639
1640 act = gaim_menu_action_new(_("IM with Password"),
1641 GAIM_CALLBACK(silcgaim_buddy_privkey_menu),
1642 NULL, NULL);
1643 m = g_list_append(m, act);
1644 }
1645
1646 if (pkfile) {
1647 act = gaim_menu_action_new(_("Show Public Key"),
1648 GAIM_CALLBACK(silcgaim_buddy_showkey),
1649 NULL, NULL);
1650 m = g_list_append(m, act);
1651
1652 } else {
1653 act = gaim_menu_action_new(_("Get Public Key..."),
1654 GAIM_CALLBACK(silcgaim_buddy_getkey_menu),
1655 NULL, NULL);
1656 m = g_list_append(m, act);
1657 }
1658
1659 if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
1660 act = gaim_menu_action_new(_("Kill User"),
1661 GAIM_CALLBACK(silcgaim_buddy_kill),
1662 NULL, NULL);
1663 m = g_list_append(m, act);
1664 }
1665
1666 if (client_entry) {
1667 wb = silc_calloc(1, sizeof(*wb));
1668 wb->sg = sg;
1669 wb->client_entry = client_entry;
1670 act = gaim_menu_action_new(_("Draw On Whiteboard"),
1671 GAIM_CALLBACK(silcgaim_buddy_wb),
1672 (void *)wb, NULL);
1673 m = g_list_append(m, act);
1674 }
1675 return m;
1676 }
1677
1678 #ifdef SILC_ATTRIBUTE_USER_ICON
1679 void silcgaim_buddy_set_icon(GaimConnection *gc, const char *iconfile)
1680 {
1681 SilcGaim sg = gc->proto_data;
1682 SilcClient client = sg->client;
1683 SilcClientConnection conn = sg->conn;
1684 SilcMime mime;
1685 GaimBuddyIcon ic;
1686 char type[32];
1687 unsigned char *icon;
1688 const char *t;
1689 struct stat st;
1690 FILE *fp;
1691 SilcAttributeObjMime obj;
1692
1693 /* Remove */
1694 if (!iconfile) {
1695 silc_client_attribute_del(client, conn,
1696 SILC_ATTRIBUTE_USER_ICON, NULL);
1697 return;
1698 }
1699
1700 /* Add */
1701 if (g_stat(iconfile, &st) < 0)
1702 return;
1703 fp = g_fopen(iconfile, "rb");
1704 if (!fp)
1705 return;
1706 ic.data = g_malloc(st.st_size);
1707 if (!ic.data)
1708 return;
1709 ic.len = fread(ic.data, 1, st.st_size, fp);
1710 fclose(fp);
1711
1712 mime = silc_mime_alloc();
1713 if (!mime) {
1714 g_free(ic.data);
1715 return;
1716 }
1717
1718 t = gaim_buddy_icon_get_type((const GaimBuddyIcon *)&ic);
1719 if (!t) {
1720 g_free(ic.data);
1721 silc_mime_free(mime);
1722 return;
1723 }
1724 if (!strcmp(t, "jpg"))
1725 t = "jpeg";
1726 g_snprintf(type, sizeof(type), "image/%s", t);
1727 silc_mime_add_field(mime, "Content-Type", type);
1728 silc_mime_add_data(mime, ic.data, ic.len);
1729
1730 obj.mime = icon = silc_mime_encode(mime, &obj.mime_len);
1731 if (obj.mime)
1732 silc_client_attribute_add(client, conn,
1733 SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj));
1734
1735 silc_free(icon);
1736 g_free(ic.data);
1737 silc_mime_free(mime);
1738 }
1739 #endif