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 "image-store.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
30 silc_channel_message(SilcClient client
, SilcClientConnection conn
,
31 SilcClientEntry sender
, SilcChannelEntry channel
,
32 SilcMessagePayload payload
,
33 SilcChannelPrivateKey key
, SilcMessageFlags flags
,
34 const unsigned char *message
,
35 SilcUInt32 message_len
);
37 silc_private_message(SilcClient client
, SilcClientConnection conn
,
38 SilcClientEntry sender
, SilcMessagePayload payload
,
39 SilcMessageFlags flags
, const unsigned char *message
,
40 SilcUInt32 message_len
);
42 silc_ask_passphrase(SilcClient client
, SilcClientConnection conn
,
43 SilcAskPassphrase completion
, void *context
);
45 /* Message sent to the application by library. `conn' associates the
46 message to a specific connection. `conn', however, may be NULL.
47 The `type' indicates the type of the message sent by the library.
48 The application can for example filter the message according the
51 void silc_say(SilcClient client
, SilcClientConnection conn
,
52 SilcClientMessageType type
, char *msg
, ...)
56 PurpleConnection
*gc
= NULL
;
57 PurpleConnectionError reason
= PURPLE_CONNECTION_ERROR_NETWORK_ERROR
;
60 silc_vsnprintf(tmp
, sizeof(tmp
), msg
, va
);
63 if (type
!= SILC_CLIENT_MESSAGE_ERROR
) {
64 purple_debug_misc("silc", "silc_say (%d) %s\n", type
, tmp
);
68 purple_debug_error("silc", "silc_say error: %s\n", tmp
);
70 if (purple_strequal(tmp
, "Authentication failed"))
71 reason
= PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED
;
74 gc
= client
->application
;
77 purple_connection_error(gc
, reason
, tmp
);
79 purple_notify_error(NULL
, _("Error"), _("Error occurred"), tmp
, NULL
);
82 /* Processes incoming MIME message. Can be private message or channel
83 message. Returns TRUE if the message `mime' was displayed. */
86 silcpurple_mime_message(SilcClient client
, SilcClientConnection conn
,
87 SilcClientEntry sender
, SilcChannelEntry channel
,
88 SilcMessagePayload payload
, SilcChannelPrivateKey key
,
89 SilcMessageFlags flags
, SilcMime mime
,
92 PurpleConnection
*gc
= client
->application
;
93 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
95 const unsigned char *data
;
97 PurpleMessageFlags cflags
= 0;
98 PurpleChatConversation
*chat
= NULL
;
104 /* Check for fragmented MIME message */
105 if (silc_mime_is_partial(mime
)) {
107 sg
->mimeass
= silc_mime_assembler_alloc();
110 mime
= silc_mime_assemble(sg
->mimeass
, mime
);
112 /* More fragments to come */
115 /* Process the complete message */
116 return silcpurple_mime_message(client
, conn
, sender
, channel
,
117 payload
, key
, flags
, mime
,
121 /* Check for multipart message */
122 if (silc_mime_is_multipart(mime
)) {
125 SilcDList parts
= silc_mime_get_multiparts(mime
, &mtype
);
127 if (purple_strequal(mtype
, "mixed")) {
128 /* Contains multiple messages */
129 silc_dlist_start(parts
);
130 while ((p
= silc_dlist_get(parts
)) != SILC_LIST_END
) {
131 /* Recursively process parts */
132 ret
= silcpurple_mime_message(client
, conn
, sender
, channel
,
133 payload
, key
, flags
, p
, TRUE
);
137 if (purple_strequal(mtype
, "alternative")) {
138 /* Same message in alternative formats. Kopete sends
139 these. Go in order from last to first. */
140 silc_dlist_end(parts
);
141 while ((p
= silc_dlist_get(parts
)) != SILC_LIST_END
) {
142 /* Go through the alternatives and display the first
144 if (silcpurple_mime_message(client
, conn
, sender
, channel
,
145 payload
, key
, flags
, p
, TRUE
)) {
155 /* Get content type and MIME data */
156 type
= silc_mime_get_field(mime
, "Content-Type");
159 data
= silc_mime_get_data(mime
, &data_len
);
163 /* Process according to content type */
166 if (strstr(type
, "text/plain")) {
167 /* Default is UTF-8, don't check for other charsets */
168 if (!strstr(type
, "utf-8"))
172 silc_channel_message(client
, conn
, sender
, channel
,
174 SILC_MESSAGE_FLAG_UTF8
, data
,
177 silc_private_message(client
, conn
, sender
, payload
,
178 SILC_MESSAGE_FLAG_UTF8
, data
,
185 if (purple_str_has_prefix(type
, "image/")) {
190 /* Get channel chat (if message is for channel) */
191 if (key
&& channel
) {
193 SilcPurplePrvgrp prv
;
195 for (l
= sg
->grps
; l
; l
= l
->next
)
196 if (((SilcPurplePrvgrp
)l
->data
)->key
== key
) {
198 chat
= purple_conversations_find_chat_with_account(
199 prv
->channel
, sg
->account
);
203 if (channel
&& !chat
)
204 chat
= purple_conversations_find_chat_with_account(
205 channel
->channel_name
, sg
->account
);
206 if (channel
&& !chat
)
209 img
= purple_image_new_from_data(g_memdup(data
, data_len
), data_len
);
212 img_id
= purple_image_store_add_temporary(img
);
216 cflags
|= PURPLE_MESSAGE_IMAGES
| PURPLE_MESSAGE_RECV
;
217 g_snprintf(tmp
, sizeof(tmp
), "<img src=\""
218 PURPLE_IMAGE_STORE_PROTOCOL
"%u\">", img_id
);
221 purple_serv_got_chat_in(gc
,
222 purple_chat_conversation_get_id(chat
),
223 sender
->nickname
, cflags
, tmp
, time(NULL
));
225 purple_serv_got_im(gc
, sender
->nickname
,
226 tmp
, cflags
, time(NULL
));
232 /* Whiteboard message */
233 if (strstr(type
, "application/x-wb") &&
234 !purple_account_get_bool(sg
->account
, "block-wb", FALSE
)) {
236 silcpurple_wb_receive_ch(client
, conn
, sender
, channel
,
237 payload
, flags
, data
, data_len
);
239 silcpurple_wb_receive(client
, conn
, sender
, payload
,
240 flags
, data
, data_len
);
247 silc_mime_free(mime
);
251 /* Message for a channel. The `sender' is the sender of the message
252 The `channel' is the channel. The `message' is the message. Note
253 that `message' maybe NULL. The `flags' indicates message flags
254 and it is used to determine how the message can be interpreted
255 (like it may tell the message is multimedia message). */
258 silc_channel_message(SilcClient client
, SilcClientConnection conn
,
259 SilcClientEntry sender
, SilcChannelEntry channel
,
260 SilcMessagePayload payload
,
261 SilcChannelPrivateKey key
, SilcMessageFlags flags
,
262 const unsigned char *message
,
263 SilcUInt32 message_len
)
265 PurpleConnection
*gc
= client
->application
;
266 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
267 PurpleChatConversation
*chat
= NULL
;
275 SilcPurplePrvgrp prv
;
277 for (l
= sg
->grps
; l
; l
= l
->next
)
278 if (((SilcPurplePrvgrp
)l
->data
)->key
== key
) {
280 chat
= purple_conversations_find_chat_with_account(
281 prv
->channel
, sg
->account
);
286 chat
= purple_conversations_find_chat_with_account(
287 channel
->channel_name
, sg
->account
);
291 if (flags
& SILC_MESSAGE_FLAG_SIGNED
&&
292 purple_account_get_bool(sg
->account
, "sign-verify", FALSE
)) {
296 if (flags
& SILC_MESSAGE_FLAG_DATA
) {
297 /* Process MIME message */
299 mime
= silc_mime_decode(NULL
, message
, message_len
);
300 silcpurple_mime_message(client
, conn
, sender
, channel
, payload
,
301 key
, flags
, mime
, FALSE
);
305 if (flags
& SILC_MESSAGE_FLAG_ACTION
) {
306 msg
= g_strdup_printf("/me %s",
307 (const char *)message
);
311 tmp
= g_markup_escape_text(msg
, -1);
313 purple_serv_got_chat_in(gc
, purple_chat_conversation_get_id(chat
),
314 sender
->nickname
, PURPLE_MESSAGE_RECV
, tmp
, time(NULL
));
320 if (flags
& SILC_MESSAGE_FLAG_NOTICE
) {
321 msg
= g_strdup_printf("(notice) <I>%s</I> %s",
322 sender
->nickname
, (const char *)message
);
327 purple_conversation_write_system_message(
328 PURPLE_CONVERSATION(chat
), msg
, 0);
333 if (flags
& SILC_MESSAGE_FLAG_UTF8
) {
334 const char *msg
= (const char *)message
;
335 char *salvaged
= NULL
;
336 if (!g_utf8_validate((const char *)message
, -1, NULL
)) {
337 salvaged
= purple_utf8_salvage((const char *)message
);
340 tmp
= g_markup_escape_text(msg
, -1);
342 purple_serv_got_chat_in(gc
, purple_chat_conversation_get_id(chat
),
343 sender
->nickname
, PURPLE_MESSAGE_RECV
, tmp
, time(NULL
));
350 /* Private message to the client. The `sender' is the sender of the
351 message. The message is `message'and maybe NULL. The `flags'
352 indicates message flags and it is used to determine how the message
353 can be interpreted (like it may tell the message is multimedia
357 silc_private_message(SilcClient client
, SilcClientConnection conn
,
358 SilcClientEntry sender
, SilcMessagePayload payload
,
359 SilcMessageFlags flags
, const unsigned char *message
,
360 SilcUInt32 message_len
)
362 PurpleConnection
*gc
= client
->application
;
363 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
364 PurpleConversation
*convo
;
370 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
371 convo
= purple_conversations_find_with_account(
372 sender
->nickname
, sg
->account
);
374 if (flags
& SILC_MESSAGE_FLAG_SIGNED
&&
375 purple_account_get_bool(sg
->account
, "sign-verify", FALSE
)) {
379 if (flags
& SILC_MESSAGE_FLAG_DATA
) {
380 /* Process MIME message */
382 mime
= silc_mime_decode(NULL
, message
, message_len
);
383 silcpurple_mime_message(client
, conn
, sender
, NULL
, payload
,
384 NULL
, flags
, mime
, FALSE
);
388 if (flags
& SILC_MESSAGE_FLAG_ACTION
&& convo
) {
389 msg
= g_strdup_printf("/me %s",
390 (const char *)message
);
395 tmp
= g_markup_escape_text(msg
, -1);
396 purple_serv_got_im(gc
, sender
->nickname
, tmp
, 0, time(NULL
));
402 if (flags
& SILC_MESSAGE_FLAG_NOTICE
&& convo
) {
403 msg
= g_strdup_printf("(notice) <I>%s</I> %s",
404 sender
->nickname
, (const char *)message
);
409 purple_conversation_write_system_message(convo
, msg
, 0);
414 if (flags
& SILC_MESSAGE_FLAG_UTF8
) {
415 const char *msg
= (const char *)message
;
416 char *salvaged
= NULL
;
417 if (!g_utf8_validate((const char *)message
, -1, NULL
)) {
418 salvaged
= purple_utf8_salvage((const char *)message
);
421 tmp
= g_markup_escape_text(msg
, -1);
423 purple_serv_got_im(gc
, sender
->nickname
, tmp
, 0, time(NULL
));
430 /* Notify message to the client. The notify arguments are sent in the
431 same order as servers sends them. The arguments are same as received
432 from the server except for ID's. If ID is received application receives
433 the corresponding entry to the ID. For example, if Client ID is received
434 application receives SilcClientEntry. Also, if the notify type is
435 for channel the channel entry is sent to application (even if server
436 does not send it because client library gets the channel entry from
437 the Channel ID in the packet's header). */
440 silc_notify(SilcClient client
, SilcClientConnection conn
,
441 SilcNotifyType type
, ...)
444 PurpleConnection
*gc
= client
->application
;
445 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
446 PurpleAccount
*account
= purple_connection_get_account(gc
);
447 PurpleChatConversation
*chat
;
448 SilcClientEntry client_entry
, client_entry2
;
449 SilcChannelEntry channel
;
450 SilcServerEntry server_entry
;
454 SilcHashTableList htl
;
456 char buf
[512], buf2
[512], *tmp
, *name
;
457 SilcNotifyType notify
;
462 memset(buf
, 0, sizeof(buf
));
466 case SILC_NOTIFY_TYPE_NONE
:
469 case SILC_NOTIFY_TYPE_INVITE
:
471 GHashTable
*components
;
472 (void)va_arg(va
, SilcChannelEntry
);
473 name
= va_arg(va
, char *);
474 client_entry
= va_arg(va
, SilcClientEntry
);
476 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
477 g_hash_table_insert(components
, g_strdup("channel"), g_strdup(name
));
478 purple_serv_got_chat_invite(gc
, name
, client_entry
->nickname
, NULL
, components
);
482 case SILC_NOTIFY_TYPE_JOIN
:
483 client_entry
= va_arg(va
, SilcClientEntry
);
484 channel
= va_arg(va
, SilcChannelEntry
);
486 /* If we joined channel, do nothing */
487 if (client_entry
== conn
->local_entry
)
490 chat
= purple_conversations_find_chat_with_account(
491 channel
->channel_name
, sg
->account
);
495 /* Join user to channel */
496 g_snprintf(buf
, sizeof(buf
), "%s@%s",
497 client_entry
->username
, client_entry
->hostname
);
498 purple_chat_conversation_add_user(chat
,
499 client_entry
->nickname
, buf
, PURPLE_CHAT_USER_NONE
, TRUE
);
503 case SILC_NOTIFY_TYPE_LEAVE
:
504 client_entry
= va_arg(va
, SilcClientEntry
);
505 channel
= va_arg(va
, SilcChannelEntry
);
507 chat
= purple_conversations_find_chat_with_account(
508 channel
->channel_name
, sg
->account
);
512 /* Remove user from channel */
513 purple_chat_conversation_remove_user(chat
,
514 client_entry
->nickname
, NULL
);
518 case SILC_NOTIFY_TYPE_SIGNOFF
:
519 client_entry
= va_arg(va
, SilcClientEntry
);
520 tmp
= va_arg(va
, char *);
522 /* Remove from all channels */
523 silc_hash_table_list(client_entry
->channels
, &htl
);
524 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
525 chat
= purple_conversations_find_chat_with_account(
526 chu
->channel
->channel_name
, sg
->account
);
529 purple_chat_conversation_remove_user(chat
,
530 client_entry
->nickname
,
533 silc_hash_table_list_reset(&htl
);
537 case SILC_NOTIFY_TYPE_TOPIC_SET
:
540 idtype
= va_arg(va
, int);
541 entry
= va_arg(va
, void *);
542 tmp
= va_arg(va
, char *);
543 channel
= va_arg(va
, SilcChannelEntry
);
545 chat
= purple_conversations_find_chat_with_account(
546 channel
->channel_name
, sg
->account
);
553 esc
= g_markup_escape_text(tmp
, -1);
554 tmp2
= purple_markup_linkify(esc
);
557 if (idtype
== SILC_ID_CLIENT
) {
558 client_entry
= (SilcClientEntry
)entry
;
559 g_snprintf(buf
, sizeof(buf
),
560 _("%s has changed the topic of <I>%s</I> to: %s"),
561 client_entry
->nickname
, channel
->channel_name
, tmp2
);
562 purple_conversation_write_system_message(
563 PURPLE_CONVERSATION(chat
), buf
, 0);
564 purple_chat_conversation_set_topic(chat
,
565 client_entry
->nickname
, tmp
);
566 } else if (idtype
== SILC_ID_SERVER
) {
567 server_entry
= (SilcServerEntry
)entry
;
568 g_snprintf(buf
, sizeof(buf
),
569 _("%s has changed the topic of <I>%s</I> to: %s"),
570 server_entry
->server_name
, channel
->channel_name
, tmp2
);
571 purple_conversation_write_system_message(
572 PURPLE_CONVERSATION(chat
), buf
, 0);
573 purple_chat_conversation_set_topic(chat
,
574 server_entry
->server_name
, tmp
);
575 } else if (idtype
== SILC_ID_CHANNEL
) {
576 channel
= (SilcChannelEntry
)entry
;
577 g_snprintf(buf
, sizeof(buf
),
578 _("%s has changed the topic of <I>%s</I> to: %s"),
579 channel
->channel_name
, channel
->channel_name
, tmp2
);
580 purple_conversation_write_system_message(
581 PURPLE_CONVERSATION(chat
), buf
, 0);
582 purple_chat_conversation_set_topic(chat
,
583 channel
->channel_name
, tmp
);
585 purple_chat_conversation_set_topic(chat
, NULL
, tmp
);
593 case SILC_NOTIFY_TYPE_NICK_CHANGE
:
594 client_entry
= va_arg(va
, SilcClientEntry
);
595 tmp
= va_arg(va
, char *); /* Old nick */
596 name
= va_arg(va
, char *); /* New nick */
598 if (purple_strequal(tmp
, name
))
601 /* Change nick on all channels */
602 silc_hash_table_list(client_entry
->channels
, &htl
);
603 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
604 chat
= purple_conversations_find_chat_with_account(
605 chu
->channel
->channel_name
, sg
->account
);
608 if (purple_chat_conversation_has_user(chat
, client_entry
->nickname
))
609 purple_chat_conversation_rename_user(chat
,
612 silc_hash_table_list_reset(&htl
);
616 case SILC_NOTIFY_TYPE_CMODE_CHANGE
:
617 idtype
= va_arg(va
, int);
618 entry
= va_arg(va
, void *);
619 mode
= va_arg(va
, SilcUInt32
);
620 (void)va_arg(va
, char *);
621 (void)va_arg(va
, char *);
622 (void)va_arg(va
, char *);
623 (void)va_arg(va
, SilcPublicKey
);
624 (void)va_arg(va
, SilcDList
);
625 channel
= va_arg(va
, SilcChannelEntry
);
627 chat
= purple_conversations_find_chat_with_account(
628 channel
->channel_name
, sg
->account
);
632 if (idtype
== SILC_ID_CLIENT
)
633 name
= ((SilcClientEntry
)entry
)->nickname
;
634 else if (idtype
== SILC_ID_SERVER
)
635 name
= ((SilcServerEntry
)entry
)->server_name
;
637 name
= ((SilcChannelEntry
)entry
)->channel_name
;
642 silcpurple_get_chmode_string(mode
, buf2
, sizeof(buf2
));
643 g_snprintf(buf
, sizeof(buf
),
644 _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name
,
645 channel
->channel_name
, buf2
);
647 g_snprintf(buf
, sizeof(buf
),
648 _("<I>%s</I> removed all channel <I>%s</I> modes"), name
,
649 channel
->channel_name
);
651 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat
), buf
, 0);
654 case SILC_NOTIFY_TYPE_CUMODE_CHANGE
:
656 PurpleChatUserFlags flags
= PURPLE_CHAT_USER_NONE
;
657 idtype
= va_arg(va
, int);
658 entry
= va_arg(va
, void *);
659 mode
= va_arg(va
, SilcUInt32
);
660 client_entry2
= va_arg(va
, SilcClientEntry
);
661 channel
= va_arg(va
, SilcChannelEntry
);
663 chat
= purple_conversations_find_chat_with_account(
664 channel
->channel_name
, sg
->account
);
668 if (idtype
== SILC_ID_CLIENT
)
669 name
= ((SilcClientEntry
)entry
)->nickname
;
670 else if (idtype
== SILC_ID_SERVER
)
671 name
= ((SilcServerEntry
)entry
)->server_name
;
673 name
= ((SilcChannelEntry
)entry
)->channel_name
;
678 silcpurple_get_chumode_string(mode
, buf2
, sizeof(buf2
));
679 g_snprintf(buf
, sizeof(buf
),
680 _("<I>%s</I> set <I>%s's</I> modes to: %s"), name
,
681 client_entry2
->nickname
, buf2
);
682 if (mode
& SILC_CHANNEL_UMODE_CHANFO
)
683 flags
|= PURPLE_CHAT_USER_FOUNDER
;
684 if (mode
& SILC_CHANNEL_UMODE_CHANOP
)
685 flags
|= PURPLE_CHAT_USER_OP
;
687 g_snprintf(buf
, sizeof(buf
),
688 _("<I>%s</I> removed all <I>%s's</I> modes"), name
,
689 client_entry2
->nickname
);
691 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat
), buf
, 0);
692 purple_chat_user_set_flags(purple_chat_conversation_find_user(
693 chat
, client_entry2
->nickname
), flags
);
697 case SILC_NOTIFY_TYPE_MOTD
:
698 tmp
= va_arg(va
, char *);
700 sg
->motd
= silc_memdup(tmp
, strlen(tmp
));
703 case SILC_NOTIFY_TYPE_KICKED
:
704 client_entry
= va_arg(va
, SilcClientEntry
);
705 tmp
= va_arg(va
, char *);
706 client_entry2
= va_arg(va
, SilcClientEntry
);
707 channel
= va_arg(va
, SilcChannelEntry
);
709 chat
= purple_conversations_find_chat_with_account(
710 channel
->channel_name
, sg
->account
);
714 if (client_entry
== conn
->local_entry
) {
715 /* Remove us from channel */
716 g_snprintf(buf
, sizeof(buf
),
717 _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
718 channel
->channel_name
, client_entry2
->nickname
,
720 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat
),
722 purple_serv_got_chat_left(gc
, purple_chat_conversation_get_id(chat
));
724 /* Remove user from channel */
725 g_snprintf(buf
, sizeof(buf
), _("Kicked by %s (%s)"),
726 client_entry2
->nickname
, tmp
? tmp
: "");
727 purple_chat_conversation_remove_user(chat
,
728 client_entry
->nickname
,
734 case SILC_NOTIFY_TYPE_KILLED
:
735 client_entry
= va_arg(va
, SilcClientEntry
);
736 tmp
= va_arg(va
, char *);
737 idtype
= va_arg(va
, int);
738 entry
= va_arg(va
, SilcClientEntry
);
740 if (client_entry
== conn
->local_entry
) {
741 if (idtype
== SILC_ID_CLIENT
) {
742 client_entry2
= (SilcClientEntry
)entry
;
743 g_snprintf(buf
, sizeof(buf
),
744 _("You have been killed by %s (%s)"),
745 client_entry2
->nickname
, tmp
? tmp
: "");
746 } else if (idtype
== SILC_ID_SERVER
) {
747 server_entry
= (SilcServerEntry
)entry
;
748 g_snprintf(buf
, sizeof(buf
),
749 _("You have been killed by %s (%s)"),
750 server_entry
->server_name
, tmp
? tmp
: "");
751 } else if (idtype
== SILC_ID_CHANNEL
) {
752 channel
= (SilcChannelEntry
)entry
;
753 g_snprintf(buf
, sizeof(buf
),
754 _("You have been killed by %s (%s)"),
755 channel
->channel_name
, tmp
? tmp
: "");
758 /* Remove us from all channels */
759 silc_hash_table_list(client_entry
->channels
, &htl
);
760 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
761 chat
= purple_conversations_find_chat_with_account(
762 chu
->channel
->channel_name
, sg
->account
);
765 purple_conversation_write_system_message(
766 PURPLE_CONVERSATION(chat
), buf
, 0);
767 purple_serv_got_chat_left(gc
, purple_chat_conversation_get_id(chat
));
769 silc_hash_table_list_reset(&htl
);
772 if (idtype
== SILC_ID_CLIENT
) {
773 client_entry2
= (SilcClientEntry
)entry
;
774 g_snprintf(buf
, sizeof(buf
),
775 _("Killed by %s (%s)"),
776 client_entry2
->nickname
, tmp
? tmp
: "");
777 } else if (idtype
== SILC_ID_SERVER
) {
778 server_entry
= (SilcServerEntry
)entry
;
779 g_snprintf(buf
, sizeof(buf
),
780 _("Killed by %s (%s)"),
781 server_entry
->server_name
, tmp
? tmp
: "");
782 } else if (idtype
== SILC_ID_CHANNEL
) {
783 channel
= (SilcChannelEntry
)entry
;
784 g_snprintf(buf
, sizeof(buf
),
785 _("Killed by %s (%s)"),
786 channel
->channel_name
, tmp
? tmp
: "");
789 /* Remove user from all channels */
790 silc_hash_table_list(client_entry
->channels
, &htl
);
791 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
792 chat
= purple_conversations_find_chat_with_account(
793 chu
->channel
->channel_name
, sg
->account
);
796 purple_chat_conversation_remove_user(chat
,
797 client_entry
->nickname
, tmp
);
799 silc_hash_table_list_reset(&htl
);
804 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE
:
807 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF
:
808 (void)va_arg(va
, void *);
809 list
= va_arg(va
, SilcDList
);
811 silc_dlist_start(list
);
812 while ((client_entry
= silc_dlist_get(list
))) {
813 /* Remove from all channels */
814 silc_hash_table_list(client_entry
->channels
, &htl
);
815 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
816 chat
= purple_conversations_find_chat_with_account(
817 chu
->channel
->channel_name
, sg
->account
);
820 purple_chat_conversation_remove_user(chat
,
821 client_entry
->nickname
,
822 _("Server signoff"));
824 silc_hash_table_list_reset(&htl
);
828 case SILC_NOTIFY_TYPE_ERROR
:
830 SilcStatus error
= va_arg(va
, int);
831 purple_notify_error(gc
, "Error Notify",
832 silc_get_status_message(error
),
833 NULL
, purple_request_cpar_from_connection(gc
));
837 case SILC_NOTIFY_TYPE_WATCH
:
839 SilcPublicKey public_key
;
844 client_entry
= va_arg(va
, SilcClientEntry
);
845 (void)va_arg(va
, char *);
846 mode
= va_arg(va
, SilcUInt32
);
847 notify
= va_arg(va
, int);
848 public_key
= va_arg(va
, SilcPublicKey
);
856 pk
= silc_pkcs_public_key_encode(public_key
, &pk_len
);
859 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
860 for (i
= 0; i
< strlen(fingerprint
); i
++)
861 if (fingerprint
[i
] == ' ')
862 fingerprint
[i
] = '_';
863 g_snprintf(buf
, sizeof(buf
) - 1,
864 "%s" G_DIR_SEPARATOR_S
"clientkeys"
865 G_DIR_SEPARATOR_S
"clientkey_%s.pub",
866 silcpurple_silcdir(), fingerprint
);
867 silc_free(fingerprint
);
870 /* Find buddy by associated public key */
871 for (buddies
= purple_blist_find_buddies(account
, NULL
); buddies
;
872 buddies
= g_slist_delete_link(buddies
, buddies
)) {
874 f
= purple_blist_node_get_string(PURPLE_BLIST_NODE(b
), "public-key");
875 if (purple_strequal(f
, buf
))
882 /* Find buddy by nickname */
883 b
= purple_blist_find_buddy(sg
->account
, client_entry
->nickname
);
885 purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
886 client_entry
->nickname
);
891 silc_free(purple_buddy_get_protocol_data(b
));
892 purple_buddy_set_protocol_data(b
, silc_memdup(&client_entry
->id
,
893 sizeof(client_entry
->id
)));
894 if (notify
== SILC_NOTIFY_TYPE_NICK_CHANGE
) {
896 } else if (notify
== SILC_NOTIFY_TYPE_UMODE_CHANGE
) {
897 /* See if client was away and is now present */
898 if (!(mode
& (SILC_UMODE_GONE
| SILC_UMODE_INDISPOSED
|
899 SILC_UMODE_BUSY
| SILC_UMODE_PAGE
|
900 SILC_UMODE_DETACHED
)) &&
901 (client_entry
->mode
& SILC_UMODE_GONE
||
902 client_entry
->mode
& SILC_UMODE_INDISPOSED
||
903 client_entry
->mode
& SILC_UMODE_BUSY
||
904 client_entry
->mode
& SILC_UMODE_PAGE
||
905 client_entry
->mode
& SILC_UMODE_DETACHED
)) {
906 client_entry
->mode
= mode
;
907 purple_protocol_got_user_status(purple_buddy_get_account(b
), purple_buddy_get_name(b
), SILCPURPLE_STATUS_ID_AVAILABLE
, NULL
);
909 else if ((mode
& SILC_UMODE_GONE
) ||
910 (mode
& SILC_UMODE_INDISPOSED
) ||
911 (mode
& SILC_UMODE_BUSY
) ||
912 (mode
& SILC_UMODE_PAGE
) ||
913 (mode
& SILC_UMODE_DETACHED
)) {
914 client_entry
->mode
= mode
;
915 purple_protocol_got_user_status(purple_buddy_get_account(b
), purple_buddy_get_name(b
), SILCPURPLE_STATUS_ID_OFFLINE
, NULL
);
917 } else if (notify
== SILC_NOTIFY_TYPE_SIGNOFF
||
918 notify
== SILC_NOTIFY_TYPE_SERVER_SIGNOFF
||
919 notify
== SILC_NOTIFY_TYPE_KILLED
) {
920 client_entry
->mode
= mode
;
921 purple_protocol_got_user_status(purple_buddy_get_account(b
), purple_buddy_get_name(b
), SILCPURPLE_STATUS_ID_OFFLINE
, NULL
);
922 } else if (notify
== SILC_NOTIFY_TYPE_NONE
) {
923 client_entry
->mode
= mode
;
924 purple_protocol_got_user_status(purple_buddy_get_account(b
), purple_buddy_get_name(b
), SILCPURPLE_STATUS_ID_AVAILABLE
, NULL
);
930 purple_debug_info("silc", "Unhandled notification: %d\n", type
);
938 /* Command handler. This function is called always after application has
939 called a command. It will be called to indicate that the command
940 was processed. It will also be called if error occurs while processing
941 the command. The `success' indicates whether the command was sent
942 or if error occurred. The `status' indicates the actual error.
943 The `argc' and `argv' are the command line arguments sent to the
944 command by application. Note that, this is not reply to the command
945 from server, this is merely and indication to application that the
946 command was processed. */
949 silc_command(SilcClient client
, SilcClientConnection conn
,
950 SilcBool success
, SilcCommand command
, SilcStatus status
,
951 SilcUInt32 argc
, unsigned char **argv
)
953 PurpleConnection
*gc
= client
->application
;
954 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
958 case SILC_COMMAND_CMODE
:
959 if (argc
== 3 && purple_strequal((char *)argv
[2], "+C"))
971 /* Command reply handler. Delivers a reply to command that was sent
972 earlier. The `conn' is the associated client connection. The `command'
973 indicates the command reply type. If the `status' other than
974 SILC_STATUS_OK an error occurred. In this case the `error' will indicate
975 the error. It is possible to receive list of command replies and list
976 of errors. In this case the `status' will indicate it is an list entry
977 (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or
978 SILC_STATUS_LIST_END).
980 The arguments received in `ap' are command specific. See a separate
981 documentation in the Toolkit Reference Manual for the command reply
985 silc_command_reply(SilcClient client
, SilcClientConnection conn
,
986 SilcCommand command
, SilcStatus status
,
987 SilcStatus error
, va_list ap
)
989 PurpleConnection
*gc
= client
->application
;
990 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
991 PurpleChatConversation
*chat
;
994 case SILC_COMMAND_JOIN
:
996 SilcChannelEntry channel
;
997 PurpleChatConversation
*chat
;
998 SilcHashTableList
*user_list
;
1000 GList
*users
= NULL
, *flags
= NULL
;
1001 char tmp
[256], *topic
;
1003 if (status
!= SILC_STATUS_OK
) {
1004 purple_notify_error(gc
, _("Join Chat"), _("Cannot join channel"),
1005 silc_get_status_message(error
),
1006 purple_request_cpar_from_connection(gc
));
1010 (void)va_arg(ap
, char *);
1011 channel
= va_arg(ap
, SilcChannelEntry
);
1012 (void)va_arg(ap
, SilcUInt32
);
1013 user_list
= va_arg(ap
, SilcHashTableList
*);
1014 topic
= va_arg(ap
, char *);
1016 /* Add channel to Purple */
1017 channel
->context
= SILC_32_TO_PTR(++sg
->channel_ids
);
1018 purple_serv_got_joined_chat(gc
, sg
->channel_ids
, channel
->channel_name
);
1019 chat
= purple_conversations_find_chat_with_account(
1020 channel
->channel_name
, sg
->account
);
1024 /* Add all users to channel */
1025 while (silc_hash_table_get(user_list
, NULL
, (void *)&chu
)) {
1026 PurpleChatUserFlags f
= PURPLE_CHAT_USER_NONE
;
1027 chu
->context
= SILC_32_TO_PTR(sg
->channel_ids
);
1029 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANFO
)
1030 f
|= PURPLE_CHAT_USER_FOUNDER
;
1031 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANOP
)
1032 f
|= PURPLE_CHAT_USER_OP
;
1033 users
= g_list_append(users
, chu
->client
->nickname
);
1034 flags
= g_list_append(flags
, GINT_TO_POINTER(f
));
1036 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANFO
) {
1037 if (chu
->client
== conn
->local_entry
)
1038 g_snprintf(tmp
, sizeof(tmp
),
1039 _("You are channel founder on <I>%s</I>"),
1040 channel
->channel_name
);
1042 g_snprintf(tmp
, sizeof(tmp
),
1043 _("Channel founder on <I>%s</I> is <I>%s</I>"),
1044 channel
->channel_name
, chu
->client
->nickname
);
1046 purple_conversation_write_system_message(
1047 PURPLE_CONVERSATION(chat
), tmp
, 0);
1051 purple_chat_conversation_add_users(chat
, users
, NULL
, flags
, FALSE
);
1057 purple_chat_conversation_set_topic(chat
, NULL
, topic
);
1060 purple_chat_conversation_set_nick(chat
, conn
->local_entry
->nickname
);
1064 case SILC_COMMAND_LEAVE
:
1067 case SILC_COMMAND_USERS
:
1070 case SILC_COMMAND_WHOIS
:
1072 SilcUInt32
*user_modes
;
1074 SilcClientEntry client_entry
;
1076 char *moodstr
, *statusstr
, *contactstr
, *langstr
, *devicestr
, *tzstr
, *geostr
;
1077 PurpleNotifyUserInfo
*user_info
;
1079 if (status
!= SILC_STATUS_OK
) {
1080 purple_notify_error(gc
, _("User Information"),
1081 _("Cannot get user information"),
1082 silc_get_status_message(error
),
1083 purple_request_cpar_from_connection(gc
));
1087 client_entry
= va_arg(ap
, SilcClientEntry
);
1088 (void)va_arg(ap
, char *);
1089 (void)va_arg(ap
, char *);
1090 (void)va_arg(ap
, char *);
1091 channels
= va_arg(ap
, SilcDList
);
1092 (void)va_arg(ap
, SilcUInt32
);
1093 (void)va_arg(ap
, SilcUInt32
); /* idle */
1094 (void)va_arg(ap
, unsigned char *);
1095 user_modes
= va_arg(ap
, SilcUInt32
*);
1097 user_info
= purple_notify_user_info_new();
1098 purple_notify_user_info_add_pair_plaintext(user_info
, _("Nickname"), client_entry
->nickname
);
1099 if (client_entry
->realname
) {
1100 purple_notify_user_info_add_pair_plaintext(user_info
, _("Real Name"), client_entry
->realname
);
1102 if (*client_entry
->hostname
) {
1104 tmp2
= g_strdup_printf("%s@%s", client_entry
->username
, client_entry
->hostname
);
1105 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), tmp2
);
1108 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), client_entry
->username
);
1110 if (client_entry
->mode
) {
1111 memset(tmp
, 0, sizeof(tmp
));
1112 silcpurple_get_umode_string(client_entry
->mode
,
1113 tmp
, sizeof(tmp
) - strlen(tmp
));
1114 /* TODO: Check whether it's correct to call add_pair_html,
1115 or if we should be using add_pair_plaintext */
1116 purple_notify_user_info_add_pair_html(user_info
, _("User Modes"), tmp
);
1119 silcpurple_parse_attrs(client_entry
->attrs
, &moodstr
, &statusstr
, &contactstr
, &langstr
, &devicestr
, &tzstr
, &geostr
);
1121 /* TODO: Check whether it's correct to call add_pair_html,
1122 or if we should be using add_pair_plaintext */
1123 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), moodstr
);
1128 purple_notify_user_info_add_pair_plaintext(user_info
, _("Status Text"), statusstr
);
1133 /* TODO: Check whether it's correct to call add_pair_html,
1134 or if we should be using add_pair_plaintext */
1135 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Contact"), contactstr
);
1140 /* TODO: Check whether it's correct to call add_pair_html,
1141 or if we should be using add_pair_plaintext */
1142 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Language"), langstr
);
1147 /* TODO: Check whether it's correct to call add_pair_html,
1148 or if we should be using add_pair_plaintext */
1149 purple_notify_user_info_add_pair_html(user_info
, _("Device"), devicestr
);
1154 /* TODO: Check whether it's correct to call add_pair_html,
1155 or if we should be using add_pair_plaintext */
1156 purple_notify_user_info_add_pair_html(user_info
, _("Timezone"), tzstr
);
1161 /* TODO: Check whether it's correct to call add_pair_html,
1162 or if we should be using add_pair_plaintext */
1163 purple_notify_user_info_add_pair_html(user_info
, _("Geolocation"), geostr
);
1167 if (*client_entry
->server
) {
1168 /* TODO: Check whether it's correct to call add_pair_html,
1169 or if we should be using add_pair_plaintext */
1170 purple_notify_user_info_add_pair_html(user_info
, _("Server"), client_entry
->server
);
1173 if (channels
&& user_modes
) {
1174 SilcChannelPayload entry
;
1177 memset(tmp
, 0, sizeof(tmp
));
1178 silc_dlist_start(channels
);
1179 while ((entry
= silc_dlist_get(channels
))) {
1180 SilcUInt32 name_len
;
1181 char *m
= silc_client_chumode_char(user_modes
[i
++]);
1182 char *name
= (char *)silc_channel_get_name(entry
, &name_len
);
1184 silc_strncat(tmp
, sizeof(tmp
) - 1, m
, strlen(m
));
1185 silc_strncat(tmp
, sizeof(tmp
) - 1, name
, name_len
);
1186 silc_strncat(tmp
, sizeof(tmp
) - 1, " ", 1);
1189 purple_notify_user_info_add_pair_plaintext(user_info
, _("Currently on"), tmp
);
1192 if (client_entry
->public_key
) {
1193 char *fingerprint
, *babbleprint
;
1196 pk
= silc_pkcs_public_key_encode(client_entry
->public_key
, &pk_len
);
1198 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
1199 babbleprint
= silc_hash_babbleprint(NULL
, pk
, pk_len
);
1200 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Fingerprint"), fingerprint
);
1201 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Babbleprint"), babbleprint
);
1202 silc_free(fingerprint
);
1203 silc_free(babbleprint
);
1208 purple_notify_userinfo(gc
, client_entry
->nickname
, user_info
, NULL
, NULL
);
1209 purple_notify_user_info_destroy(user_info
);
1213 case SILC_COMMAND_WHOWAS
:
1215 SilcClientEntry client_entry
;
1216 char *nickname
, *realname
, *username
;
1217 PurpleNotifyUserInfo
*user_info
;
1219 if (status
!= SILC_STATUS_OK
) {
1220 purple_notify_error(gc
, _("User Information"),
1221 _("Cannot get user information"),
1222 silc_get_status_message(error
),
1223 purple_request_cpar_from_connection(gc
));
1227 client_entry
= va_arg(ap
, SilcClientEntry
);
1228 nickname
= va_arg(ap
, char *);
1229 username
= va_arg(ap
, char *);
1230 realname
= va_arg(ap
, char *);
1234 user_info
= purple_notify_user_info_new();
1235 purple_notify_user_info_add_pair_plaintext(user_info
, _("Nickname"), nickname
);
1237 purple_notify_user_info_add_pair_plaintext(user_info
, _("Real Name"), realname
);
1239 if (client_entry
&& *client_entry
->hostname
) {
1241 tmp
= g_strdup_printf("%s@%s", username
, client_entry
->hostname
);
1242 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), tmp
);
1245 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), username
);
1247 if (client_entry
&& *client_entry
->server
) {
1248 /* TODO: Check whether it's correct to call add_pair_html,
1249 or if we should be using add_pair_plaintext */
1250 purple_notify_user_info_add_pair_html(user_info
, _("Server"), client_entry
->server
);
1254 if (client_entry
&& client_entry
->public_key
) {
1255 char *fingerprint
, *babbleprint
;
1258 pk
= silc_pkcs_public_key_encode(client_entry
->public_key
, &pk_len
);
1260 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
1261 babbleprint
= silc_hash_babbleprint(NULL
, pk
, pk_len
);
1262 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Fingerprint"), fingerprint
);
1263 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Babbleprint"), babbleprint
);
1264 silc_free(fingerprint
);
1265 silc_free(babbleprint
);
1270 purple_notify_userinfo(gc
, nickname
, user_info
, NULL
, NULL
);
1271 purple_notify_user_info_destroy(user_info
);
1275 case SILC_COMMAND_DETACH
:
1278 SilcBuffer detach_data
;
1280 if (status
!= SILC_STATUS_OK
) {
1281 purple_notify_error(gc
, _("Detach From Server"), _("Cannot detach"),
1282 silc_get_status_message(error
),
1283 purple_request_cpar_from_connection(gc
));
1287 detach_data
= va_arg(ap
, SilcBuffer
);
1289 /* Save the detachment data to file. */
1290 file
= silcpurple_session_file(purple_account_get_username(sg
->account
));
1292 silc_file_writefile(file
, (const char *)silc_buffer_data(detach_data
),
1293 silc_buffer_len(detach_data
));
1297 case SILC_COMMAND_TOPIC
:
1299 SilcChannelEntry channel
;
1301 if (status
!= SILC_STATUS_OK
) {
1302 purple_notify_error(gc
, _("Topic"), _("Cannot set topic"),
1303 silc_get_status_message(error
),
1304 purple_request_cpar_from_connection(gc
));
1308 channel
= va_arg(ap
, SilcChannelEntry
);
1310 chat
= purple_conversations_find_chat_with_account(
1311 channel
->channel_name
, sg
->account
);
1313 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1314 channel
->channel_name
);
1320 purple_chat_conversation_set_topic(chat
, NULL
, channel
->topic
);
1324 case SILC_COMMAND_NICK
:
1326 SilcClientEntry local_entry
;
1327 SilcHashTableList htl
;
1328 SilcChannelUser chu
;
1329 const char *oldnick
, *newnick
;
1331 if (status
!= SILC_STATUS_OK
) {
1332 purple_notify_error(gc
, _("Nick"), _("Failed to change nickname"),
1333 silc_get_status_message(error
),
1334 purple_request_cpar_from_connection(gc
));
1338 local_entry
= va_arg(ap
, SilcClientEntry
);
1339 newnick
= va_arg(ap
, char *);
1341 /* Change nick on all channels */
1342 silc_hash_table_list(local_entry
->channels
, &htl
);
1343 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
1344 chat
= purple_conversations_find_chat_with_account(
1345 chu
->channel
->channel_name
, sg
->account
);
1348 oldnick
= purple_chat_conversation_get_nick(chat
);
1349 if (!purple_strequal(oldnick
,
1350 purple_normalize(purple_conversation_get_account
1351 (PURPLE_CONVERSATION(chat
)), newnick
))) {
1353 purple_chat_conversation_rename_user(chat
,
1355 purple_chat_conversation_set_nick(chat
, newnick
);
1358 silc_hash_table_list_reset(&htl
);
1360 purple_connection_set_display_name(gc
, newnick
);
1364 case SILC_COMMAND_LIST
:
1368 PurpleRoomlistRoom
*room
;
1370 if (sg
->roomlist_cancelled
)
1373 if (error
!= SILC_STATUS_OK
) {
1374 purple_notify_error(gc
, _("Error"), _("Error retrieving room list"),
1375 silc_get_status_message(error
),
1376 purple_request_cpar_from_connection(gc
));
1377 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1378 g_object_unref(sg
->roomlist
);
1379 sg
->roomlist
= NULL
;
1383 (void)va_arg(ap
, SilcChannelEntry
);
1384 name
= va_arg(ap
, char *);
1386 purple_notify_error(gc
, _("Roomlist"), _("Cannot get room list"),
1387 _("Network is empty"),
1388 purple_request_cpar_from_connection(gc
));
1389 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1390 g_object_unref(sg
->roomlist
);
1391 sg
->roomlist
= NULL
;
1394 topic
= va_arg(ap
, char *);
1395 usercount
= va_arg(ap
, int);
1397 room
= purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM
, name
, NULL
);
1398 purple_roomlist_room_add_field(sg
->roomlist
, room
, name
);
1399 purple_roomlist_room_add_field(sg
->roomlist
, room
,
1400 SILC_32_TO_PTR(usercount
));
1401 purple_roomlist_room_add_field(sg
->roomlist
, room
,
1402 topic
? topic
: "");
1403 purple_roomlist_room_add(sg
->roomlist
, room
);
1405 if (status
== SILC_STATUS_LIST_END
||
1406 status
== SILC_STATUS_OK
) {
1407 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1408 g_object_unref(sg
->roomlist
);
1409 sg
->roomlist
= NULL
;
1414 case SILC_COMMAND_GETKEY
:
1416 SilcPublicKey public_key
;
1418 if (status
!= SILC_STATUS_OK
) {
1419 purple_notify_error(gc
, _("Get Public Key"),
1420 _("Cannot fetch the public key"),
1421 silc_get_status_message(error
),
1422 purple_request_cpar_from_connection(gc
));
1426 (void)va_arg(ap
, SilcUInt32
);
1427 (void)va_arg(ap
, void *);
1428 public_key
= va_arg(ap
, SilcPublicKey
);
1431 purple_notify_error(gc
, _("Get Public Key"),
1432 _("Cannot fetch the public key"),
1433 _("No public key was received"),
1434 purple_request_cpar_from_connection(gc
));
1438 case SILC_COMMAND_INFO
:
1445 if (status
!= SILC_STATUS_OK
) {
1446 purple_notify_error(gc
, _("Server Information"),
1447 _("Cannot get server information"),
1448 silc_get_status_message(error
),
1449 purple_request_cpar_from_connection(gc
));
1453 (void)va_arg(ap
, SilcServerEntry
);
1454 server_name
= va_arg(ap
, char *);
1455 server_info
= va_arg(ap
, char *);
1457 if (server_name
&& server_info
) {
1458 g_snprintf(tmp
, sizeof(tmp
), "Server: %s\n%s",
1459 server_name
, server_info
);
1460 purple_notify_info(gc
, NULL
, _("Server Information"), tmp
,
1461 purple_request_cpar_from_connection(gc
));
1466 case SILC_COMMAND_STATS
:
1468 SilcClientStats
*stats
;
1471 if (status
!= SILC_STATUS_OK
) {
1472 purple_notify_error(gc
, _("Server Statistics"),
1473 _("Cannot get server statistics"),
1474 silc_get_status_message(error
),
1475 purple_request_cpar_from_connection(gc
));
1479 stats
= va_arg(ap
, SilcClientStats
*);
1481 msg
= g_strdup_printf(_("Local server start time: %s\n"
1482 "Local server uptime: %s\n"
1483 "Local server clients: %d\n"
1484 "Local server channels: %d\n"
1485 "Local server operators: %d\n"
1486 "Local router operators: %d\n"
1487 "Local cell clients: %d\n"
1488 "Local cell channels: %d\n"
1489 "Local cell servers: %d\n"
1490 "Total clients: %d\n"
1491 "Total channels: %d\n"
1492 "Total servers: %d\n"
1493 "Total routers: %d\n"
1494 "Total server operators: %d\n"
1495 "Total router operators: %d\n"),
1496 silc_time_string(stats
->starttime
),
1497 purple_str_seconds_to_string((int)stats
->uptime
),
1498 (int)stats
->my_clients
,
1499 (int)stats
->my_channels
,
1500 (int)stats
->my_server_ops
,
1501 (int)stats
->my_router_ops
,
1502 (int)stats
->cell_clients
,
1503 (int)stats
->cell_channels
,
1504 (int)stats
->cell_servers
,
1505 (int)stats
->clients
,
1506 (int)stats
->channels
,
1507 (int)stats
->servers
,
1508 (int)stats
->routers
,
1509 (int)stats
->server_ops
,
1510 (int)stats
->router_ops
);
1512 purple_notify_info(gc
, NULL
,
1513 _("Network Statistics"), msg
,
1514 purple_request_cpar_from_connection(gc
));
1519 case SILC_COMMAND_PING
:
1521 if (status
!= SILC_STATUS_OK
) {
1522 purple_notify_error(gc
, _("Ping"), _("Ping failed"),
1523 silc_get_status_message(error
),
1524 purple_request_cpar_from_connection(gc
));
1528 purple_notify_info(gc
, _("Ping"), _("Ping reply received from server"),
1529 NULL
, purple_request_cpar_from_connection(gc
));
1533 case SILC_COMMAND_KILL
:
1534 if (status
!= SILC_STATUS_OK
) {
1535 purple_notify_error(gc
, _("Kill User"),
1536 _("Could not kill user"),
1537 silc_get_status_message(error
),
1538 purple_request_cpar_from_connection(gc
));
1543 case SILC_COMMAND_CMODE
:
1545 SilcChannelEntry channel_entry
;
1546 SilcDList channel_pubkeys
, list
;
1547 SilcArgumentDecodedList e
;
1549 if (status
!= SILC_STATUS_OK
)
1552 channel_entry
= va_arg(ap
, SilcChannelEntry
);
1553 (void)va_arg(ap
, SilcUInt32
);
1554 (void)va_arg(ap
, SilcPublicKey
);
1555 channel_pubkeys
= va_arg(ap
, SilcDList
);
1560 list
= silc_dlist_init();
1562 if (channel_pubkeys
) {
1563 silc_dlist_start(channel_pubkeys
);
1564 while ((e
= silc_dlist_get(channel_pubkeys
))) {
1565 if (e
->arg_type
== 0x00 ||
1566 e
->arg_type
== 0x03)
1567 silc_dlist_add(list
, silc_pkcs_public_key_copy(e
->argument
));
1570 silcpurple_chat_chauth_show(sg
, channel_entry
, list
);
1574 case SILC_COMMAND_WATCH
:
1575 if (status
!= SILC_STATUS_OK
) {
1576 purple_notify_error(gc
, _("WATCH"), _("Cannot watch user"),
1577 silc_get_status_message(error
),
1578 purple_request_cpar_from_connection(gc
));
1584 if (status
== SILC_STATUS_OK
)
1585 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command
);
1587 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command
,
1588 silc_get_status_message(error
));
1593 /* Generic command reply callback for silc_client_command_send. Simply
1594 calls the default command_reply client operation callback */
1596 SilcBool
silcpurple_command_reply(SilcClient client
, SilcClientConnection conn
,
1597 SilcCommand command
, SilcStatus status
,
1598 SilcStatus error
, void *context
, va_list ap
)
1600 silc_command_reply(client
, conn
, command
, status
, error
, ap
);
1607 SilcAskPassphrase ask_pass
;
1608 SilcGetAuthMeth get_auth
;
1611 } *SilcPurpleAskPassphrase
;
1614 silc_ask_auth_password_cb(const unsigned char *passphrase
,
1615 SilcUInt32 passphrase_len
, void *context
)
1617 SilcPurpleAskPassphrase internal
= context
;
1619 if (!passphrase
|| !(*passphrase
))
1620 internal
->u
.get_auth(SILC_AUTH_NONE
, NULL
, 0, internal
->context
);
1622 internal
->u
.get_auth(SILC_AUTH_PASSWORD
,
1623 (unsigned char *)passphrase
,
1624 passphrase_len
, internal
->context
);
1625 silc_free(internal
);
1628 /* Find authentication method and authentication data by hostname and
1629 port. The hostname may be IP address as well. The `auth_method' is
1630 the authentication method the remote connection requires. It is
1631 however possible that remote accepts also some other authentication
1632 method. Application should use the method that may have been
1633 configured for this connection. If none has been configured it should
1634 use the required `auth_method'. If the `auth_method' is
1635 SILC_AUTH_NONE, server does not require any authentication or the
1636 required authentication method is not known. The `completion'
1637 callback must be called to deliver the chosen authentication method
1638 and data. The `conn' may be NULL. */
1641 silc_get_auth_method(SilcClient client
, SilcClientConnection conn
,
1642 char *hostname
, SilcUInt16 port
,
1643 SilcAuthMethod auth_method
,
1644 SilcGetAuthMeth completion
, void *context
)
1646 PurpleConnection
*gc
= client
->application
;
1647 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1648 SilcPurpleAskPassphrase internal
;
1649 const char *password
;
1653 purple_connection_update_progress(gc
, _("Resuming session"), 4, 5);
1655 purple_connection_update_progress(gc
, _("Authenticating connection"), 4, 5);
1657 /* Check configuration if we have this connection configured. */
1658 if (auth_method
== SILC_AUTH_PUBLIC_KEY
&&
1659 purple_account_get_bool(sg
->account
, "pubkey-auth", FALSE
)) {
1660 completion(SILC_AUTH_PUBLIC_KEY
, NULL
, 0, context
);
1663 if (auth_method
== SILC_AUTH_PASSWORD
) {
1664 password
= purple_connection_get_password(gc
);
1665 if (password
&& *password
) {
1666 completion(SILC_AUTH_PASSWORD
, (unsigned char *)password
, strlen(password
), context
);
1670 /* Ask password from user */
1671 internal
= silc_calloc(1, sizeof(*internal
));
1674 internal
->u
.get_auth
= completion
;
1675 internal
->context
= context
;
1676 silc_ask_passphrase(client
, conn
, silc_ask_auth_password_cb
,
1681 completion(SILC_AUTH_NONE
, NULL
, 0, context
);
1685 /* Called to verify received public key. The `conn_type' indicates which
1686 entity (server or client) has sent the public key. If user decides to
1687 trust the key the application may save the key as trusted public key for
1688 later use. The `completion' must be called after the public key has
1692 silc_verify_public_key(SilcClient client
, SilcClientConnection conn
,
1693 SilcConnectionType conn_type
,
1694 SilcPublicKey public_key
,
1695 SilcVerifyPublicKey completion
, void *context
)
1697 PurpleConnection
*gc
= client
->application
;
1698 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1700 if (!sg
->conn
&& (conn_type
== SILC_CONN_SERVER
||
1701 conn_type
== SILC_CONN_ROUTER
)) {
1704 purple_connection_update_progress(gc
, _("Resuming session"), 3, 5);
1706 purple_connection_update_progress(gc
, _("Verifying server public key"),
1710 /* Verify public key */
1711 silcpurple_verify_public_key(client
, conn
, NULL
, conn_type
,
1712 public_key
, completion
, context
);
1716 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal
, const char *passphrase
)
1718 if (!passphrase
|| !(*passphrase
))
1719 internal
->u
.ask_pass(NULL
, 0, internal
->context
);
1721 internal
->u
.ask_pass((unsigned char *)passphrase
,
1722 strlen(passphrase
), internal
->context
);
1723 silc_free(internal
);
1726 /* Ask (interact, that is) a passphrase from user. The passphrase is
1727 returned to the library by calling the `completion' callback with
1728 the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1729 if not then the library will attempt to encode. */
1732 silc_ask_passphrase(SilcClient client
, SilcClientConnection conn
,
1733 SilcAskPassphrase completion
, void *context
)
1735 PurpleConnection
*gc
= client
->application
;
1736 SilcPurpleAskPassphrase internal
= silc_calloc(1, sizeof(*internal
));
1740 internal
->u
.ask_pass
= completion
;
1741 internal
->context
= context
;
1742 purple_request_input(gc
, _("Passphrase"), NULL
,
1743 _("Passphrase required"), NULL
, FALSE
, TRUE
, NULL
,
1744 _("OK"), G_CALLBACK(silc_ask_passphrase_cb
),
1745 _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb
),
1746 purple_request_cpar_from_connection(gc
), internal
);
1750 /* Called to indicate that incoming key agreement request has been
1751 received. If the application wants to perform key agreement it may
1752 call silc_client_perform_key_agreement to initiate key agreement or
1753 silc_client_send_key_agreement to provide connection point to the
1754 remote client in case the `hostname' is NULL. If key agreement is
1755 not desired this request can be ignored. The `protocol' is either
1756 value 0 for TCP or value 1 for UDP. */
1759 silc_key_agreement(SilcClient client
, SilcClientConnection conn
,
1760 SilcClientEntry client_entry
,
1761 const char *hostname
, SilcUInt16 protocol
,
1764 silcpurple_buddy_keyagr_request(client
, conn
, client_entry
,
1765 hostname
, port
, protocol
);
1769 /* Notifies application that file transfer protocol session is being
1770 requested by the remote client indicated by the `client_entry' from
1771 the `hostname' and `port'. The `session_id' is the file transfer
1772 session and it can be used to either accept or reject the file
1773 transfer request, by calling the silc_client_file_receive or
1774 silc_client_file_close, respectively. */
1777 silc_ftp(SilcClient client
, SilcClientConnection conn
,
1778 SilcClientEntry client_entry
, SilcUInt32 session_id
,
1779 const char *hostname
, SilcUInt16 port
)
1781 silcpurple_ftp_request(client
, conn
, client_entry
, session_id
,
1785 SilcClientOperations ops
= {
1787 silc_channel_message
,
1788 silc_private_message
,
1792 silc_get_auth_method
,
1793 silc_verify_public_key
,
1794 silc_ask_passphrase
,