comparison src/protocols/silc/buddy.c @ 8849:50d0f76639e7

[gaim-migrate @ 9616] Let there be SILC. committer: Tailor Script <tailor@pidgin.im>
author Ethan Blanton <elb@pidgin.im>
date Sat, 01 May 2004 19:34:44 +0000
parents
children 4f7c365c5c5a
comparison
equal deleted inserted replaced
8848:56e6b9bdcdbc 8849:50d0f76639e7
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
24 /***************************** Key Agreement *********************************/
25
26 static void
27 silcgaim_buddy_keyagr(GaimConnection *gc, const char *name);
28
29 typedef struct {
30 char *nick;
31 GaimConnection *gc;
32 } *SilcGaimResolve;
33
34 static void
35 silcgaim_buddy_keyagr_resolved(SilcClient client,
36 SilcClientConnection conn,
37 SilcClientEntry *clients,
38 SilcUInt32 clients_count,
39 void *context)
40 {
41 GaimConnection *gc = client->application;
42 SilcGaimResolve r = context;
43 char tmp[256];
44
45 if (!clients) {
46 g_snprintf(tmp, sizeof(tmp),
47 _("User %s is not present in the network"), r->nick);
48 gaim_notify_error(gc, _("Key Agreement"),
49 _("Cannot perform the key agreement"), tmp);
50 silc_free(r->nick);
51 silc_free(r);
52 return;
53 }
54
55 silcgaim_buddy_keyagr(gc, r->nick);
56 silc_free(r->nick);
57 silc_free(r);
58 }
59
60 typedef struct {
61 gboolean responder;
62 } *SilcGaimKeyAgr;
63
64 static void
65 silcgaim_buddy_keyagr_cb(SilcClient client,
66 SilcClientConnection conn,
67 SilcClientEntry client_entry,
68 SilcKeyAgreementStatus status,
69 SilcSKEKeyMaterial *key,
70 void *context)
71 {
72 GaimConnection *gc = client->application;
73 SilcGaim sg = gc->proto_data;
74 SilcGaimKeyAgr a = context;
75
76 if (!sg->conn)
77 return;
78
79 switch (status) {
80 case SILC_KEY_AGREEMENT_OK:
81 {
82 GaimConversation *convo;
83 char tmp[128];
84
85 /* Set the private key for this client */
86 silc_client_del_private_message_key(client, conn, client_entry);
87 silc_client_add_private_message_key_ske(client, conn, client_entry,
88 NULL, NULL, key, a->responder);
89 silc_ske_free_key_material(key);
90
91 /* Open IM window */
92 convo = gaim_find_conversation_with_account(client_entry->nickname,
93 sg->account);
94 if (convo)
95 gaim_conv_window_show(gaim_conversation_get_window(convo));
96 else
97 convo = gaim_conversation_new(GAIM_CONV_IM, sg->account,
98 client_entry->nickname);
99 g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname);
100 gaim_conversation_set_title(convo, tmp);
101 }
102 break;
103
104 case SILC_KEY_AGREEMENT_ERROR:
105 gaim_notify_error(gc, _("Key Agreement"),
106 _("Error occurred during key agreement"), NULL);
107 break;
108
109 case SILC_KEY_AGREEMENT_FAILURE:
110 gaim_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL);
111 break;
112
113 case SILC_KEY_AGREEMENT_TIMEOUT:
114 gaim_notify_error(gc, _("Key Agreement"),
115 _("Timeout during key agreement"), NULL);
116 break;
117
118 case SILC_KEY_AGREEMENT_ABORTED:
119 gaim_notify_error(gc, _("Key Agreement"),
120 _("Key agreement was aborted"), NULL);
121 break;
122
123 case SILC_KEY_AGREEMENT_ALREADY_STARTED:
124 gaim_notify_error(gc, _("Key Agreement"),
125 _("Key agreement is already started"), NULL);
126 break;
127
128 case SILC_KEY_AGREEMENT_SELF_DENIED:
129 gaim_notify_error(gc, _("Key Agreement"),
130 _("Key agreement cannot be started with yourself"),
131 NULL);
132 break;
133
134 default:
135 break;
136 }
137
138 silc_free(a);
139 }
140
141 static void
142 silcgaim_buddy_keyagr_do(GaimConnection *gc, const char *name,
143 gboolean force_local)
144 {
145 SilcGaim sg = gc->proto_data;
146 SilcClientEntry *clients;
147 SilcUInt32 clients_count;
148 char *local_ip = NULL, *remote_ip = NULL;;
149 gboolean local = TRUE;
150 char *nickname;
151 SilcGaimKeyAgr a;
152
153 if (!sg->conn || !name)
154 return;
155
156 if (!silc_parse_userfqdn(name, &nickname, NULL))
157 return;
158
159 /* Find client entry */
160 clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name,
161 &clients_count);
162 if (!clients) {
163 /* Resolve unknown user */
164 SilcGaimResolve r = silc_calloc(1, sizeof(*r));
165 if (!r)
166 return;
167 r->nick = g_strdup(name);
168 r->gc = gc;
169 silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
170 silcgaim_buddy_keyagr_resolved, r);
171 silc_free(nickname);
172 return;
173 }
174
175 /* Resolve the local IP from the outgoing socket connection. We resolve
176 it to check whether we have a private range IP address or public IP
177 address. If we have public then we will assume that we are not behind
178 NAT and will provide automatically the point of connection to the
179 agreement. If we have private range address we assume that we are
180 behind NAT and we let the responder provide the point of connection.
181
182 The algorithm also checks the remote IP address of server connection.
183 If it is private range address and we have private range address we
184 assume that we are chatting in LAN and will provide the point of
185 connection.
186
187 Naturally this algorithm does not always get things right. */
188
189 if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) {
190 /* Check if the IP is private */
191 if (!force_local && silcgaim_ip_is_private(local_ip)) {
192 local = FALSE;
193
194 /* Local IP is private, resolve the remote server IP to see whether
195 we are talking to Internet or just on LAN. */
196 if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL,
197 &remote_ip))
198 if (silcgaim_ip_is_private(remote_ip))
199 /* We assume we are in LAN. Let's provide
200 the connection point. */
201 local = TRUE;
202 }
203 }
204
205 if (force_local)
206 local = TRUE;
207
208 if (local && !local_ip)
209 local_ip = silc_net_localip();
210
211 a = silc_calloc(1, sizeof(*a));
212 if (!a)
213 return;
214 a->responder = local;
215
216 /* Send the key agreement request */
217 silc_client_send_key_agreement(sg->client, sg->conn, clients[0],
218 local ? local_ip : NULL, NULL, 0, 60,
219 silcgaim_buddy_keyagr_cb, a);
220
221 silc_free(local_ip);
222 silc_free(remote_ip);
223 silc_free(clients);
224 }
225
226 typedef struct {
227 SilcClient client;
228 SilcClientConnection conn;
229 SilcClientID client_id;
230 char *hostname;
231 SilcUInt16 port;
232 } *SilcGaimKeyAgrAsk;
233
234 static void
235 silcgaim_buddy_keyagr_request_cb(SilcGaimKeyAgrAsk a, gint id)
236 {
237 SilcGaimKeyAgr ai;
238 SilcClientEntry client_entry;
239
240 if (id != 1)
241 goto out;
242
243 /* Get the client entry. */
244 client_entry = silc_client_get_client_by_id(a->client, a->conn,
245 &a->client_id);
246 if (!client_entry) {
247 gaim_notify_error(a->client->application, _("Key Agreement"),
248 _("The remote user is not present in the network any more"),
249 NULL);
250 goto out;
251 }
252
253 /* If the hostname was provided by the requestor perform the key agreement
254 now. Otherwise, we will send him a request to connect to us. */
255 if (a->hostname) {
256 ai = silc_calloc(1, sizeof(*ai));
257 if (!ai)
258 goto out;
259 ai->responder = FALSE;
260 silc_client_perform_key_agreement(a->client, a->conn, client_entry,
261 a->hostname, a->port,
262 silcgaim_buddy_keyagr_cb, ai);
263 } else {
264 /* Send request. Force us as the point of connection since requestor
265 did not provide the point of connection. */
266 silcgaim_buddy_keyagr_do(a->client->application,
267 client_entry->nickname, TRUE);
268 }
269
270 out:
271 silc_free(a->hostname);
272 silc_free(a);
273 }
274
275 void silcgaim_buddy_keyagr_request(SilcClient client,
276 SilcClientConnection conn,
277 SilcClientEntry client_entry,
278 const char *hostname, SilcUInt16 port)
279 {
280 char tmp[128], tmp2[128];
281 SilcGaimKeyAgrAsk a;
282
283 g_snprintf(tmp, sizeof(tmp),
284 _("Key agreement request received from %s. Would you like to "
285 "perform the key agreement?"), client_entry->nickname);
286 if (hostname)
287 g_snprintf(tmp2, sizeof(tmp2),
288 _("The remote user is waiting key agreement on:\n"
289 "Remote host: %s\nRemote port: %d"), hostname, port);
290
291 a = silc_calloc(1, sizeof(*a));
292 if (!a)
293 return;
294 a->client = client;
295 a->conn = conn;
296 a->client_id = *client_entry->id;
297 if (hostname)
298 a->hostname = strdup(hostname);
299 a->port = port;
300
301 gaim_request_action(NULL, _("Key Agreement Request"), tmp,
302 hostname ? tmp2 : NULL, 1, a, 2,
303 _("Yes"), G_CALLBACK(silcgaim_buddy_keyagr_request_cb),
304 _("No"), G_CALLBACK(silcgaim_buddy_keyagr_request_cb));
305 }
306
307 static void
308 silcgaim_buddy_keyagr(GaimConnection *gc, const char *name)
309 {
310 silcgaim_buddy_keyagr_do(gc, name, FALSE);
311 }
312
313
314 /**************************** Static IM Key **********************************/
315
316 static void
317 silcgaim_buddy_resetkey(GaimConnection *gc, const char *name)
318 {
319 SilcGaim sg = gc->proto_data;
320 char *nickname;
321 SilcClientEntry *clients;
322 SilcUInt32 clients_count;
323
324 if (!name)
325 return;
326 if (!silc_parse_userfqdn(name, &nickname, NULL))
327 return;
328
329 /* Find client entry */
330 clients = silc_client_get_clients_local(sg->client, sg->conn,
331 nickname, name,
332 &clients_count);
333 if (!clients) {
334 silc_free(nickname);
335 return;
336 }
337
338 clients[0]->prv_resp = FALSE;
339 silc_client_del_private_message_key(sg->client, sg->conn,
340 clients[0]);
341 silc_free(clients);
342 silc_free(nickname);
343 }
344
345 typedef struct {
346 SilcClient client;
347 SilcClientConnection conn;
348 SilcClientID client_id;
349 } *SilcGaimPrivkey;
350
351 static void
352 silcgaim_buddy_privkey(GaimConnection *gc, const char *name);
353
354 static void
355 silcgaim_buddy_privkey_cb(SilcGaimPrivkey p, const char *passphrase)
356 {
357 SilcClientEntry client_entry;
358
359 if (!passphrase || !(*passphrase)) {
360 silc_free(p);
361 return;
362 }
363
364 /* Get the client entry. */
365 client_entry = silc_client_get_client_by_id(p->client, p->conn,
366 &p->client_id);
367 if (!client_entry) {
368 gaim_notify_error(p->client->application, _("IM With Password"),
369 _("The remote user is not present in the network any more"),
370 NULL);
371 silc_free(p);
372 return;
373 }
374
375 /* Set the private message key */
376 silc_client_del_private_message_key(p->client, p->conn,
377 client_entry);
378 silc_client_add_private_message_key(p->client, p->conn,
379 client_entry, NULL, NULL,
380 (unsigned char *)passphrase,
381 strlen(passphrase), FALSE,
382 client_entry->prv_resp);
383 if (!client_entry->prv_resp)
384 silc_client_send_private_message_key_request(p->client,
385 p->conn,
386 client_entry);
387 silc_free(p);
388 }
389
390 static void
391 silcgaim_buddy_privkey_resolved(SilcClient client,
392 SilcClientConnection conn,
393 SilcClientEntry *clients,
394 SilcUInt32 clients_count,
395 void *context)
396 {
397 char tmp[256];
398
399 if (!clients) {
400 g_snprintf(tmp, sizeof(tmp),
401 _("User %s is not present in the network"),
402 (const char *)context);
403 gaim_notify_error(client->application, _("IM With Password"),
404 _("Cannot set IM key"), tmp);
405 g_free(context);
406 return;
407 }
408
409 silcgaim_buddy_privkey(client->application, context);
410 silc_free(context);
411 }
412
413 static void
414 silcgaim_buddy_privkey(GaimConnection *gc, const char *name)
415 {
416 SilcGaim sg = gc->proto_data;
417 char *nickname;
418 SilcGaimPrivkey p;
419 SilcClientEntry *clients;
420 SilcUInt32 clients_count;
421
422 if (!name)
423 return;
424 if (!silc_parse_userfqdn(name, &nickname, NULL))
425 return;
426
427 /* Find client entry */
428 clients = silc_client_get_clients_local(sg->client, sg->conn,
429 nickname, name,
430 &clients_count);
431 if (!clients) {
432 silc_client_get_clients(sg->client, sg->conn, nickname, NULL,
433 silcgaim_buddy_privkey_resolved,
434 g_strdup(name));
435 silc_free(nickname);
436 return;
437 }
438
439 p = silc_calloc(1, sizeof(*p));
440 if (!p)
441 return;
442 p->client = sg->client;
443 p->conn = sg->conn;
444 p->client_id = *clients[0]->id;
445 gaim_request_input(NULL, _("IM With Password"), NULL,
446 _("Set IM Password"), NULL, FALSE, TRUE, NULL,
447 _("OK"), G_CALLBACK(silcgaim_buddy_privkey_cb),
448 _("Cancel"), G_CALLBACK(silcgaim_buddy_privkey_cb),
449 p);
450
451 silc_free(clients);
452 silc_free(nickname);
453 }
454
455
456 /**************************** Get Public Key *********************************/
457
458 typedef struct {
459 SilcClient client;
460 SilcClientConnection conn;
461 SilcClientID client_id;
462 } *SilcGaimBuddyGetkey;
463
464 static void
465 silcgaim_buddy_getkey(GaimConnection *gc, const char *name);
466
467 static void
468 silcgaim_buddy_getkey_cb(SilcGaimBuddyGetkey g,
469 SilcClientCommandReplyContext cmd)
470 {
471 SilcClientEntry client_entry;
472 unsigned char *pk;
473 SilcUInt32 pk_len;
474
475 /* Get the client entry. */
476 client_entry = silc_client_get_client_by_id(g->client, g->conn,
477 &g->client_id);
478 if (!client_entry) {
479 gaim_notify_error(g->client->application, _("Get Public Key"),
480 _("The remote user is not present in the network any more"),
481 NULL);
482 silc_free(g);
483 return;
484 }
485
486 if (!client_entry->public_key) {
487 silc_free(g);
488 return;
489 }
490
491 /* Now verify the public key */
492 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
493 silcgaim_verify_public_key(g->client, g->conn, client_entry->nickname,
494 SILC_SOCKET_TYPE_CLIENT,
495 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
496 NULL, NULL);
497 silc_free(pk);
498 silc_free(g);
499 }
500
501 static void
502 silcgaim_buddy_getkey_resolved(SilcClient client,
503 SilcClientConnection conn,
504 SilcClientEntry *clients,
505 SilcUInt32 clients_count,
506 void *context)
507 {
508 char tmp[256];
509
510 if (!clients) {
511 g_snprintf(tmp, sizeof(tmp),
512 _("User %s is not present in the network"),
513 (const char *)context);
514 gaim_notify_error(client->application, _("Get Public Key"),
515 _("Cannot fetch the public key"), tmp);
516 g_free(context);
517 return;
518 }
519
520 silcgaim_buddy_getkey(client->application, context);
521 silc_free(context);
522 }
523
524 static void
525 silcgaim_buddy_getkey(GaimConnection *gc, const char *name)
526 {
527 SilcGaim sg = gc->proto_data;
528 SilcClient client = sg->client;
529 SilcClientConnection conn = sg->conn;
530 SilcClientEntry *clients;
531 SilcUInt32 clients_count;
532 SilcGaimBuddyGetkey g;
533 char *nickname;
534
535 if (!name)
536 return;
537
538 if (!silc_parse_userfqdn(name, &nickname, NULL))
539 return;
540
541 /* Find client entry */
542 clients = silc_client_get_clients_local(client, conn, nickname, name,
543 &clients_count);
544 if (!clients) {
545 silc_client_get_clients(client, conn, nickname, NULL,
546 silcgaim_buddy_getkey_resolved,
547 g_strdup(name));
548 silc_free(nickname);
549 return;
550 }
551
552 /* Call GETKEY */
553 g = silc_calloc(1, sizeof(*g));
554 if (!g)
555 return;
556 g->client = client;
557 g->conn = conn;
558 g->client_id = *clients[0]->id;
559 silc_client_command_call(client, conn, NULL, "GETKEY",
560 clients[0]->nickname, NULL);
561 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
562 conn->cmd_ident,
563 (SilcCommandCb)silcgaim_buddy_getkey_cb, g);
564 silc_free(clients);
565 silc_free(nickname);
566 }
567
568 static void
569 silcgaim_buddy_showkey(GaimConnection *gc, const char *name)
570 {
571 SilcGaim sg = gc->proto_data;
572 SilcPublicKey public_key;
573 const char *pkfile;
574 GaimBuddy *b;
575
576 b = gaim_find_buddy(gc->account, name);
577 if (!b)
578 return;
579
580 pkfile = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
581 if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) &&
582 !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) {
583 gaim_notify_error(gc,
584 _("Show Public Key"),
585 _("Could not load public key"), NULL);
586 return;
587 }
588
589 silcgaim_show_public_key(sg, name, public_key, NULL, NULL);
590 silc_pkcs_public_key_free(public_key);
591 }
592
593
594 /**************************** Buddy routines *********************************/
595
596 /* The buddies are implemented by using the WHOIS and WATCH commands that
597 can be used to search users by their public key. Since nicknames aren't
598 unique in SILC we cannot trust the buddy list using their nickname. We
599 associate public keys to buddies and use those to search and watch
600 in the network.
601
602 The problem is that Gaim does not return GaimBuddy contexts to the
603 callbacks but the buddy names. Naturally, this is not going to work
604 with SILC. But, for now, we have to do what we can... */
605
606 typedef struct {
607 SilcClient client;
608 SilcClientConnection conn;
609 SilcClientID client_id;
610 GaimBuddy *b;
611 unsigned char *offline_pk;
612 SilcUInt32 offline_pk_len;
613 unsigned int offline : 1;
614 unsigned int pubkey_search : 1;
615 unsigned int init : 1;
616 } *SilcGaimBuddyRes;
617
618 static void
619 silcgaim_add_buddy_ask_pk_cb(SilcGaimBuddyRes r, gint id);
620 static void
621 silcgaim_add_buddy_resolved(SilcClient client,
622 SilcClientConnection conn,
623 SilcClientEntry *clients,
624 SilcUInt32 clients_count,
625 void *context);
626
627 void silcgaim_get_info(GaimConnection *gc, const char *who)
628 {
629 SilcGaim sg = gc->proto_data;
630 SilcClient client = sg->client;
631 SilcClientConnection conn = sg->conn;
632 SilcClientEntry client_entry;
633 GaimBuddy *b;
634 const char *filename, *nick = who;
635 char tmp[256];
636
637 if (!who)
638 return;
639 if (strlen(who) > 1 && who[0] == '@')
640 nick = who + 1;
641 if (strlen(who) > 1 && who[0] == '*')
642 nick = who + 1;
643 if (strlen(who) > 2 && who[0] == '*' && who[1] == '@')
644 nick = who + 2;
645
646 b = gaim_find_buddy(gc->account, nick);
647 if (b) {
648 /* See if we have this buddy's public key. If we do use that
649 to search the details. */
650 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
651 if (filename) {
652 /* Call WHOIS. The user info is displayed in the WHOIS
653 command reply. */
654 silc_client_command_call(client, conn, NULL, "WHOIS",
655 "-details", "-pubkey", filename, NULL);
656 return;
657 }
658
659 if (!b->proto_data) {
660 g_snprintf(tmp, sizeof(tmp),
661 _("User %s is not present in the network"), b->name);
662 gaim_notify_error(gc, _("User Information"),
663 _("Cannot get user information"), tmp);
664 return;
665 }
666
667 client_entry = silc_client_get_client_by_id(client, conn, b->proto_data);
668 if (client_entry) {
669 /* Call WHOIS. The user info is displayed in the WHOIS
670 command reply. */
671 silc_client_command_call(client, conn, NULL, "WHOIS",
672 client_entry->nickname, "-details", NULL);
673 }
674 } else {
675 /* Call WHOIS just with nickname. */
676 silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL);
677 }
678 }
679
680 static void
681 silcgaim_add_buddy_pk_no(SilcGaimBuddyRes r)
682 {
683 char tmp[512];
684 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"),
685 r->b->name);
686 gaim_notify_error(r->client->application, _("Add Buddy"), tmp,
687 _("You cannot receive buddy notifications untill you "
688 "import its public key. You can use the Get Public Key "
689 "command to get the public key."));
690 gaim_blist_update_buddy_presence(r->b, GAIM_BUDDY_OFFLINE);
691 }
692
693 static void
694 silcgaim_add_buddy_save(bool success, void *context)
695 {
696 SilcGaimBuddyRes r = context;
697 GaimBuddy *b = r->b;
698 SilcClient client = r->client;
699 SilcClientEntry client_entry;
700 SilcAttributePayload attr;
701 SilcAttribute attribute;
702 SilcVCardStruct vcard;
703 SilcAttributeObjMime message, extension;
704 SilcAttributeObjPk serverpk, usersign, serversign;
705 gboolean usign_success = TRUE, ssign_success = TRUE;
706 unsigned char filename[256], filename2[256], *fingerprint = NULL, *tmp;
707 SilcUInt32 len;
708 int i;
709
710 if (!success) {
711 /* The user did not trust the public key. */
712 silcgaim_add_buddy_pk_no(r);
713 silc_free(r);
714 return;
715 }
716
717 if (r->offline) {
718 /* User is offline. Associate the imported public key with
719 this user. */
720 fingerprint = silc_hash_fingerprint(NULL, r->offline_pk,
721 r->offline_pk_len);
722 for (i = 0; i < strlen(fingerprint); i++)
723 if (fingerprint[i] == ' ')
724 fingerprint[i] = '_';
725 g_snprintf(filename, sizeof(filename) - 1,
726 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
727 silcgaim_silcdir(), fingerprint);
728 gaim_blist_node_set_string((GaimBlistNode *)b, "public-key", filename);
729 gaim_blist_save();
730 gaim_blist_update_buddy_presence(r->b, GAIM_BUDDY_OFFLINE);
731 silc_free(fingerprint);
732 silc_free(r->offline_pk);
733 silc_free(r);
734 return;
735 }
736
737 /* Get the client entry. */
738 client_entry = silc_client_get_client_by_id(r->client, r->conn,
739 &r->client_id);
740 if (!client_entry) {
741 silc_free(r);
742 return;
743 }
744
745 memset(&vcard, 0, sizeof(vcard));
746 memset(&message, 0, sizeof(message));
747 memset(&extension, 0, sizeof(extension));
748 memset(&serverpk, 0, sizeof(serverpk));
749 memset(&usersign, 0, sizeof(usersign));
750 memset(&serversign, 0, sizeof(serversign));
751
752 /* Now that we have the public key and we trust it now we
753 save the attributes of the buddy and update its status. */
754
755 silc_dlist_start(client_entry->attrs);
756 while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
757 attribute = silc_attribute_get_attribute(attr);
758
759 switch (attribute) {
760
761 case SILC_ATTRIBUTE_USER_INFO:
762 if (!silc_attribute_get_object(attr, (void *)&vcard,
763 sizeof(vcard)))
764 continue;
765 break;
766
767 case SILC_ATTRIBUTE_STATUS_MESSAGE:
768 if (!silc_attribute_get_object(attr, (void *)&message,
769 sizeof(message)))
770 continue;
771 break;
772
773 case SILC_ATTRIBUTE_EXTENSION:
774 if (!silc_attribute_get_object(attr, (void *)&extension,
775 sizeof(extension)))
776 continue;
777 break;
778
779 case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY:
780 if (serverpk.type)
781 continue;
782 if (!silc_attribute_get_object(attr, (void *)&serverpk,
783 sizeof(serverpk)))
784 continue;
785 break;
786
787 case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE:
788 if (usersign.data)
789 continue;
790 if (!silc_attribute_get_object(attr, (void *)&usersign,
791 sizeof(usersign)))
792 continue;
793 break;
794
795 case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE:
796 if (serversign.data)
797 continue;
798 if (!silc_attribute_get_object(attr, (void *)&serversign,
799 sizeof(serversign)))
800 continue;
801 break;
802
803 default:
804 break;
805 }
806 }
807
808 /* Verify the attribute signatures */
809
810 if (usersign.data) {
811 SilcPKCS pkcs;
812 unsigned char *verifyd;
813 SilcUInt32 verify_len;
814
815 silc_pkcs_alloc("rsa", &pkcs);
816 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
817 FALSE, &verify_len);
818 if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){
819 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
820 usersign.data,
821 usersign.data_len,
822 verifyd, verify_len))
823 usign_success = FALSE;
824 }
825 silc_free(verifyd);
826 }
827
828 if (serversign.data && !strcmp(serverpk.type, "silc-rsa")) {
829 SilcPublicKey public_key;
830 SilcPKCS pkcs;
831 unsigned char *verifyd;
832 SilcUInt32 verify_len;
833
834 if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len,
835 &public_key)) {
836 silc_pkcs_alloc("rsa", &pkcs);
837 verifyd = silc_attribute_get_verify_data(client_entry->attrs,
838 TRUE, &verify_len);
839 if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) {
840 if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash,
841 serversign.data,
842 serversign.data_len,
843 verifyd, verify_len))
844 ssign_success = FALSE;
845 }
846 silc_pkcs_public_key_free(public_key);
847 silc_free(verifyd);
848 }
849 }
850
851 fingerprint = silc_fingerprint(client_entry->fingerprint,
852 client_entry->fingerprint_len);
853 for (i = 0; i < strlen(fingerprint); i++)
854 if (fingerprint[i] == ' ')
855 fingerprint[i] = '_';
856
857 if (usign_success || ssign_success) {
858 struct passwd *pw;
859 struct stat st;
860
861 memset(filename2, 0, sizeof(filename2));
862
863 /* Filename for dir */
864 tmp = fingerprint + strlen(fingerprint) - 9;
865 g_snprintf(filename, sizeof(filename) - 1,
866 "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s",
867 silcgaim_silcdir(), tmp);
868
869 pw = getpwuid(getuid());
870 if (!pw)
871 return;
872
873 /* Create dir if it doesn't exist */
874 if ((stat(filename, &st)) == -1) {
875 if (errno == ENOENT) {
876 if (pw->pw_uid == geteuid())
877 mkdir(filename, 0755);
878 }
879 }
880
881 /* Save VCard */
882 g_snprintf(filename2, sizeof(filename2) - 1,
883 "%s" G_DIR_SEPARATOR_S "vcard", filename);
884 if (vcard.full_name) {
885 tmp = silc_vcard_encode(&vcard, &len);
886 silc_file_writefile(filename2, tmp, len);
887 silc_free(tmp);
888 }
889
890 /* Save status message */
891 if (message.mime) {
892 memset(filename2, 0, sizeof(filename2));
893 g_snprintf(filename2, sizeof(filename2) - 1,
894 "%s" G_DIR_SEPARATOR_S "status_message.mime",
895 filename);
896 silc_file_writefile(filename2, message.mime,
897 message.mime_len);
898 }
899
900 /* Save extension data */
901 if (extension.mime) {
902 memset(filename2, 0, sizeof(filename2));
903 g_snprintf(filename2, sizeof(filename2) - 1,
904 "%s" G_DIR_SEPARATOR_S "extension.mime",
905 filename);
906 silc_file_writefile(filename2, extension.mime,
907 extension.mime_len);
908 }
909 }
910
911 /* Save the public key path to buddy properties, as it is used
912 to identify the buddy in the network (and not the nickname). */
913 memset(filename, 0, sizeof(filename));
914 g_snprintf(filename, sizeof(filename) - 1,
915 "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub",
916 silcgaim_silcdir(), fingerprint);
917 gaim_blist_node_set_string((GaimBlistNode *)b, "public-key", filename);
918 gaim_blist_save();
919
920 /* Update online status on the buddy list */
921 gaim_blist_update_buddy_presence(b, GAIM_BUDDY_ONLINE);
922
923 /* Finally, start watching this user so we receive its status
924 changes from the server */
925 g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename);
926 silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey",
927 filename2, NULL);
928
929 silc_free(fingerprint);
930 silc_free(r);
931 }
932
933 static void
934 silcgaim_add_buddy_ask_import(void *user_data, const char *name)
935 {
936 SilcGaimBuddyRes r = (SilcGaimBuddyRes)user_data;
937 SilcPublicKey public_key;
938
939 /* Load the public key */
940 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
941 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
942 silcgaim_add_buddy_ask_pk_cb(r, 0);
943 gaim_notify_error(r->client->application,
944 _("Add Buddy"), _("Could not load public key"), NULL);
945 return;
946 }
947
948 /* Now verify the public key */
949 r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len);
950 silcgaim_verify_public_key(r->client, r->conn, r->b->name,
951 SILC_SOCKET_TYPE_CLIENT,
952 r->offline_pk, r->offline_pk_len,
953 SILC_SKE_PK_TYPE_SILC,
954 silcgaim_add_buddy_save, r);
955 }
956
957 static void
958 silcgaim_add_buddy_ask_pk_cancel(void *user_data, const char *name)
959 {
960 SilcGaimBuddyRes r = (SilcGaimBuddyRes)user_data;
961
962 /* The user did not import public key. The buddy is unusable. */
963 silcgaim_add_buddy_pk_no(r);
964 silc_free(r);
965 }
966
967 static void
968 silcgaim_add_buddy_ask_pk_cb(SilcGaimBuddyRes r, gint id)
969 {
970 if (id != 0) {
971 /* The user did not import public key. The buddy is unusable. */
972 silcgaim_add_buddy_pk_no(r);
973 silc_free(r);
974 return;
975 }
976
977 /* Open file selector to select the public key. */
978 gaim_request_file(NULL, _("Open..."), NULL,
979 G_CALLBACK(silcgaim_add_buddy_ask_import),
980 G_CALLBACK(silcgaim_add_buddy_ask_pk_cancel), r);
981 }
982
983 static void
984 silcgaim_add_buddy_ask_pk(SilcGaimBuddyRes r)
985 {
986 char tmp[512];
987 g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"),
988 r->b->name);
989 gaim_request_action(NULL, _("Add Buddy"), tmp,
990 _("To add the buddy you must import its public key. "
991 "Press Import to import a public key."), 0, r, 2,
992 _("Cancel"), G_CALLBACK(silcgaim_add_buddy_ask_pk_cb),
993 _("Import..."), G_CALLBACK(silcgaim_add_buddy_ask_pk_cb));
994 }
995
996 static void
997 silcgaim_add_buddy_getkey_cb(SilcGaimBuddyRes r,
998 SilcClientCommandReplyContext cmd)
999 {
1000 SilcClientEntry client_entry;
1001 unsigned char *pk;
1002 SilcUInt32 pk_len;
1003
1004 /* Get the client entry. */
1005 client_entry = silc_client_get_client_by_id(r->client, r->conn,
1006 &r->client_id);
1007 if (!client_entry || !client_entry->public_key) {
1008 /* The buddy is offline/nonexistent. We will require user
1009 to associate a public key with the buddy or the buddy
1010 cannot be added. */
1011 r->offline = TRUE;
1012 silcgaim_add_buddy_ask_pk(r);
1013 return;
1014 }
1015
1016 /* Now verify the public key */
1017 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1018 silcgaim_verify_public_key(r->client, r->conn, client_entry->nickname,
1019 SILC_SOCKET_TYPE_CLIENT,
1020 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1021 silcgaim_add_buddy_save, r);
1022 silc_free(pk);
1023 }
1024
1025 static void
1026 silcgaim_add_buddy_select_cb(SilcGaimBuddyRes r, GaimRequestFields *fields)
1027 {
1028 GaimRequestField *f;
1029 const GList *list;
1030 SilcClientEntry client_entry;
1031
1032 f = gaim_request_fields_get_field(fields, "list");
1033 list = gaim_request_field_list_get_selected(f);
1034 if (!list) {
1035 /* The user did not select any user. */
1036 silcgaim_add_buddy_pk_no(r);
1037 silc_free(r);
1038 return;
1039 }
1040
1041 client_entry = gaim_request_field_list_get_data(f, list->data);
1042 silcgaim_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r);
1043 }
1044
1045 static void
1046 silcgaim_add_buddy_select_cancel(SilcGaimBuddyRes r, GaimRequestFields *fields)
1047 {
1048 /* The user did not select any user. */
1049 silcgaim_add_buddy_pk_no(r);
1050 silc_free(r);
1051 }
1052
1053 static void
1054 silcgaim_add_buddy_select(SilcGaimBuddyRes r,
1055 SilcClientEntry *clients,
1056 SilcUInt32 clients_count)
1057 {
1058 GaimRequestFields *fields;
1059 GaimRequestFieldGroup *g;
1060 GaimRequestField *f;
1061 char tmp[512];
1062 int i;
1063
1064 fields = gaim_request_fields_new();
1065 g = gaim_request_field_group_new(NULL);
1066 f = gaim_request_field_list_new("list", NULL);
1067 gaim_request_field_group_add_field(g, f);
1068 gaim_request_field_list_set_multi_select(f, FALSE);
1069 gaim_request_fields_add_group(fields, g);
1070
1071 for (i = 0; i < clients_count; i++) {
1072 g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)",
1073 clients[i]->realname, clients[i]->nickname,
1074 clients[i]->username, clients[i]->hostname ?
1075 clients[i]->hostname : "");
1076 gaim_request_field_list_add(f, tmp, clients[i]);
1077 }
1078
1079 g_snprintf(tmp, sizeof(tmp),
1080 _("More than one users were found with the same %s. Select "
1081 "the correct user from the list to add to the buddy list."),
1082 r->pubkey_search ? "public key" : "name");
1083 gaim_request_fields(NULL, _("Add Buddy"),
1084 _("Select correct user"), tmp, fields,
1085 "OK", G_CALLBACK(silcgaim_add_buddy_select_cb),
1086 "Cancel", G_CALLBACK(silcgaim_add_buddy_select_cancel), r);
1087 }
1088
1089 static void
1090 silcgaim_add_buddy_resolved(SilcClient client,
1091 SilcClientConnection conn,
1092 SilcClientEntry *clients,
1093 SilcUInt32 clients_count,
1094 void *context)
1095 {
1096 SilcGaimBuddyRes r = context;
1097 GaimBuddy *b = r->b;
1098 SilcAttributePayload pub;
1099 SilcAttributeObjPk userpk;
1100 unsigned char *pk;
1101 SilcUInt32 pk_len;
1102 const char *filename;
1103
1104 /* If the buddy is offline/nonexistent, we will require user
1105 to associate a public key with the buddy or the buddy
1106 cannot be added. */
1107 if (!clients_count) {
1108 if (r->init) {
1109 silc_free(r);
1110 return;
1111 }
1112
1113 r->offline = TRUE;
1114 silcgaim_add_buddy_ask_pk(r);
1115 return;
1116 }
1117
1118 /* If more than one client was found with nickname, we need to verify
1119 from user which one is the correct. */
1120 if (clients_count > 1 && !r->pubkey_search) {
1121 if (r->init) {
1122 silc_free(r);
1123 return;
1124 }
1125
1126 silcgaim_add_buddy_select(r, clients, clients_count);
1127 return;
1128 }
1129
1130 /* If we searched using public keys and more than one entry was found
1131 the same person is logged on multiple times. */
1132 if (clients_count > 1 && r->pubkey_search && b->name) {
1133 if (r->init) {
1134 /* Find the entry that closest matches to the
1135 buddy nickname. */
1136 int i;
1137 for (i = 0; i < clients_count; i++) {
1138 if (!strncasecmp(b->name, clients[i]->nickname,
1139 strlen(b->name))) {
1140 clients[0] = clients[i];
1141 break;
1142 }
1143 }
1144 } else {
1145 /* Verify from user which one is correct */
1146 silcgaim_add_buddy_select(r, clients, clients_count);
1147 return;
1148 }
1149 }
1150
1151 /* The client was found. Now get its public key and verify
1152 that before adding the buddy. */
1153 memset(&userpk, 0, sizeof(userpk));
1154 b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id));
1155 r->client_id = *clients[0]->id;
1156
1157 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
1158
1159 /* Get the public key from attributes, if not present then
1160 resolve it with GETKEY unless we have it cached already. */
1161 if (clients[0]->attrs && !clients[0]->public_key) {
1162 pub = silcgaim_get_attr(clients[0]->attrs,
1163 SILC_ATTRIBUTE_USER_PUBLIC_KEY);
1164 if (!pub || !silc_attribute_get_object(pub, (void *)&userpk,
1165 sizeof(userpk))) {
1166 /* Get public key with GETKEY */
1167 silc_client_command_call(client, conn, NULL,
1168 "GETKEY", clients[0]->nickname, NULL);
1169 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1170 conn->cmd_ident,
1171 (SilcCommandCb)silcgaim_add_buddy_getkey_cb,
1172 r);
1173 return;
1174 }
1175 if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len,
1176 &clients[0]->public_key))
1177 return;
1178 silc_free(userpk.data);
1179 } else if (filename && !clients[0]->public_key) {
1180 if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1181 SILC_PKCS_FILE_PEM) &&
1182 !silc_pkcs_load_public_key(filename, &clients[0]->public_key,
1183 SILC_PKCS_FILE_BIN)) {
1184 /* Get public key with GETKEY */
1185 silc_client_command_call(client, conn, NULL,
1186 "GETKEY", clients[0]->nickname, NULL);
1187 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1188 conn->cmd_ident,
1189 (SilcCommandCb)silcgaim_add_buddy_getkey_cb,
1190 r);
1191 return;
1192 }
1193 } else if (!clients[0]->public_key) {
1194 /* Get public key with GETKEY */
1195 silc_client_command_call(client, conn, NULL,
1196 "GETKEY", clients[0]->nickname, NULL);
1197 silc_client_command_pending(conn, SILC_COMMAND_GETKEY,
1198 conn->cmd_ident,
1199 (SilcCommandCb)silcgaim_add_buddy_getkey_cb,
1200 r);
1201 return;
1202 }
1203
1204 /* We have the public key, verify it. */
1205 pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len);
1206 silcgaim_verify_public_key(client, conn, clients[0]->nickname,
1207 SILC_SOCKET_TYPE_CLIENT,
1208 pk, pk_len, SILC_SKE_PK_TYPE_SILC,
1209 silcgaim_add_buddy_save, r);
1210 silc_free(pk);
1211 }
1212
1213 static void
1214 silcgaim_add_buddy_i(GaimConnection *gc, GaimBuddy *b, gboolean init)
1215 {
1216 SilcGaim sg = gc->proto_data;
1217 SilcClient client = sg->client;
1218 SilcClientConnection conn = sg->conn;
1219 SilcGaimBuddyRes r;
1220 SilcBuffer attrs;
1221 const char *filename, *name = b->name;
1222
1223 r = silc_calloc(1, sizeof(*r));
1224 if (!r)
1225 return;
1226 r->client = client;
1227 r->conn = conn;
1228 r->b = b;
1229 r->init = init;
1230
1231 /* See if we have this buddy's public key. If we do use that
1232 to search the details. */
1233 filename = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
1234 if (filename) {
1235 SilcPublicKey public_key;
1236 SilcAttributeObjPk userpk;
1237
1238 if (!silc_pkcs_load_public_key(filename, &public_key,
1239 SILC_PKCS_FILE_PEM) &&
1240 !silc_pkcs_load_public_key(filename, &public_key,
1241 SILC_PKCS_FILE_BIN))
1242 return;
1243
1244 /* Get all attributes, and use the public key to search user */
1245 name = NULL;
1246 attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
1247 SILC_ATTRIBUTE_SERVICE,
1248 SILC_ATTRIBUTE_STATUS_MOOD,
1249 SILC_ATTRIBUTE_STATUS_FREETEXT,
1250 SILC_ATTRIBUTE_STATUS_MESSAGE,
1251 SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
1252 SILC_ATTRIBUTE_PREFERRED_CONTACT,
1253 SILC_ATTRIBUTE_TIMEZONE,
1254 SILC_ATTRIBUTE_GEOLOCATION,
1255 SILC_ATTRIBUTE_DEVICE_INFO, 0);
1256 userpk.type = "silc-rsa";
1257 userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len);
1258 attrs = silc_attribute_payload_encode(attrs,
1259 SILC_ATTRIBUTE_USER_PUBLIC_KEY,
1260 SILC_ATTRIBUTE_FLAG_VALID,
1261 &userpk, sizeof(userpk));
1262 silc_free(userpk.data);
1263 silc_pkcs_public_key_free(public_key);
1264 r->pubkey_search = TRUE;
1265 } else {
1266 /* Get all attributes */
1267 attrs = silc_client_attributes_request(0);
1268 }
1269
1270 /* Resolve */
1271 silc_client_get_clients_whois(client, conn, name, NULL, attrs,
1272 silcgaim_add_buddy_resolved, r);
1273 silc_buffer_free(attrs);
1274 }
1275
1276 void silcgaim_add_buddy(GaimConnection *gc, const char *name, GaimGroup *grp)
1277 {
1278 GaimBuddy *b;
1279
1280 b = gaim_find_buddy_in_group(gc->account, name, grp);
1281 if (!b)
1282 return;
1283
1284 silcgaim_add_buddy_i(gc, b, FALSE);
1285 }
1286
1287 void silcgaim_add_buddies(GaimConnection *gc, GList *buddies)
1288 {
1289 while (buddies) {
1290 GaimBuddy *b;
1291 b = gaim_find_buddy(gc->account, buddies->data);
1292 if (!b)
1293 continue;
1294 silcgaim_add_buddy_i(gc, b, TRUE);
1295 buddies = buddies->next;
1296 }
1297 }
1298
1299 void silcgaim_remove_buddy(GaimConnection *gc, const char *name,
1300 const char *group)
1301 {
1302 GaimBuddy *b;
1303 GaimGroup *g;
1304
1305 g = gaim_find_group(group);
1306 b = gaim_find_buddy_in_group(gc->account, name, g);
1307 if (!b)
1308 return;
1309
1310 silc_free(b->proto_data);
1311 }
1312
1313 void silcgaim_idle_set(GaimConnection *gc, int idle)
1314
1315 {
1316 SilcGaim sg = gc->proto_data;
1317 SilcClient client = sg->client;
1318 SilcClientConnection conn = sg->conn;
1319 SilcAttributeObjService service;
1320 const char *server;
1321 int port;
1322
1323 server = gaim_account_get_string(sg->account, "server",
1324 "silc.silcnet.org");
1325 port = gaim_account_get_int(sg->account, "port", 706),
1326
1327 memset(&service, 0, sizeof(service));
1328 silc_client_attribute_del(client, conn,
1329 SILC_ATTRIBUTE_SERVICE, NULL);
1330 service.port = port;
1331 g_snprintf(service.address, sizeof(service.address), "%s", server);
1332 service.idle = idle;
1333 silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE,
1334 &service, sizeof(service));
1335 }
1336
1337 char *silcgaim_status_text(GaimBuddy *b)
1338 {
1339 SilcGaim sg = b->account->gc->proto_data;
1340 SilcClient client = sg->client;
1341 SilcClientConnection conn = sg->conn;
1342 SilcClientID *client_id = b->proto_data;
1343 SilcClientEntry client_entry;
1344 SilcAttributePayload attr;
1345 SilcAttributeMood mood = 0;
1346
1347 /* Get the client entry. */
1348 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1349 if (!client_entry)
1350 return NULL;
1351
1352 /* If user is online, we show the mood status, if available.
1353 If user is offline or away that status is indicated. */
1354
1355 if (client_entry->mode & SILC_UMODE_DETACHED)
1356 return g_strdup(_("Detached"));
1357 if (client_entry->mode & SILC_UMODE_GONE)
1358 return g_strdup(_("Away"));
1359 if (client_entry->mode & SILC_UMODE_INDISPOSED)
1360 return g_strdup(_("Indisposed"));
1361 if (client_entry->mode & SILC_UMODE_BUSY)
1362 return g_strdup(_("Busy"));
1363 if (client_entry->mode & SILC_UMODE_PAGE)
1364 return g_strdup(_("Wake Me Up"));
1365 if (client_entry->mode & SILC_UMODE_HYPER)
1366 return g_strdup(_("Hyper Active"));
1367 if (client_entry->mode & SILC_UMODE_ROBOT)
1368 return g_strdup(_("Robot"));
1369
1370 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
1371 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
1372 /* The mood is a bit mask, so we could show multiple moods,
1373 but let's show only one for now. */
1374 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
1375 return g_strdup(_("Happy"));
1376 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
1377 return g_strdup(_("Sad"));
1378 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
1379 return g_strdup(_("Angry"));
1380 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
1381 return g_strdup(_("Jealous"));
1382 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
1383 return g_strdup(_("Ashamed"));
1384 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
1385 return g_strdup(_("Invincible"));
1386 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
1387 return g_strdup(_("In Love"));
1388 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
1389 return g_strdup(_("Sleepy"));
1390 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
1391 return g_strdup(_("Bored"));
1392 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
1393 return g_strdup(_("Excited"));
1394 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
1395 return g_strdup(_("Anxious"));
1396 }
1397
1398 return NULL;
1399 }
1400
1401 char *silcgaim_tooltip_text(GaimBuddy *b)
1402 {
1403 SilcGaim sg = b->account->gc->proto_data;
1404 SilcClient client = sg->client;
1405 SilcClientConnection conn = sg->conn;
1406 SilcClientID *client_id = b->proto_data;
1407 SilcClientEntry client_entry;
1408 SilcAttributePayload attr;
1409 SilcAttributeMood mood = 0;
1410 SilcAttributeContact contact;
1411 SilcAttributeObjDevice device;
1412 SilcAttributeObjGeo geo;
1413 GString *s;
1414 char *buf;
1415 char tmp[256];
1416
1417 s = g_string_new("");
1418
1419 /* Get the client entry. */
1420 client_entry = silc_client_get_client_by_id(client, conn, client_id);
1421 if (!client_entry)
1422 return NULL;
1423
1424 if (client_entry->nickname)
1425 g_string_append_printf(s, "<b>Nickname:</b> %s\n",
1426 client_entry->nickname);
1427 if (client_entry->username && client_entry->hostname)
1428 g_string_append_printf(s, "<b>Username:</b> %s@%s\n",
1429 client_entry->username, client_entry->hostname);
1430 if (client_entry->mode) {
1431 g_string_append_printf(s, "<b>Modes:</b> ");
1432 memset(tmp, 0, sizeof(tmp));
1433 silcgaim_get_umode_string(client_entry->mode,
1434 tmp, sizeof(tmp) - strlen(tmp));
1435 g_string_append_printf(s, "%s\n", tmp);
1436 }
1437
1438 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD);
1439 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
1440 if (mood)
1441 g_string_append_printf(s, "<b>Mood:</b> ");
1442 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
1443 g_string_append_printf(s, "[Happy] ");
1444 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
1445 g_string_append_printf(s, "[Sad] ");
1446 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
1447 g_string_append_printf(s, "[Angry] ");
1448 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
1449 g_string_append_printf(s, "[Jealous] ");
1450 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
1451 g_string_append_printf(s, "[Ashamed] ");
1452 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
1453 g_string_append_printf(s, "[Invincible] ");
1454 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
1455 g_string_append_printf(s, "[In Love] ");
1456 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
1457 g_string_append_printf(s, "[Sleepy] ");
1458 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
1459 g_string_append_printf(s, "[Bored] ");
1460 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
1461 g_string_append_printf(s, "[Excited] ");
1462 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
1463 g_string_append_printf(s, "[Anxious] ");
1464 if (mood)
1465 g_string_append_printf(s, "\n");
1466 }
1467
1468 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_FREETEXT);
1469 memset(tmp, 0, sizeof(tmp));
1470 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
1471 g_string_append_printf(s, "<b>Status Text:</b> %s\n", tmp);
1472
1473 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT);
1474 if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) {
1475 if (contact)
1476 g_string_append_printf(s, "<b>Preferred Contact:</b> ");
1477 if (contact & SILC_ATTRIBUTE_CONTACT_CHAT)
1478 g_string_append_printf(s, "[chat] ");
1479 if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL)
1480 g_string_append_printf(s, "[email] ");
1481 if (contact & SILC_ATTRIBUTE_CONTACT_CALL)
1482 g_string_append_printf(s, "[phone] ");
1483 if (contact & SILC_ATTRIBUTE_CONTACT_PAGE)
1484 g_string_append_printf(s, "[paging] ");
1485 if (contact & SILC_ATTRIBUTE_CONTACT_SMS)
1486 g_string_append_printf(s, "[SMS] ");
1487 if (contact & SILC_ATTRIBUTE_CONTACT_MMS)
1488 g_string_append_printf(s, "[MMS] ");
1489 if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO)
1490 g_string_append_printf(s, "[video conferencing] ");
1491 g_string_append_printf(s, "\n");
1492 }
1493
1494 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE);
1495 memset(tmp, 0, sizeof(tmp));
1496 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
1497 g_string_append_printf(s, "<b>Preferred Language:</b> %s\n", tmp);
1498
1499 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_DEVICE_INFO);
1500 memset(&device, 0, sizeof(device));
1501 if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) {
1502 g_string_append_printf(s, "<b>Device:</b> ");
1503 if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER)
1504 g_string_append_printf(s, "Computer: ");
1505 if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE)
1506 g_string_append_printf(s, "Mobile Phone: ");
1507 if (device.type == SILC_ATTRIBUTE_DEVICE_PDA)
1508 g_string_append_printf(s, "PDA: ");
1509 if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL)
1510 g_string_append_printf(s, "Terminal: ");
1511 g_string_append_printf(s, "%s %s %s %s\n",
1512 device.manufacturer ? device.manufacturer : "",
1513 device.version ? device.version : "",
1514 device.model ? device.model : "",
1515 device.language ? device.language : "");
1516 }
1517
1518 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_TIMEZONE);
1519 memset(tmp, 0, sizeof(tmp));
1520 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
1521 g_string_append_printf(s, "<b>Timezone:</b> %s\n", tmp);
1522
1523 attr = silcgaim_get_attr(client_entry->attrs, SILC_ATTRIBUTE_GEOLOCATION);
1524 memset(&geo, 0, sizeof(geo));
1525 if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo)))
1526 g_string_append_printf(s, "<b>Geolocation:</b> %s %s %s (%s)\n",
1527 geo.longitude ? geo.longitude : "",
1528 geo.latitude ? geo.latitude : "",
1529 geo.altitude ? geo.altitude : "",
1530 geo.accuracy ? geo.accuracy : "");
1531
1532 buf = g_string_free(s, FALSE);
1533 return buf;
1534 }
1535
1536 static void
1537 silcgaim_buddy_kill(GaimConnection *gc, const char *name)
1538 {
1539 SilcGaim sg = gc->proto_data;
1540 SilcClient client = sg->client;
1541 SilcClientConnection conn = sg->conn;
1542
1543 /* Call KILL */
1544 silc_client_command_call(client, conn, NULL, "KILL",
1545 name, "Killed by operator", NULL);
1546 }
1547
1548 static void
1549 silcgaim_buddy_send_file(GaimConnection *gc, const char *name)
1550 {
1551 silcgaim_ftp_send_file(gc, name);
1552 }
1553
1554 GList *silcgaim_buddy_menu(GaimConnection *gc, const char *name)
1555 {
1556 SilcGaim sg = gc->proto_data;
1557 SilcClientConnection conn = sg->conn;
1558 GList *m = NULL;
1559 struct proto_buddy_menu *pbm;
1560 GaimBuddy *b;
1561 const char *pkfile = NULL;
1562 SilcClientEntry client_entry = NULL;
1563
1564 b = gaim_find_buddy(gc->account, name);
1565 if (b) {
1566 pkfile = gaim_blist_node_get_string((GaimBlistNode *)b, "public-key");
1567 client_entry = silc_client_get_client_by_id(sg->client,
1568 sg->conn,
1569 b->proto_data);
1570 }
1571
1572 if (client_entry && client_entry->send_key) {
1573 pbm = g_new0(struct proto_buddy_menu, 1);
1574 pbm->label = _("Reset IM Key");
1575 pbm->callback = silcgaim_buddy_resetkey;
1576 pbm->gc = gc;
1577 m = g_list_append(m, pbm);
1578 } else {
1579 pbm = g_new0(struct proto_buddy_menu, 1);
1580 pbm->label = _("IM with Key Exchange");
1581 pbm->callback = silcgaim_buddy_keyagr;
1582 pbm->gc = gc;
1583 m = g_list_append(m, pbm);
1584
1585 pbm = g_new0(struct proto_buddy_menu, 1);
1586 pbm->label = _("IM with Password");
1587 pbm->callback = silcgaim_buddy_privkey;
1588 pbm->gc = gc;
1589 m = g_list_append(m, pbm);
1590 }
1591
1592 if (pkfile) {
1593 pbm = g_new0(struct proto_buddy_menu, 1);
1594 pbm->label = _("Show Public Key");
1595 pbm->callback = silcgaim_buddy_showkey;
1596 pbm->gc = gc;
1597 m = g_list_append(m, pbm);
1598 } else {
1599 pbm = g_new0(struct proto_buddy_menu, 1);
1600 pbm->label = _("Get Public Key...");
1601 pbm->callback = silcgaim_buddy_getkey;
1602 pbm->gc = gc;
1603 m = g_list_append(m, pbm);
1604 }
1605
1606 pbm = g_new0(struct proto_buddy_menu, 1);
1607 pbm->label = _("Send File...");
1608 pbm->callback = silcgaim_buddy_send_file;
1609 pbm->gc = gc;
1610 m = g_list_append(m, pbm);
1611
1612 if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
1613 pbm = g_new0(struct proto_buddy_menu, 1);
1614 pbm->label = _("Kill User");
1615 pbm->callback = silcgaim_buddy_kill;
1616 pbm->gc = gc;
1617 m = g_list_append(m, pbm);
1618 }
1619
1620 return m;
1621 }