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