5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2004 - 2007 Pekka Riikonen
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.
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.
21 #include "glibcompat.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
29 /***************************** Key Agreement *********************************/
32 silcpurple_buddy_keyagr(PurpleBlistNode
*node
, gpointer data
);
35 silcpurple_buddy_keyagr_do(PurpleConnection
*gc
, const char *name
,
36 gboolean force_local
);
44 silcpurple_buddy_keyagr_resolved(SilcClient client
,
45 SilcClientConnection conn
,
50 PurpleConnection
*gc
= client
->application
;
51 SilcPurpleResolve r
= context
;
55 g_snprintf(tmp
, sizeof(tmp
),
56 _("User %s is not present in the network"), r
->nick
);
57 purple_notify_error(gc
, _("Key Agreement"),
58 _("Cannot perform the key agreement"), tmp
,
59 purple_request_cpar_from_connection(gc
));
65 silcpurple_buddy_keyagr_do(gc
, r
->nick
, FALSE
);
71 silcpurple_buddy_keyagr_cb(SilcClient client
,
72 SilcClientConnection conn
,
73 SilcClientEntry client_entry
,
74 SilcKeyAgreementStatus status
,
75 SilcSKEKeyMaterial key
,
78 PurpleConnection
*gc
= client
->application
;
79 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
85 case SILC_KEY_AGREEMENT_OK
:
87 PurpleIMConversation
*im
;
90 /* Set the private key for this client */
91 silc_client_del_private_message_key(client
, conn
, client_entry
);
92 silc_client_add_private_message_key_ske(client
, conn
, client_entry
,
94 silc_ske_free_key_material(key
);
98 im
= purple_conversations_find_im_with_account(
99 client_entry
->nickname
, sg
->account
);
101 /* we don't have windows in the core anymore...but we may want to
102 * provide some method for asking the UI to show the window
103 purple_conversation_window_show(purple_conversation_get_window(im));
106 im
= purple_im_conversation_new(sg
->account
,
107 client_entry
->nickname
);
109 g_snprintf(tmp
, sizeof(tmp
), "%s [private key]", client_entry
->nickname
);
110 purple_conversation_set_title(PURPLE_CONVERSATION(im
), tmp
);
114 case SILC_KEY_AGREEMENT_ERROR
:
115 purple_notify_error(gc
, _("Key Agreement"),
116 _("Error occurred during key agreement"), NULL
,
117 purple_request_cpar_from_connection(gc
));
120 case SILC_KEY_AGREEMENT_FAILURE
:
121 purple_notify_error(gc
, _("Key Agreement"),
122 _("Key Agreement failed"), NULL
,
123 purple_request_cpar_from_connection(gc
));
126 case SILC_KEY_AGREEMENT_TIMEOUT
:
127 purple_notify_error(gc
, _("Key Agreement"),
128 _("Timeout during key agreement"), NULL
,
129 purple_request_cpar_from_connection(gc
));
132 case SILC_KEY_AGREEMENT_ABORTED
:
133 purple_notify_error(gc
, _("Key Agreement"),
134 _("Key agreement was aborted"), NULL
,
135 purple_request_cpar_from_connection(gc
));
138 case SILC_KEY_AGREEMENT_ALREADY_STARTED
:
139 purple_notify_error(gc
, _("Key Agreement"), _("Key agreement is"
140 " already started"), NULL
,
141 purple_request_cpar_from_connection(gc
));
144 case SILC_KEY_AGREEMENT_SELF_DENIED
:
145 purple_notify_error(gc
, _("Key Agreement"), _("Key agreement "
146 "cannot be started with yourself"), NULL
,
147 purple_request_cpar_from_connection(gc
));
156 silcpurple_buddy_keyagr_do(PurpleConnection
*gc
, const char *name
,
157 gboolean force_local
)
159 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
161 SilcClientEntry client_entry
;
162 SilcClientConnectionParams params
;
163 char *local_ip
= NULL
, *remote_ip
= NULL
;
164 gboolean local
= TRUE
;
167 if (!sg
->conn
|| !name
)
170 /* Find client entry */
171 clients
= silc_client_get_clients_local(sg
->client
, sg
->conn
, name
,
174 /* Resolve unknown user */
175 SilcPurpleResolve r
= silc_calloc(1, sizeof(*r
));
178 r
->nick
= g_strdup(name
);
180 silc_client_get_clients(sg
->client
, sg
->conn
, name
, NULL
,
181 silcpurple_buddy_keyagr_resolved
, r
);
185 silc_socket_stream_get_info(silc_packet_stream_get_stream(sg
->conn
->stream
),
186 &sock
, NULL
, NULL
, NULL
);
188 /* Resolve the local IP from the outgoing socket connection. We resolve
189 it to check whether we have a private range IP address or public IP
190 address. If we have public then we will assume that we are not behind
191 NAT and will provide automatically the point of connection to the
192 agreement. If we have private range address we assume that we are
193 behind NAT and we let the responder provide the point of connection.
195 The algorithm also checks the remote IP address of server connection.
196 If it is private range address and we have private range address we
197 assume that we are chatting in LAN and will provide the point of
200 Naturally this algorithm does not always get things right. */
202 if (silc_net_check_local_by_sock(sock
, NULL
, &local_ip
)) {
203 /* Check if the IP is private */
204 if (!force_local
&& silcpurple_ip_is_private(local_ip
)) {
207 /* Local IP is private, resolve the remote server IP to see whether
208 we are talking to Internet or just on LAN. */
209 if (silc_net_check_host_by_sock(sock
, NULL
,
211 if (silcpurple_ip_is_private(remote_ip
))
212 /* We assume we are in LAN. Let's provide
213 the connection point. */
221 if (local
&& !local_ip
)
222 local_ip
= silc_net_localip();
224 silc_dlist_start(clients
);
225 client_entry
= silc_dlist_get(clients
);
227 memset(¶ms
, 0, sizeof(params
));
228 params
.timeout_secs
= 60;
230 /* Provide connection point */
231 params
.local_ip
= local_ip
;
233 /* Send the key agreement request */
234 silc_client_send_key_agreement(sg
->client
, sg
->conn
, client_entry
,
235 ¶ms
, sg
->public_key
,
237 silcpurple_buddy_keyagr_cb
, NULL
);
240 silc_free(remote_ip
);
241 silc_client_list_free(sg
->client
, sg
->conn
, clients
);
246 SilcClientConnection conn
;
247 SilcClientID client_id
;
250 } *SilcPurpleKeyAgrAsk
;
253 silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a
, gint id
)
255 SilcClientEntry client_entry
;
256 SilcClientConnectionParams params
;
261 /* Get the client entry. */
262 client_entry
= silc_client_get_client_by_id(a
->client
, a
->conn
,
265 purple_notify_error(a
->client
->application
, _("Key Agreement"),
266 _("The remote user is not present in the network any more"),
271 /* If the hostname was provided by the requestor perform the key agreement
272 now. Otherwise, we will send him a request to connect to us. */
274 memset(¶ms
, 0, sizeof(params
));
275 params
.timeout_secs
= 60;
276 silc_client_perform_key_agreement(a
->client
, a
->conn
,
277 client_entry
, ¶ms
,
279 a
->conn
->private_key
,
280 a
->hostname
, a
->port
,
281 silcpurple_buddy_keyagr_cb
, NULL
);
283 /* Send request. Force us as the point of connection since requestor
284 did not provide the point of connection. */
285 silcpurple_buddy_keyagr_do(a
->client
->application
,
286 client_entry
->nickname
, TRUE
);
294 void silcpurple_buddy_keyagr_request(SilcClient client
,
295 SilcClientConnection conn
,
296 SilcClientEntry client_entry
,
297 const char *hostname
, SilcUInt16 port
,
300 char tmp
[128], tmp2
[128];
301 SilcPurpleKeyAgrAsk a
;
302 PurpleConnection
*gc
= client
->application
;
304 /* For now Pidgin don't support UDP key agreement */
308 g_snprintf(tmp
, sizeof(tmp
),
309 _("Key agreement request received from %s. Would you like to "
310 "perform the key agreement?"), client_entry
->nickname
);
312 g_snprintf(tmp2
, sizeof(tmp2
),
313 _("The remote user is waiting key agreement on:\n"
314 "Remote host: %s\nRemote port: %d"), hostname
, port
);
316 a
= silc_calloc(1, sizeof(*a
));
321 a
->client_id
= client_entry
->id
;
323 a
->hostname
= g_strdup(hostname
);
326 purple_request_action(client
->application
, _("Key Agreement Request"), tmp
,
327 hostname
? tmp2
: NULL
, 1, purple_request_cpar_from_connection(gc
),
328 a
, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb
),
329 _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb
));
333 silcpurple_buddy_keyagr(PurpleBlistNode
*node
, gpointer data
)
336 PurpleAccount
*account
;
338 buddy
= (PurpleBuddy
*)node
;
339 account
= purple_buddy_get_account(buddy
);
340 silcpurple_buddy_keyagr_do(purple_account_get_connection(account
),
341 purple_buddy_get_name(buddy
), FALSE
);
345 /**************************** Static IM Key **********************************/
348 silcpurple_buddy_resetkey(PurpleBlistNode
*node
, gpointer data
)
351 PurpleConnection
*gc
;
355 g_return_if_fail(PURPLE_IS_BUDDY(node
));
357 b
= (PurpleBuddy
*) node
;
358 gc
= purple_account_get_connection(purple_buddy_get_account(b
));
359 sg
= purple_connection_get_protocol_data(gc
);
361 /* Find client entry */
362 clients
= silc_client_get_clients_local(sg
->client
, sg
->conn
,
363 purple_buddy_get_name(b
), FALSE
);
367 silc_dlist_start(clients
);
368 silc_client_del_private_message_key(sg
->client
, sg
->conn
,
369 silc_dlist_get(clients
));
370 silc_client_list_free(sg
->client
, sg
->conn
, clients
);
375 SilcClientConnection conn
;
376 SilcClientID client_id
;
377 } *SilcPurplePrivkey
;
380 silcpurple_buddy_privkey(PurpleConnection
*gc
, const char *name
);
383 silcpurple_buddy_privkey_cb(SilcPurplePrivkey p
, const char *passphrase
)
385 SilcClientEntry client_entry
;
387 if (!passphrase
|| !(*passphrase
)) {
392 /* Get the client entry. */
393 client_entry
= silc_client_get_client_by_id(p
->client
, p
->conn
,
396 purple_notify_error(p
->client
->application
, _("IM With Password"),
397 _("The remote user is not present in the network any more"),
403 /* Set the private message key */
404 silc_client_del_private_message_key(p
->client
, p
->conn
,
406 silc_client_add_private_message_key(p
->client
, p
->conn
,
407 client_entry
, NULL
, NULL
,
408 (unsigned char *)passphrase
,
414 silcpurple_buddy_privkey_resolved(SilcClient client
,
415 SilcClientConnection conn
,
423 g_snprintf(tmp
, sizeof(tmp
),
424 _("User %s is not present in the network"),
425 (const char *)context
);
426 purple_notify_error(client
->application
, _("IM With Password"),
427 _("Cannot set IM key"), tmp
, NULL
);
432 silcpurple_buddy_privkey(client
->application
, context
);
437 silcpurple_buddy_privkey(PurpleConnection
*gc
, const char *name
)
439 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
442 SilcClientEntry client_entry
;
447 /* Find client entry */
448 clients
= silc_client_get_clients_local(sg
->client
, sg
->conn
,
451 silc_client_get_clients(sg
->client
, sg
->conn
, name
, NULL
,
452 silcpurple_buddy_privkey_resolved
,
457 silc_dlist_start(clients
);
458 client_entry
= silc_dlist_get(clients
);
460 p
= silc_calloc(1, sizeof(*p
));
463 p
->client
= sg
->client
;
465 p
->client_id
= client_entry
->id
;
466 purple_request_input(gc
, _("IM With Password"), NULL
,
467 _("Set IM Password"), NULL
, FALSE
, TRUE
, NULL
,
468 _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb
),
469 _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb
),
470 purple_request_cpar_from_connection(gc
), p
);
472 silc_client_list_free(sg
->client
, sg
->conn
, clients
);
476 silcpurple_buddy_privkey_menu(PurpleBlistNode
*node
, gpointer data
)
479 PurpleConnection
*gc
;
481 g_return_if_fail(PURPLE_IS_BUDDY(node
));
483 buddy
= (PurpleBuddy
*) node
;
484 gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
486 silcpurple_buddy_privkey(gc
, purple_buddy_get_name(buddy
));
490 /**************************** Get Public Key *********************************/
494 SilcClientConnection conn
;
495 SilcClientID client_id
;
496 } *SilcPurpleBuddyGetkey
;
499 silcpurple_buddy_getkey(PurpleConnection
*gc
, const char *name
);
502 silcpurple_buddy_getkey_cb(SilcClient client
, SilcClientConnection conn
,
503 SilcCommand command
, SilcStatus status
,
504 SilcStatus error
, void *context
, va_list ap
)
506 SilcClientEntry client_entry
;
507 SilcPurpleBuddyGetkey g
= context
;
509 if (status
!= SILC_STATUS_OK
) {
510 purple_notify_error(g
->client
->application
, _("Get Public Key"),
511 _("The remote user is not present in the network any more"),
517 /* Get the client entry. */
518 client_entry
= silc_client_get_client_by_id(g
->client
, g
->conn
,
521 purple_notify_error(g
->client
->application
, _("Get Public Key"),
522 _("The remote user is not present in the network any more"),
528 if (!client_entry
->public_key
) {
533 /* Now verify the public key */
534 silcpurple_verify_public_key(g
->client
, g
->conn
, client_entry
->nickname
,
535 SILC_CONN_CLIENT
, client_entry
->public_key
,
542 silcpurple_buddy_getkey_resolved(SilcClient client
,
543 SilcClientConnection conn
,
551 g_snprintf(tmp
, sizeof(tmp
),
552 _("User %s is not present in the network"),
553 (const char *)context
);
554 purple_notify_error(client
->application
, _("Get Public Key"),
555 _("Cannot fetch the public key"), tmp
, NULL
);
560 silcpurple_buddy_getkey(client
->application
, context
);
565 silcpurple_buddy_getkey(PurpleConnection
*gc
, const char *name
)
567 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
568 SilcClient client
= sg
->client
;
569 SilcClientConnection conn
= sg
->conn
;
570 SilcClientEntry client_entry
;
572 SilcPurpleBuddyGetkey g
;
573 SilcUInt16 cmd_ident
;
578 /* Find client entry */
579 clients
= silc_client_get_clients_local(client
, conn
, name
, FALSE
);
581 silc_client_get_clients(client
, conn
, name
, NULL
,
582 silcpurple_buddy_getkey_resolved
,
587 silc_dlist_start(clients
);
588 client_entry
= silc_dlist_get(clients
);
591 g
= silc_calloc(1, sizeof(*g
));
596 g
->client_id
= client_entry
->id
;
597 cmd_ident
= silc_client_command_call(client
, conn
, NULL
, "GETKEY",
598 client_entry
->nickname
, NULL
);
599 silc_client_command_pending(conn
, SILC_COMMAND_GETKEY
, cmd_ident
,
600 silcpurple_buddy_getkey_cb
, g
);
601 silc_client_list_free(client
, conn
, clients
);
605 silcpurple_buddy_getkey_menu(PurpleBlistNode
*node
, gpointer data
)
608 PurpleConnection
*gc
;
610 g_return_if_fail(PURPLE_IS_BUDDY(node
));
612 buddy
= (PurpleBuddy
*) node
;
613 gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
615 silcpurple_buddy_getkey(gc
, purple_buddy_get_name(buddy
));
619 silcpurple_buddy_showkey(PurpleBlistNode
*node
, gpointer data
)
622 PurpleConnection
*gc
;
624 SilcPublicKey public_key
;
627 g_return_if_fail(PURPLE_IS_BUDDY(node
));
629 b
= (PurpleBuddy
*) node
;
630 gc
= purple_account_get_connection(purple_buddy_get_account(b
));
631 sg
= purple_connection_get_protocol_data(gc
);
633 pkfile
= purple_blist_node_get_string(node
, "public-key");
634 if (!silc_pkcs_load_public_key(pkfile
, &public_key
)) {
635 purple_notify_error(gc
, _("Show Public Key"),
636 _("Could not load public key"), NULL
,
637 purple_request_cpar_from_connection(gc
));
641 silcpurple_show_public_key(sg
, purple_buddy_get_name(b
), public_key
, NULL
, NULL
);
642 silc_pkcs_public_key_free(public_key
);
646 /**************************** Buddy routines *********************************/
648 /* The buddies are implemented by using the WHOIS and WATCH commands that
649 can be used to search users by their public key. Since nicknames aren't
650 unique in SILC we cannot trust the buddy list using their nickname. We
651 associate public keys to buddies and use those to search and watch
654 The problem is that Purple does not return PurpleBuddy contexts to the
655 callbacks but the buddy names. Naturally, this is not going to work
656 with SILC. But, for now, we have to do what we can... */
660 SilcClientConnection conn
;
661 SilcClientID client_id
;
663 unsigned char *offline_pk
;
664 SilcUInt32 offline_pk_len
;
665 SilcPublicKey public_key
;
666 unsigned int offline
: 1;
667 unsigned int pubkey_search
: 1;
668 unsigned int init
: 1;
669 } *SilcPurpleBuddyRes
;
672 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r
, gint id
);
674 silcpurple_add_buddy_resolved(SilcClient client
,
675 SilcClientConnection conn
,
680 void silcpurple_get_info(PurpleConnection
*gc
, const char *who
)
682 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
683 SilcClient client
= sg
->client
;
684 SilcClientConnection conn
= sg
->conn
;
685 SilcClientEntry client_entry
;
687 const char *filename
, *nick
= who
;
692 if (strlen(who
) > 1 && who
[0] == '@')
694 if (strlen(who
) > 1 && who
[0] == '*')
696 if (strlen(who
) > 2 && who
[0] == '*' && who
[1] == '@')
699 b
= purple_blist_find_buddy(purple_connection_get_account(gc
), nick
);
701 /* See if we have this buddy's public key. If we do use that
702 to search the details. */
704 filename
= purple_blist_node_get_string((PurpleBlistNode
*)b
, "public-key");
706 /* Call WHOIS. The user info is displayed in the WHOIS
708 silc_client_command_call(client
, conn
, NULL
, "WHOIS",
709 "-details", "-pubkey", filename
, NULL
);
713 if (!(proto_data
= purple_buddy_get_protocol_data(b
))) {
714 g_snprintf(tmp
, sizeof(tmp
),
715 _("User %s is not present in the network"), purple_buddy_get_name(b
));
716 purple_notify_error(gc
, _("User Information"),
717 _("Cannot get user information"), tmp
,
718 purple_request_cpar_from_connection(gc
));
722 client_entry
= silc_client_get_client_by_id(client
, conn
, proto_data
);
724 /* Call WHOIS. The user info is displayed in the WHOIS
726 silc_client_command_call(client
, conn
, NULL
, "WHOIS",
727 client_entry
->nickname
, "-details", NULL
);
730 /* Call WHOIS just with nickname. */
731 silc_client_command_call(client
, conn
, NULL
, "WHOIS", nick
, NULL
);
736 silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r
)
739 g_snprintf(tmp
, sizeof(tmp
), _("The %s buddy is not trusted"),
740 purple_buddy_get_name(r
->b
));
741 purple_notify_error(r
->client
->application
, _("Add Buddy"), tmp
,
742 _("You cannot receive buddy notifications until you "
743 "import his/her public key. You can use the Get Public Key "
744 "command to get the public key."),
745 purple_request_cpar_from_account(purple_buddy_get_account(r
->b
)));
746 purple_protocol_got_user_status(purple_buddy_get_account(r
->b
), purple_buddy_get_name(r
->b
), SILCPURPLE_STATUS_ID_OFFLINE
, NULL
);
750 silcpurple_add_buddy_save(SilcBool success
, void *context
)
752 SilcPurpleBuddyRes r
= context
;
753 PurpleBuddy
*b
= r
->b
;
754 SilcClientEntry client_entry
;
755 SilcAttributePayload attr
;
756 SilcAttribute attribute
;
757 SilcVCardStruct vcard
;
758 SilcMime message
= NULL
, extension
= NULL
;
759 SilcMime usericon
= NULL
;
760 SilcAttributeObjPk serverpk
, usersign
, serversign
;
761 gboolean usign_success
= TRUE
, ssign_success
= TRUE
;
762 char filename
[512], filename2
[512], *fingerprint
= NULL
, *tmp
;
768 /* The user did not trust the public key. */
769 silcpurple_add_buddy_pk_no(r
);
770 silc_free(r
->offline_pk
);
772 silc_pkcs_public_key_free(r
->public_key
);
778 /* User is offline. Associate the imported public key with
780 fingerprint
= silc_hash_fingerprint(NULL
, r
->offline_pk
,
782 for (i
= 0; i
< strlen(fingerprint
); i
++)
783 if (fingerprint
[i
] == ' ')
784 fingerprint
[i
] = '_';
785 g_snprintf(filename
, sizeof(filename
) - 1,
786 "%s" G_DIR_SEPARATOR_S
"clientkeys" G_DIR_SEPARATOR_S
"clientkey_%s.pub",
787 silcpurple_silcdir(), fingerprint
);
788 purple_blist_node_set_string((PurpleBlistNode
*)b
, "public-key", filename
);
789 purple_protocol_got_user_status(purple_buddy_get_account(r
->b
), purple_buddy_get_name(r
->b
), SILCPURPLE_STATUS_ID_OFFLINE
, NULL
);
790 silc_free(fingerprint
);
791 silc_free(r
->offline_pk
);
793 silc_pkcs_public_key_free(r
->public_key
);
798 /* Get the client entry. */
799 client_entry
= silc_client_get_client_by_id(r
->client
, r
->conn
,
802 silc_free(r
->offline_pk
);
803 silc_pkcs_public_key_free(r
->public_key
);
805 silc_pkcs_public_key_free(r
->public_key
);
810 memset(&vcard
, 0, sizeof(vcard
));
811 memset(&serverpk
, 0, sizeof(serverpk
));
812 memset(&usersign
, 0, sizeof(usersign
));
813 memset(&serversign
, 0, sizeof(serversign
));
815 /* Now that we have the public key and we trust it now we
816 save the attributes of the buddy and update its status. */
818 if (client_entry
->attrs
) {
819 silc_dlist_start(client_entry
->attrs
);
820 while ((attr
= silc_dlist_get(client_entry
->attrs
))
822 attribute
= silc_attribute_get_attribute(attr
);
825 case SILC_ATTRIBUTE_USER_INFO
:
826 if (!silc_attribute_get_object(attr
, (void *)&vcard
,
831 case SILC_ATTRIBUTE_STATUS_MESSAGE
:
832 message
= silc_mime_alloc();
833 if (!silc_attribute_get_object(attr
, (void *)message
,
838 case SILC_ATTRIBUTE_EXTENSION
:
839 extension
= silc_mime_alloc();
840 if (!silc_attribute_get_object(attr
, (void *)extension
,
845 case SILC_ATTRIBUTE_USER_ICON
:
846 usericon
= silc_mime_alloc();
847 if (!silc_attribute_get_object(attr
, (void *)usericon
,
852 case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY
:
855 if (!silc_attribute_get_object(attr
, (void *)&serverpk
,
860 case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE
:
863 if (!silc_attribute_get_object(attr
, (void *)&usersign
,
868 case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE
:
871 if (!silc_attribute_get_object(attr
, (void *)&serversign
,
882 /* Verify the attribute signatures */
883 silc_hash_alloc((const unsigned char *)"sha1", &hash
);
886 unsigned char *verifyd
;
887 SilcUInt32 verify_len
;
889 verifyd
= silc_attribute_get_verify_data(client_entry
->attrs
,
891 if (verifyd
&& !silc_pkcs_verify(client_entry
->public_key
,
894 verifyd
, verify_len
, hash
))
895 usign_success
= FALSE
;
899 if (serversign
.data
) {
900 SilcPublicKey public_key
;
901 SilcPKCSType type
= 0;
902 unsigned char *verifyd
;
903 SilcUInt32 verify_len
;
905 if (purple_strequal(serverpk
.type
, "silc-rsa"))
906 type
= SILC_PKCS_SILC
;
907 else if (purple_strequal(serverpk
.type
, "ssh-rsa"))
908 type
= SILC_PKCS_SSH2
;
909 else if (purple_strequal(serverpk
.type
, "x509v3-sign-rsa"))
910 type
= SILC_PKCS_X509V3
;
911 else if (purple_strequal(serverpk
.type
, "pgp-sign-rsa"))
912 type
= SILC_PKCS_OPENPGP
;
914 if (silc_pkcs_public_key_alloc(type
, serverpk
.data
,
917 verifyd
= silc_attribute_get_verify_data(client_entry
->attrs
,
919 if (verifyd
&& !silc_pkcs_verify(public_key
,
924 ssign_success
= FALSE
;
925 silc_pkcs_public_key_free(public_key
);
930 fingerprint
= silc_fingerprint(client_entry
->fingerprint
, 20);
931 for (i
= 0; i
< strlen(fingerprint
); i
++)
932 if (fingerprint
[i
] == ' ')
933 fingerprint
[i
] = '_';
935 if (usign_success
|| ssign_success
) {
938 memset(filename2
, 0, sizeof(filename2
));
940 /* Filename for dir */
941 tmp
= fingerprint
+ strlen(fingerprint
) - 9;
942 g_snprintf(filename
, sizeof(filename
) - 1,
943 "%s" G_DIR_SEPARATOR_S
"friends" G_DIR_SEPARATOR_S
"%s",
944 silcpurple_silcdir(), tmp
);
946 pw
= getpwuid(getuid());
950 /* Create dir if it doesn't exist */
951 if (pw
->pw_uid
== geteuid()) {
952 if (g_mkdir(filename
, 0755) != 0 && errno
!= EEXIST
)
957 g_snprintf(filename2
, sizeof(filename2
) - 1,
958 "%s" G_DIR_SEPARATOR_S
"vcard", filename
);
959 if (vcard
.full_name
) {
960 tmp
= (char *)silc_vcard_encode(&vcard
, &len
);
961 silc_file_writefile(filename2
, tmp
, len
);
965 /* Save status message */
967 memset(filename2
, 0, sizeof(filename2
));
968 g_snprintf(filename2
, sizeof(filename2
) - 1,
969 "%s" G_DIR_SEPARATOR_S
"status_message.mime",
971 tmp
= (char *)silc_mime_get_data(message
, &len
);
972 silc_file_writefile(filename2
, tmp
, len
);
973 silc_mime_free(message
);
976 /* Save extension data */
978 memset(filename2
, 0, sizeof(filename2
));
979 g_snprintf(filename2
, sizeof(filename2
) - 1,
980 "%s" G_DIR_SEPARATOR_S
"extension.mime",
982 tmp
= (char *)silc_mime_get_data(extension
, &len
);
983 silc_file_writefile(filename2
, tmp
, len
);
984 silc_mime_free(extension
);
989 const char *type
= silc_mime_get_field(usericon
, "Content-Type");
991 (purple_strequal(type
, "image/jpeg") ||
992 purple_strequal(type
, "image/gif") ||
993 purple_strequal(type
, "image/bmp") ||
994 purple_strequal(type
, "image/png"))) {
995 const unsigned char *data
;
997 data
= silc_mime_get_data(usericon
, &data_len
);
999 /* TODO: Check if SILC gives us something to use as the checksum instead */
1000 purple_buddy_icons_set_for_user(purple_buddy_get_account(r
->b
), purple_buddy_get_name(r
->b
), g_memdup(data
, data_len
), data_len
, NULL
);
1003 silc_mime_free(usericon
);
1007 /* Save the public key path to buddy properties, as it is used
1008 to identify the buddy in the network (and not the nickname). */
1009 memset(filename
, 0, sizeof(filename
));
1010 g_snprintf(filename
, sizeof(filename
) - 1,
1011 "%s" G_DIR_SEPARATOR_S
"clientkeys" G_DIR_SEPARATOR_S
"clientkey_%s.pub",
1012 silcpurple_silcdir(), fingerprint
);
1013 purple_blist_node_set_string((PurpleBlistNode
*)b
, "public-key", filename
);
1015 /* Update online status */
1016 purple_protocol_got_user_status(purple_buddy_get_account(r
->b
), purple_buddy_get_name(r
->b
), SILCPURPLE_STATUS_ID_AVAILABLE
, NULL
);
1018 /* Finally, start watching this user so we receive its status
1019 changes from the server */
1020 g_snprintf(filename2
, sizeof(filename2
) - 1, "+%s", filename
);
1021 silc_client_command_call(r
->client
, r
->conn
, NULL
, "WATCH", "-pubkey",
1024 silc_hash_free(hash
);
1025 silc_free(fingerprint
);
1026 silc_free(r
->offline_pk
);
1028 silc_pkcs_public_key_free(r
->public_key
);
1033 silcpurple_add_buddy_ask_import(void *user_data
, const char *name
)
1035 SilcPurpleBuddyRes r
= (SilcPurpleBuddyRes
)user_data
;
1037 /* Load the public key */
1038 if (!silc_pkcs_load_public_key(name
, &r
->public_key
)) {
1039 silcpurple_add_buddy_ask_pk_cb(r
, 0);
1040 purple_notify_error(r
->client
->application
, _("Add Buddy"),
1041 _("Could not load public key"), NULL
,
1042 purple_request_cpar_from_account(
1043 purple_buddy_get_account(r
->b
)));
1047 /* Now verify the public key */
1048 r
->offline_pk
= silc_pkcs_public_key_encode(r
->public_key
, &r
->offline_pk_len
);
1049 silcpurple_verify_public_key(r
->client
, r
->conn
, purple_buddy_get_name(r
->b
),
1050 SILC_CONN_CLIENT
, r
->public_key
,
1051 silcpurple_add_buddy_save
, r
);
1055 silcpurple_add_buddy_ask_pk_cancel(void *user_data
, const char *name
)
1057 SilcPurpleBuddyRes r
= (SilcPurpleBuddyRes
)user_data
;
1059 /* The user did not import public key. The buddy is unusable. */
1060 silcpurple_add_buddy_pk_no(r
);
1065 silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r
, gint id
)
1068 /* The user did not import public key. The buddy is unusable. */
1069 silcpurple_add_buddy_pk_no(r
);
1074 /* Open file selector to select the public key. */
1075 purple_request_file(r
->client
->application
, _("Open..."), NULL
, FALSE
,
1076 G_CALLBACK(silcpurple_add_buddy_ask_import
),
1077 G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel
),
1078 purple_request_cpar_from_account(purple_buddy_get_account(r
->b
)), r
);
1083 silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r
)
1086 g_snprintf(tmp
, sizeof(tmp
), _("The %s buddy is not present in the network"),
1087 purple_buddy_get_name(r
->b
));
1088 purple_request_action(r
->client
->application
, _("Add Buddy"), tmp
,
1089 _("To add the buddy you must import his/her public key. "
1090 "Press Import to import a public key."), 0,
1091 purple_request_cpar_from_account(purple_buddy_get_account(r
->b
)), r
, 2,
1092 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb
),
1093 _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb
));
1097 silcpurple_add_buddy_getkey_cb(SilcClient client
, SilcClientConnection conn
,
1098 SilcCommand command
, SilcStatus status
,
1099 SilcStatus error
, void *context
, va_list ap
)
1101 SilcPurpleBuddyRes r
= context
;
1102 SilcClientEntry client_entry
;
1104 if (status
!= SILC_STATUS_OK
) {
1105 /* The buddy is offline/nonexistent. We will require user
1106 to associate a public key with the buddy or the buddy
1109 silcpurple_add_buddy_ask_pk(r
);
1113 /* Get the client entry. */
1114 client_entry
= silc_client_get_client_by_id(r
->client
, r
->conn
,
1116 if (!client_entry
|| !client_entry
->public_key
) {
1117 /* The buddy is offline/nonexistent. We will require user
1118 to associate a public key with the buddy or the buddy
1121 silcpurple_add_buddy_ask_pk(r
);
1125 /* Now verify the public key */
1126 silcpurple_verify_public_key(r
->client
, r
->conn
, client_entry
->nickname
,
1127 SILC_CONN_CLIENT
, client_entry
->public_key
,
1128 silcpurple_add_buddy_save
, r
);
1133 silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r
, PurpleRequestFields
*fields
)
1135 PurpleRequestField
*f
;
1137 SilcClientEntry client_entry
;
1140 f
= purple_request_fields_get_field(fields
, "list");
1141 list
= purple_request_field_list_get_selected(f
);
1143 /* The user did not select any user. */
1144 silcpurple_add_buddy_pk_no(r
);
1149 client_entry
= purple_request_field_list_get_data(f
, list
->data
);
1150 clients
= silc_dlist_init();
1151 silc_dlist_add(clients
, client_entry
);
1152 silcpurple_add_buddy_resolved(r
->client
, r
->conn
, SILC_STATUS_OK
,
1154 silc_dlist_uninit(clients
);
1158 silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r
, PurpleRequestFields
*fields
)
1160 /* The user did not select any user. */
1161 silcpurple_add_buddy_pk_no(r
);
1166 silcpurple_add_buddy_select(SilcPurpleBuddyRes r
, SilcDList clients
)
1168 PurpleRequestFields
*fields
;
1169 PurpleRequestFieldGroup
*g
;
1170 PurpleRequestField
*f
;
1171 char tmp
[512], tmp2
[128];
1173 SilcClientEntry client_entry
;
1175 fields
= purple_request_fields_new();
1176 g
= purple_request_field_group_new(NULL
);
1177 f
= purple_request_field_list_new("list", NULL
);
1178 purple_request_field_group_add_field(g
, f
);
1179 purple_request_field_list_set_multi_select(f
, FALSE
);
1180 purple_request_fields_add_group(fields
, g
);
1182 silc_dlist_start(clients
);
1183 while ((client_entry
= silc_dlist_get(clients
))) {
1185 if (*client_entry
->fingerprint
) {
1186 fingerprint
= silc_fingerprint(client_entry
->fingerprint
, 20);
1187 g_snprintf(tmp2
, sizeof(tmp2
), "\n%s", fingerprint
);
1189 g_snprintf(tmp
, sizeof(tmp
), "%s - %s (%s@%s)%s",
1190 client_entry
->realname
, client_entry
->nickname
,
1191 client_entry
->username
, *client_entry
->hostname
?
1192 client_entry
->hostname
: "",
1193 fingerprint
? tmp2
: "");
1194 purple_request_field_list_add_icon(f
, tmp
, NULL
, client_entry
);
1195 silc_free(fingerprint
);
1198 purple_request_fields(r
->client
->application
, _("Add Buddy"),
1199 _("Select correct user"),
1201 ? _("More than one user was found with the same public key. Select "
1202 "the correct user from the list to add to the buddy list.")
1203 : _("More than one user was found with the same name. Select "
1204 "the correct user from the list to add to the buddy list."),
1206 _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb
),
1207 _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel
),
1208 purple_request_cpar_from_account(purple_buddy_get_account(r
->b
)), r
);
1212 silcpurple_add_buddy_resolved(SilcClient client
,
1213 SilcClientConnection conn
,
1218 SilcPurpleBuddyRes r
= context
;
1219 PurpleBuddy
*b
= r
->b
;
1220 SilcAttributePayload pub
;
1221 SilcAttributeObjPk userpk
;
1222 const char *filename
;
1223 SilcClientEntry client_entry
= NULL
;
1224 SilcUInt16 cmd_ident
;
1227 filename
= purple_blist_node_get_string((PurpleBlistNode
*)b
, "public-key");
1229 /* If the buddy is offline/nonexistent, we will require user
1230 to associate a public key with the buddy or the buddy
1239 /* If the user has already associated a public key, try loading it
1240 * before prompting the user to load it again */
1241 if (filename
!= NULL
)
1242 silcpurple_add_buddy_ask_import(r
, filename
);
1244 silcpurple_add_buddy_ask_pk(r
);
1248 /* If more than one client was found with nickname, we need to verify
1249 from user which one is the correct. */
1250 if (silc_dlist_count(clients
) > 1 && !r
->pubkey_search
) {
1256 silcpurple_add_buddy_select(r
, clients
);
1260 silc_dlist_start(clients
);
1261 client_entry
= silc_dlist_get(clients
);
1263 name
= purple_buddy_get_name(b
);
1265 /* If we searched using public keys and more than one entry was found
1266 the same person is logged on multiple times. */
1267 if (silc_dlist_count(clients
) > 1 && r
->pubkey_search
&& name
) {
1269 /* Find the entry that closest matches to the
1271 SilcClientEntry entry
;
1272 silc_dlist_start(clients
);
1273 while ((entry
= silc_dlist_get(clients
))) {
1274 if (!g_ascii_strncasecmp(name
, entry
->nickname
,
1276 client_entry
= entry
;
1281 /* Verify from user which one is correct */
1282 silcpurple_add_buddy_select(r
, clients
);
1287 g_return_if_fail(client_entry
!= NULL
);
1289 /* The client was found. Now get its public key and verify
1290 that before adding the buddy. */
1291 memset(&userpk
, 0, sizeof(userpk
));
1292 purple_buddy_set_protocol_data(b
, silc_memdup(&client_entry
->id
, sizeof(client_entry
->id
)));
1293 r
->client_id
= client_entry
->id
;
1295 /* Get the public key from attributes, if not present then
1296 resolve it with GETKEY unless we have it cached already. */
1297 if (client_entry
->attrs
&& !client_entry
->public_key
) {
1298 pub
= silcpurple_get_attr(client_entry
->attrs
,
1299 SILC_ATTRIBUTE_USER_PUBLIC_KEY
);
1300 if (!pub
|| !silc_attribute_get_object(pub
, (void *)&userpk
,
1302 /* Get public key with GETKEY */
1304 silc_client_command_call(client
, conn
, NULL
,
1305 "GETKEY", client_entry
->nickname
, NULL
);
1306 silc_client_command_pending(conn
, SILC_COMMAND_GETKEY
,
1308 silcpurple_add_buddy_getkey_cb
,
1312 if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC
,
1313 userpk
.data
, userpk
.data_len
,
1314 &client_entry
->public_key
))
1316 silc_free(userpk
.data
);
1317 } else if (filename
&& !client_entry
->public_key
) {
1318 if (!silc_pkcs_load_public_key(filename
, &client_entry
->public_key
)) {
1319 /* Get public key with GETKEY */
1321 silc_client_command_call(client
, conn
, NULL
,
1322 "GETKEY", client_entry
->nickname
, NULL
);
1323 silc_client_command_pending(conn
, SILC_COMMAND_GETKEY
,
1325 silcpurple_add_buddy_getkey_cb
,
1329 } else if (!client_entry
->public_key
) {
1330 /* Get public key with GETKEY */
1332 silc_client_command_call(client
, conn
, NULL
,
1333 "GETKEY", client_entry
->nickname
, NULL
);
1334 silc_client_command_pending(conn
, SILC_COMMAND_GETKEY
,
1336 silcpurple_add_buddy_getkey_cb
,
1341 /* We have the public key, verify it. */
1342 silcpurple_verify_public_key(client
, conn
, client_entry
->nickname
,
1344 client_entry
->public_key
,
1345 silcpurple_add_buddy_save
, r
);
1349 silcpurple_add_buddy_i(PurpleConnection
*gc
, PurpleBuddy
*b
, gboolean init
)
1351 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1352 SilcClient client
= sg
->client
;
1353 SilcClientConnection conn
= sg
->conn
;
1354 SilcPurpleBuddyRes r
;
1356 const char *filename
, *name
= purple_buddy_get_name(b
);
1358 r
= silc_calloc(1, sizeof(*r
));
1366 /* See if we have this buddy's public key. If we do use that
1367 to search the details. */
1368 filename
= purple_blist_node_get_string((PurpleBlistNode
*)b
, "public-key");
1370 SilcPublicKey public_key
;
1371 SilcAttributeObjPk userpk
;
1373 if (!silc_pkcs_load_public_key(filename
, &public_key
))
1376 /* Get all attributes, and use the public key to search user */
1378 attrs
= silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO
,
1379 SILC_ATTRIBUTE_SERVICE
,
1380 SILC_ATTRIBUTE_STATUS_MOOD
,
1381 SILC_ATTRIBUTE_STATUS_FREETEXT
,
1382 SILC_ATTRIBUTE_STATUS_MESSAGE
,
1383 SILC_ATTRIBUTE_PREFERRED_LANGUAGE
,
1384 SILC_ATTRIBUTE_PREFERRED_CONTACT
,
1385 SILC_ATTRIBUTE_TIMEZONE
,
1386 SILC_ATTRIBUTE_GEOLOCATION
,
1387 SILC_ATTRIBUTE_USER_ICON
,
1388 SILC_ATTRIBUTE_DEVICE_INFO
, 0);
1389 userpk
.type
= "silc-rsa";
1390 userpk
.data
= silc_pkcs_public_key_encode(public_key
, &userpk
.data_len
);
1391 attrs
= silc_attribute_payload_encode(attrs
,
1392 SILC_ATTRIBUTE_USER_PUBLIC_KEY
,
1393 SILC_ATTRIBUTE_FLAG_VALID
,
1394 &userpk
, sizeof(userpk
));
1395 silc_free(userpk
.data
);
1396 silc_pkcs_public_key_free(public_key
);
1397 r
->pubkey_search
= TRUE
;
1399 /* Get all attributes */
1400 attrs
= silc_client_attributes_request(0);
1404 silc_client_get_clients_whois(client
, conn
, name
, NULL
, attrs
,
1405 silcpurple_add_buddy_resolved
, r
);
1406 silc_buffer_free(attrs
);
1409 void silcpurple_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
, const char *message
)
1411 /* Don't add if the buddy is already on the list.
1413 * SILC doesn't have groups, so we also don't need to do anything
1415 if (purple_buddy_get_protocol_data(buddy
) == NULL
)
1416 silcpurple_add_buddy_i(gc
, buddy
, FALSE
);
1419 void silcpurple_send_buddylist(PurpleConnection
*gc
)
1422 PurpleAccount
*account
;
1424 account
= purple_connection_get_account(gc
);
1426 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
1427 buddies
= g_slist_delete_link(buddies
, buddies
))
1429 PurpleBuddy
*buddy
= buddies
->data
;
1430 silcpurple_add_buddy_i(gc
, buddy
, TRUE
);
1434 void silcpurple_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
,
1437 silc_free(purple_buddy_get_protocol_data(buddy
));
1440 void silcpurple_idle_set(PurpleConnection
*gc
, int idle
)
1445 SilcClientConnection conn
;
1446 SilcAttributeObjService service
;
1450 sg
= purple_connection_get_protocol_data(gc
);
1454 client
= sg
->client
;
1462 server
= purple_account_get_string(sg
->account
, "server",
1463 "silc.silcnet.org");
1464 port
= purple_account_get_int(sg
->account
, "port", 706),
1466 memset(&service
, 0, sizeof(service
));
1467 silc_client_attribute_del(client
, conn
,
1468 SILC_ATTRIBUTE_SERVICE
, NULL
);
1469 service
.port
= port
;
1470 g_snprintf(service
.address
, sizeof(service
.address
), "%s", server
);
1471 service
.idle
= idle
;
1472 silc_client_attribute_add(client
, conn
, SILC_ATTRIBUTE_SERVICE
,
1473 &service
, sizeof(service
));
1476 char *silcpurple_status_text(PurpleBuddy
*b
)
1478 PurpleAccount
*account
= purple_buddy_get_account(b
);
1479 PurpleConnection
*gc
= purple_account_get_connection(account
);
1480 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1481 SilcClient client
= sg
->client
;
1482 SilcClientConnection conn
= sg
->conn
;
1483 SilcClientID
*client_id
= purple_buddy_get_protocol_data(b
);
1484 SilcClientEntry client_entry
;
1485 SilcAttributePayload attr
;
1486 SilcAttributeMood mood
= 0;
1488 /* Get the client entry. */
1489 client_entry
= silc_client_get_client_by_id(client
, conn
, client_id
);
1493 /* If user is online, we show the mood status, if available.
1494 If user is offline or away that status is indicated. */
1496 if (client_entry
->mode
& SILC_UMODE_DETACHED
)
1497 return g_strdup(_("Detached"));
1498 if (client_entry
->mode
& SILC_UMODE_GONE
)
1499 return g_strdup(_("Away"));
1500 if (client_entry
->mode
& SILC_UMODE_INDISPOSED
)
1501 return g_strdup(_("Indisposed"));
1502 if (client_entry
->mode
& SILC_UMODE_BUSY
)
1503 return g_strdup(_("Busy"));
1504 if (client_entry
->mode
& SILC_UMODE_PAGE
)
1505 return g_strdup(_("Wake Me Up"));
1506 if (client_entry
->mode
& SILC_UMODE_HYPER
)
1507 return g_strdup(_("Hyper Active"));
1508 if (client_entry
->mode
& SILC_UMODE_ROBOT
)
1509 return g_strdup(_("Robot"));
1511 attr
= silcpurple_get_attr(client_entry
->attrs
, SILC_ATTRIBUTE_STATUS_MOOD
);
1512 if (attr
&& silc_attribute_get_object(attr
, &mood
, sizeof(mood
))) {
1513 /* The mood is a bit mask, so we could show multiple moods,
1514 but let's show only one for now. */
1515 if (mood
& SILC_ATTRIBUTE_MOOD_HAPPY
)
1516 return g_strdup(_("Happy"));
1517 if (mood
& SILC_ATTRIBUTE_MOOD_SAD
)
1518 return g_strdup(_("Sad"));
1519 if (mood
& SILC_ATTRIBUTE_MOOD_ANGRY
)
1520 return g_strdup(_("Angry"));
1521 if (mood
& SILC_ATTRIBUTE_MOOD_JEALOUS
)
1522 return g_strdup(_("Jealous"));
1523 if (mood
& SILC_ATTRIBUTE_MOOD_ASHAMED
)
1524 return g_strdup(_("Ashamed"));
1525 if (mood
& SILC_ATTRIBUTE_MOOD_INVINCIBLE
)
1526 return g_strdup(_("Invincible"));
1527 if (mood
& SILC_ATTRIBUTE_MOOD_INLOVE
)
1528 return g_strdup(_("In Love"));
1529 if (mood
& SILC_ATTRIBUTE_MOOD_SLEEPY
)
1530 return g_strdup(_("Sleepy"));
1531 if (mood
& SILC_ATTRIBUTE_MOOD_BORED
)
1532 return g_strdup(_("Bored"));
1533 if (mood
& SILC_ATTRIBUTE_MOOD_EXCITED
)
1534 return g_strdup(_("Excited"));
1535 if (mood
& SILC_ATTRIBUTE_MOOD_ANXIOUS
)
1536 return g_strdup(_("Anxious"));
1542 void silcpurple_tooltip_text(PurpleBuddy
*b
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
1544 PurpleAccount
*account
= purple_buddy_get_account(b
);
1545 PurpleConnection
*gc
= purple_account_get_connection(account
);
1546 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1547 SilcClient client
= sg
->client
;
1548 SilcClientConnection conn
= sg
->conn
;
1549 SilcClientID
*client_id
= purple_buddy_get_protocol_data(b
);
1550 SilcClientEntry client_entry
;
1551 char *moodstr
, *statusstr
, *contactstr
, *langstr
, *devicestr
, *tzstr
, *geostr
;
1554 /* Get the client entry. */
1555 client_entry
= silc_client_get_client_by_id(client
, conn
, client_id
);
1559 /* TODO: Check whether it's correct to call add_pair_html,
1560 or if we should be using add_pair_plaintext */
1561 purple_notify_user_info_add_pair_html(user_info
, _("Nickname"), client_entry
->nickname
);
1562 g_snprintf(tmp
, sizeof(tmp
), "%s@%s", client_entry
->username
, client_entry
->hostname
);
1563 /* TODO: Check whether it's correct to call add_pair_html,
1564 or if we should be using add_pair_plaintext */
1565 purple_notify_user_info_add_pair_html(user_info
, _("Username"), tmp
);
1566 if (client_entry
->mode
) {
1567 memset(tmp
, 0, sizeof(tmp
));
1568 silcpurple_get_umode_string(client_entry
->mode
,
1569 tmp
, sizeof(tmp
) - strlen(tmp
));
1570 purple_notify_user_info_add_pair_plaintext(user_info
, _("User Modes"), tmp
);
1573 silcpurple_parse_attrs(client_entry
->attrs
, &moodstr
, &statusstr
, &contactstr
, &langstr
, &devicestr
, &tzstr
, &geostr
);
1576 /* TODO: Check whether it's correct to call add_pair_html,
1577 or if we should be using add_pair_plaintext */
1578 purple_notify_user_info_add_pair_html(user_info
, _("Message"), statusstr
);
1584 /* TODO: Check whether it's correct to call add_pair_html,
1585 or if we should be using add_pair_plaintext */
1586 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), moodstr
);
1591 /* TODO: Check whether it's correct to call add_pair_html,
1592 or if we should be using add_pair_plaintext */
1593 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Contact"), contactstr
);
1598 /* TODO: Check whether it's correct to call add_pair_html,
1599 or if we should be using add_pair_plaintext */
1600 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Language"), langstr
);
1605 /* TODO: Check whether it's correct to call add_pair_html,
1606 or if we should be using add_pair_plaintext */
1607 purple_notify_user_info_add_pair_html(user_info
, _("Device"), devicestr
);
1612 /* TODO: Check whether it's correct to call add_pair_html,
1613 or if we should be using add_pair_plaintext */
1614 purple_notify_user_info_add_pair_html(user_info
, _("Timezone"), tzstr
);
1619 /* TODO: Check whether it's correct to call add_pair_html,
1620 or if we should be using add_pair_plaintext */
1621 purple_notify_user_info_add_pair_html(user_info
, _("Geolocation"), geostr
);
1628 silcpurple_buddy_kill(PurpleBlistNode
*node
, gpointer data
)
1631 PurpleConnection
*gc
;
1634 g_return_if_fail(PURPLE_IS_BUDDY(node
));
1636 b
= (PurpleBuddy
*) node
;
1637 gc
= purple_account_get_connection(purple_buddy_get_account(b
));
1638 sg
= purple_connection_get_protocol_data(gc
);
1641 silc_client_command_call(sg
->client
, sg
->conn
, NULL
, "KILL",
1642 purple_buddy_get_name(b
), "Killed by operator", NULL
);
1647 SilcClientEntry client_entry
;
1648 } *SilcPurpleBuddyWb
;
1651 silcpurple_buddy_wb(PurpleBlistNode
*node
, gpointer data
)
1653 SilcPurpleBuddyWb wb
= data
;
1654 silcpurple_wb_init(wb
->sg
, wb
->client_entry
);
1658 GList
*silcpurple_buddy_menu(PurpleBuddy
*buddy
)
1660 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1661 PurpleConnection
*gc
= purple_account_get_connection(account
);
1662 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1663 SilcClientConnection conn
= sg
->conn
;
1664 const char *pkfile
= NULL
;
1665 SilcClientEntry client_entry
= NULL
;
1666 PurpleMenuAction
*act
;
1668 SilcPurpleBuddyWb wb
;
1670 pkfile
= purple_blist_node_get_string((PurpleBlistNode
*) buddy
, "public-key");
1671 client_entry
= silc_client_get_client_by_id(sg
->client
,
1673 purple_buddy_get_protocol_data(buddy
));
1676 silc_client_private_message_key_is_set(sg
->client
,
1677 sg
->conn
, client_entry
)) {
1678 act
= purple_menu_action_new(_("Reset IM Key"),
1679 PURPLE_CALLBACK(silcpurple_buddy_resetkey
),
1681 m
= g_list_append(m
, act
);
1683 act
= purple_menu_action_new(_("IM with Key Exchange"),
1684 PURPLE_CALLBACK(silcpurple_buddy_keyagr
),
1686 m
= g_list_append(m
, act
);
1688 act
= purple_menu_action_new(_("IM with Password"),
1689 PURPLE_CALLBACK(silcpurple_buddy_privkey_menu
),
1691 m
= g_list_append(m
, act
);
1695 act
= purple_menu_action_new(_("Show Public Key"),
1696 PURPLE_CALLBACK(silcpurple_buddy_showkey
),
1698 m
= g_list_append(m
, act
);
1701 act
= purple_menu_action_new(_("Get Public Key..."),
1702 PURPLE_CALLBACK(silcpurple_buddy_getkey_menu
),
1704 m
= g_list_append(m
, act
);
1707 if (conn
&& conn
->local_entry
->mode
& SILC_UMODE_ROUTER_OPERATOR
) {
1708 act
= purple_menu_action_new(_("Kill User"),
1709 PURPLE_CALLBACK(silcpurple_buddy_kill
),
1711 m
= g_list_append(m
, act
);
1715 wb
= silc_calloc(1, sizeof(*wb
));
1717 wb
->client_entry
= client_entry
;
1718 act
= purple_menu_action_new(_("Draw On Whiteboard"),
1719 PURPLE_CALLBACK(silcpurple_buddy_wb
),
1721 m
= g_list_append(m
, act
);
1726 void silcpurple_buddy_set_icon(PurpleConnection
*gc
, PurpleImage
*img
)
1728 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1729 SilcClient client
= sg
->client
;
1730 SilcClientConnection conn
= sg
->conn
;
1736 silc_client_attribute_del(client
, conn
,
1737 SILC_ATTRIBUTE_USER_ICON
, NULL
);
1741 type
= purple_image_get_mimetype(img
);
1744 if (purple_strequal(purple_image_get_extension(img
), "ico"))
1748 mime
= silc_mime_alloc();
1752 silc_mime_add_field(mime
, "Content-Type", type
);
1753 silc_mime_add_data(mime
, purple_image_get_data(img
),
1754 purple_image_get_data_size(img
));
1756 silc_client_attribute_add(client
, conn
, SILC_ATTRIBUTE_USER_ICON
,
1757 mime
, sizeof(*mime
));
1759 silc_mime_free(mime
);