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"))
972 silcpurple_whois_more(SilcClientEntry client_entry
, gint id
)
974 SilcAttributePayload attr
;
975 SilcAttribute attribute
;
977 SilcVCardStruct vcard
;
983 memset(&vcard
, 0, sizeof(vcard
));
985 s
= g_string_new("");
987 silc_dlist_start(client_entry
->attrs
);
988 while ((attr
= silc_dlist_get(client_entry
->attrs
)) != SILC_LIST_END
) {
989 attribute
= silc_attribute_get_attribute(attr
);
992 case SILC_ATTRIBUTE_USER_INFO
:
993 if (!silc_attribute_get_object(attr
, (void *)&vcard
,
996 g_string_append_printf(s
, "%s:\n\n", _("Personal Information"));
998 g_string_append_printf(s
, "%s:\t\t%s\n",
1001 if (vcard
.first_name
)
1002 g_string_append_printf(s
, "%s:\t%s\n",
1005 if (vcard
.middle_names
)
1006 g_string_append_printf(s
, "%s:\t%s\n",
1008 vcard
.middle_names
);
1009 if (vcard
.family_name
)
1010 g_string_append_printf(s
, "%s:\t%s\n",
1014 g_string_append_printf(s
, "%s:\t\t%s\n",
1018 g_string_append_printf(s
, "%s:\t\t%s\n",
1022 g_string_append_printf(s
, "%s:\t\t%s\n",
1026 g_string_append_printf(s
, "%s:\t\t%s\n",
1030 g_string_append_printf(s
, "%s:\t%s\n",
1034 g_string_append_printf(s
, "%s:\t\t%s\n",
1038 g_string_append_printf(s
, "%s:\t%s\n",
1042 g_string_append_printf(s
, "%s:\t%s\n",
1045 for (i
= 0; i
< vcard
.num_tels
; i
++) {
1046 if (vcard
.tels
[i
].telnum
)
1047 g_string_append_printf(s
, "%s:\t\t\t%s\n",
1049 vcard
.tels
[i
].telnum
);
1051 for (i
= 0; i
< vcard
.num_emails
; i
++) {
1052 if (vcard
.emails
[i
].address
)
1053 g_string_append_printf(s
, "%s:\t\t%s\n",
1055 vcard
.emails
[i
].address
);
1058 g_string_append_printf(s
, "\n%s:\t\t%s\n",
1065 purple_notify_info(NULL
, _("User Information"), _("User Information"),
1067 g_string_free(s
, TRUE
);
1072 /* Command reply handler. Delivers a reply to command that was sent
1073 earlier. The `conn' is the associated client connection. The `command'
1074 indicates the command reply type. If the `status' other than
1075 SILC_STATUS_OK an error occurred. In this case the `error' will indicate
1076 the error. It is possible to receive list of command replies and list
1077 of errors. In this case the `status' will indicate it is an list entry
1078 (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or
1079 SILC_STATUS_LIST_END).
1081 The arguments received in `ap' are command specific. See a separate
1082 documentation in the Toolkit Reference Manual for the command reply
1086 silc_command_reply(SilcClient client
, SilcClientConnection conn
,
1087 SilcCommand command
, SilcStatus status
,
1088 SilcStatus error
, va_list ap
)
1090 PurpleConnection
*gc
= client
->application
;
1091 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1092 PurpleChatConversation
*chat
;
1095 case SILC_COMMAND_JOIN
:
1097 SilcChannelEntry channel
;
1098 PurpleChatConversation
*chat
;
1099 SilcHashTableList
*user_list
;
1100 SilcChannelUser chu
;
1101 GList
*users
= NULL
, *flags
= NULL
;
1102 char tmp
[256], *topic
;
1104 if (status
!= SILC_STATUS_OK
) {
1105 purple_notify_error(gc
, _("Join Chat"), _("Cannot join channel"),
1106 silc_get_status_message(error
),
1107 purple_request_cpar_from_connection(gc
));
1111 (void)va_arg(ap
, char *);
1112 channel
= va_arg(ap
, SilcChannelEntry
);
1113 (void)va_arg(ap
, SilcUInt32
);
1114 user_list
= va_arg(ap
, SilcHashTableList
*);
1115 topic
= va_arg(ap
, char *);
1117 /* Add channel to Purple */
1118 channel
->context
= SILC_32_TO_PTR(++sg
->channel_ids
);
1119 purple_serv_got_joined_chat(gc
, sg
->channel_ids
, channel
->channel_name
);
1120 chat
= purple_conversations_find_chat_with_account(
1121 channel
->channel_name
, sg
->account
);
1125 /* Add all users to channel */
1126 while (silc_hash_table_get(user_list
, NULL
, (void *)&chu
)) {
1127 PurpleChatUserFlags f
= PURPLE_CHAT_USER_NONE
;
1128 chu
->context
= SILC_32_TO_PTR(sg
->channel_ids
);
1130 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANFO
)
1131 f
|= PURPLE_CHAT_USER_FOUNDER
;
1132 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANOP
)
1133 f
|= PURPLE_CHAT_USER_OP
;
1134 users
= g_list_append(users
, chu
->client
->nickname
);
1135 flags
= g_list_append(flags
, GINT_TO_POINTER(f
));
1137 if (chu
->mode
& SILC_CHANNEL_UMODE_CHANFO
) {
1138 if (chu
->client
== conn
->local_entry
)
1139 g_snprintf(tmp
, sizeof(tmp
),
1140 _("You are channel founder on <I>%s</I>"),
1141 channel
->channel_name
);
1143 g_snprintf(tmp
, sizeof(tmp
),
1144 _("Channel founder on <I>%s</I> is <I>%s</I>"),
1145 channel
->channel_name
, chu
->client
->nickname
);
1147 purple_conversation_write_system_message(
1148 PURPLE_CONVERSATION(chat
), tmp
, 0);
1152 purple_chat_conversation_add_users(chat
, users
, NULL
, flags
, FALSE
);
1158 purple_chat_conversation_set_topic(chat
, NULL
, topic
);
1161 purple_chat_conversation_set_nick(chat
, conn
->local_entry
->nickname
);
1165 case SILC_COMMAND_LEAVE
:
1168 case SILC_COMMAND_USERS
:
1171 case SILC_COMMAND_WHOIS
:
1173 SilcUInt32
*user_modes
;
1175 SilcClientEntry client_entry
;
1177 char *moodstr
, *statusstr
, *contactstr
, *langstr
, *devicestr
, *tzstr
, *geostr
;
1178 PurpleNotifyUserInfo
*user_info
;
1180 if (status
!= SILC_STATUS_OK
) {
1181 purple_notify_error(gc
, _("User Information"),
1182 _("Cannot get user information"),
1183 silc_get_status_message(error
),
1184 purple_request_cpar_from_connection(gc
));
1188 client_entry
= va_arg(ap
, SilcClientEntry
);
1189 (void)va_arg(ap
, char *);
1190 (void)va_arg(ap
, char *);
1191 (void)va_arg(ap
, char *);
1192 channels
= va_arg(ap
, SilcDList
);
1193 (void)va_arg(ap
, SilcUInt32
);
1194 (void)va_arg(ap
, SilcUInt32
); /* idle */
1195 (void)va_arg(ap
, unsigned char *);
1196 user_modes
= va_arg(ap
, SilcUInt32
*);
1198 user_info
= purple_notify_user_info_new();
1199 purple_notify_user_info_add_pair_plaintext(user_info
, _("Nickname"), client_entry
->nickname
);
1200 if (client_entry
->realname
) {
1201 purple_notify_user_info_add_pair_plaintext(user_info
, _("Real Name"), client_entry
->realname
);
1203 if (*client_entry
->hostname
) {
1205 tmp2
= g_strdup_printf("%s@%s", client_entry
->username
, client_entry
->hostname
);
1206 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), tmp2
);
1209 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), client_entry
->username
);
1211 if (client_entry
->mode
) {
1212 memset(tmp
, 0, sizeof(tmp
));
1213 silcpurple_get_umode_string(client_entry
->mode
,
1214 tmp
, sizeof(tmp
) - strlen(tmp
));
1215 /* TODO: Check whether it's correct to call add_pair_html,
1216 or if we should be using add_pair_plaintext */
1217 purple_notify_user_info_add_pair_html(user_info
, _("User Modes"), tmp
);
1220 silcpurple_parse_attrs(client_entry
->attrs
, &moodstr
, &statusstr
, &contactstr
, &langstr
, &devicestr
, &tzstr
, &geostr
);
1222 /* TODO: Check whether it's correct to call add_pair_html,
1223 or if we should be using add_pair_plaintext */
1224 purple_notify_user_info_add_pair_html(user_info
, _("Mood"), moodstr
);
1229 purple_notify_user_info_add_pair_plaintext(user_info
, _("Status Text"), statusstr
);
1234 /* TODO: Check whether it's correct to call add_pair_html,
1235 or if we should be using add_pair_plaintext */
1236 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Contact"), contactstr
);
1241 /* TODO: Check whether it's correct to call add_pair_html,
1242 or if we should be using add_pair_plaintext */
1243 purple_notify_user_info_add_pair_html(user_info
, _("Preferred Language"), langstr
);
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
, _("Device"), devicestr
);
1255 /* TODO: Check whether it's correct to call add_pair_html,
1256 or if we should be using add_pair_plaintext */
1257 purple_notify_user_info_add_pair_html(user_info
, _("Timezone"), tzstr
);
1262 /* TODO: Check whether it's correct to call add_pair_html,
1263 or if we should be using add_pair_plaintext */
1264 purple_notify_user_info_add_pair_html(user_info
, _("Geolocation"), geostr
);
1268 if (*client_entry
->server
) {
1269 /* TODO: Check whether it's correct to call add_pair_html,
1270 or if we should be using add_pair_plaintext */
1271 purple_notify_user_info_add_pair_html(user_info
, _("Server"), client_entry
->server
);
1274 if (channels
&& user_modes
) {
1275 SilcChannelPayload entry
;
1278 memset(tmp
, 0, sizeof(tmp
));
1279 silc_dlist_start(channels
);
1280 while ((entry
= silc_dlist_get(channels
))) {
1281 SilcUInt32 name_len
;
1282 char *m
= silc_client_chumode_char(user_modes
[i
++]);
1283 char *name
= (char *)silc_channel_get_name(entry
, &name_len
);
1285 silc_strncat(tmp
, sizeof(tmp
) - 1, m
, strlen(m
));
1286 silc_strncat(tmp
, sizeof(tmp
) - 1, name
, name_len
);
1287 silc_strncat(tmp
, sizeof(tmp
) - 1, " ", 1);
1290 purple_notify_user_info_add_pair_plaintext(user_info
, _("Currently on"), tmp
);
1293 if (client_entry
->public_key
) {
1294 char *fingerprint
, *babbleprint
;
1297 pk
= silc_pkcs_public_key_encode(client_entry
->public_key
, &pk_len
);
1299 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
1300 babbleprint
= silc_hash_babbleprint(NULL
, pk
, pk_len
);
1301 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Fingerprint"), fingerprint
);
1302 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Babbleprint"), babbleprint
);
1303 silc_free(fingerprint
);
1304 silc_free(babbleprint
);
1309 #if 0 /* XXX for now, let's not show attrs here */
1310 if (client_entry
->attrs
)
1311 purple_request_action(gc
, _("User Information"),
1312 _("User Information"),
1313 buf
, 1, client_entry
, 2,
1314 _("OK"), G_CALLBACK(silcpurple_whois_more
),
1315 _("_More..."), G_CALLBACK(silcpurple_whois_more
), gc
->account
, NULL
, NULL
);
1318 purple_notify_userinfo(gc
, client_entry
->nickname
, user_info
, NULL
, NULL
);
1319 purple_notify_user_info_destroy(user_info
);
1323 case SILC_COMMAND_WHOWAS
:
1325 SilcClientEntry client_entry
;
1326 char *nickname
, *realname
, *username
;
1327 PurpleNotifyUserInfo
*user_info
;
1329 if (status
!= SILC_STATUS_OK
) {
1330 purple_notify_error(gc
, _("User Information"),
1331 _("Cannot get user information"),
1332 silc_get_status_message(error
),
1333 purple_request_cpar_from_connection(gc
));
1337 client_entry
= va_arg(ap
, SilcClientEntry
);
1338 nickname
= va_arg(ap
, char *);
1339 username
= va_arg(ap
, char *);
1340 realname
= va_arg(ap
, char *);
1344 user_info
= purple_notify_user_info_new();
1345 purple_notify_user_info_add_pair_plaintext(user_info
, _("Nickname"), nickname
);
1347 purple_notify_user_info_add_pair_plaintext(user_info
, _("Real Name"), realname
);
1349 if (client_entry
&& *client_entry
->hostname
) {
1351 tmp
= g_strdup_printf("%s@%s", username
, client_entry
->hostname
);
1352 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), tmp
);
1355 purple_notify_user_info_add_pair_plaintext(user_info
, _("Username"), username
);
1357 if (client_entry
&& *client_entry
->server
) {
1358 /* TODO: Check whether it's correct to call add_pair_html,
1359 or if we should be using add_pair_plaintext */
1360 purple_notify_user_info_add_pair_html(user_info
, _("Server"), client_entry
->server
);
1364 if (client_entry
&& client_entry
->public_key
) {
1365 char *fingerprint
, *babbleprint
;
1368 pk
= silc_pkcs_public_key_encode(client_entry
->public_key
, &pk_len
);
1370 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
1371 babbleprint
= silc_hash_babbleprint(NULL
, pk
, pk_len
);
1372 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Fingerprint"), fingerprint
);
1373 purple_notify_user_info_add_pair_plaintext(user_info
, _("Public Key Babbleprint"), babbleprint
);
1374 silc_free(fingerprint
);
1375 silc_free(babbleprint
);
1380 purple_notify_userinfo(gc
, nickname
, user_info
, NULL
, NULL
);
1381 purple_notify_user_info_destroy(user_info
);
1385 case SILC_COMMAND_DETACH
:
1388 SilcBuffer detach_data
;
1390 if (status
!= SILC_STATUS_OK
) {
1391 purple_notify_error(gc
, _("Detach From Server"), _("Cannot detach"),
1392 silc_get_status_message(error
),
1393 purple_request_cpar_from_connection(gc
));
1397 detach_data
= va_arg(ap
, SilcBuffer
);
1399 /* Save the detachment data to file. */
1400 file
= silcpurple_session_file(purple_account_get_username(sg
->account
));
1402 silc_file_writefile(file
, (const char *)silc_buffer_data(detach_data
),
1403 silc_buffer_len(detach_data
));
1407 case SILC_COMMAND_TOPIC
:
1409 SilcChannelEntry channel
;
1411 if (status
!= SILC_STATUS_OK
) {
1412 purple_notify_error(gc
, _("Topic"), _("Cannot set topic"),
1413 silc_get_status_message(error
),
1414 purple_request_cpar_from_connection(gc
));
1418 channel
= va_arg(ap
, SilcChannelEntry
);
1420 chat
= purple_conversations_find_chat_with_account(
1421 channel
->channel_name
, sg
->account
);
1423 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1424 channel
->channel_name
);
1430 purple_chat_conversation_set_topic(chat
, NULL
, channel
->topic
);
1434 case SILC_COMMAND_NICK
:
1436 SilcClientEntry local_entry
;
1437 SilcHashTableList htl
;
1438 SilcChannelUser chu
;
1439 const char *oldnick
, *newnick
;
1441 if (status
!= SILC_STATUS_OK
) {
1442 purple_notify_error(gc
, _("Nick"), _("Failed to change nickname"),
1443 silc_get_status_message(error
),
1444 purple_request_cpar_from_connection(gc
));
1448 local_entry
= va_arg(ap
, SilcClientEntry
);
1449 newnick
= va_arg(ap
, char *);
1451 /* Change nick on all channels */
1452 silc_hash_table_list(local_entry
->channels
, &htl
);
1453 while (silc_hash_table_get(&htl
, NULL
, (void *)&chu
)) {
1454 chat
= purple_conversations_find_chat_with_account(
1455 chu
->channel
->channel_name
, sg
->account
);
1458 oldnick
= purple_chat_conversation_get_nick(chat
);
1459 if (!purple_strequal(oldnick
,
1460 purple_normalize(purple_conversation_get_account
1461 (PURPLE_CONVERSATION(chat
)), newnick
))) {
1463 purple_chat_conversation_rename_user(chat
,
1465 purple_chat_conversation_set_nick(chat
, newnick
);
1468 silc_hash_table_list_reset(&htl
);
1470 purple_connection_set_display_name(gc
, newnick
);
1474 case SILC_COMMAND_LIST
:
1478 PurpleRoomlistRoom
*room
;
1480 if (sg
->roomlist_cancelled
)
1483 if (error
!= SILC_STATUS_OK
) {
1484 purple_notify_error(gc
, _("Error"), _("Error retrieving room list"),
1485 silc_get_status_message(error
),
1486 purple_request_cpar_from_connection(gc
));
1487 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1488 g_object_unref(sg
->roomlist
);
1489 sg
->roomlist
= NULL
;
1493 (void)va_arg(ap
, SilcChannelEntry
);
1494 name
= va_arg(ap
, char *);
1496 purple_notify_error(gc
, _("Roomlist"), _("Cannot get room list"),
1497 _("Network is empty"),
1498 purple_request_cpar_from_connection(gc
));
1499 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1500 g_object_unref(sg
->roomlist
);
1501 sg
->roomlist
= NULL
;
1504 topic
= va_arg(ap
, char *);
1505 usercount
= va_arg(ap
, int);
1507 room
= purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM
, name
, NULL
);
1508 purple_roomlist_room_add_field(sg
->roomlist
, room
, name
);
1509 purple_roomlist_room_add_field(sg
->roomlist
, room
,
1510 SILC_32_TO_PTR(usercount
));
1511 purple_roomlist_room_add_field(sg
->roomlist
, room
,
1512 topic
? topic
: "");
1513 purple_roomlist_room_add(sg
->roomlist
, room
);
1515 if (status
== SILC_STATUS_LIST_END
||
1516 status
== SILC_STATUS_OK
) {
1517 purple_roomlist_set_in_progress(sg
->roomlist
, FALSE
);
1518 g_object_unref(sg
->roomlist
);
1519 sg
->roomlist
= NULL
;
1524 case SILC_COMMAND_GETKEY
:
1526 SilcPublicKey public_key
;
1528 if (status
!= SILC_STATUS_OK
) {
1529 purple_notify_error(gc
, _("Get Public Key"),
1530 _("Cannot fetch the public key"),
1531 silc_get_status_message(error
),
1532 purple_request_cpar_from_connection(gc
));
1536 (void)va_arg(ap
, SilcUInt32
);
1537 (void)va_arg(ap
, void *);
1538 public_key
= va_arg(ap
, SilcPublicKey
);
1541 purple_notify_error(gc
, _("Get Public Key"),
1542 _("Cannot fetch the public key"),
1543 _("No public key was received"),
1544 purple_request_cpar_from_connection(gc
));
1548 case SILC_COMMAND_INFO
:
1555 if (status
!= SILC_STATUS_OK
) {
1556 purple_notify_error(gc
, _("Server Information"),
1557 _("Cannot get server information"),
1558 silc_get_status_message(error
),
1559 purple_request_cpar_from_connection(gc
));
1563 (void)va_arg(ap
, SilcServerEntry
);
1564 server_name
= va_arg(ap
, char *);
1565 server_info
= va_arg(ap
, char *);
1567 if (server_name
&& server_info
) {
1568 g_snprintf(tmp
, sizeof(tmp
), "Server: %s\n%s",
1569 server_name
, server_info
);
1570 purple_notify_info(gc
, NULL
, _("Server Information"), tmp
,
1571 purple_request_cpar_from_connection(gc
));
1576 case SILC_COMMAND_STATS
:
1578 SilcClientStats
*stats
;
1581 if (status
!= SILC_STATUS_OK
) {
1582 purple_notify_error(gc
, _("Server Statistics"),
1583 _("Cannot get server statistics"),
1584 silc_get_status_message(error
),
1585 purple_request_cpar_from_connection(gc
));
1589 stats
= va_arg(ap
, SilcClientStats
*);
1591 msg
= g_strdup_printf(_("Local server start time: %s\n"
1592 "Local server uptime: %s\n"
1593 "Local server clients: %d\n"
1594 "Local server channels: %d\n"
1595 "Local server operators: %d\n"
1596 "Local router operators: %d\n"
1597 "Local cell clients: %d\n"
1598 "Local cell channels: %d\n"
1599 "Local cell servers: %d\n"
1600 "Total clients: %d\n"
1601 "Total channels: %d\n"
1602 "Total servers: %d\n"
1603 "Total routers: %d\n"
1604 "Total server operators: %d\n"
1605 "Total router operators: %d\n"),
1606 silc_time_string(stats
->starttime
),
1607 purple_str_seconds_to_string((int)stats
->uptime
),
1608 (int)stats
->my_clients
,
1609 (int)stats
->my_channels
,
1610 (int)stats
->my_server_ops
,
1611 (int)stats
->my_router_ops
,
1612 (int)stats
->cell_clients
,
1613 (int)stats
->cell_channels
,
1614 (int)stats
->cell_servers
,
1615 (int)stats
->clients
,
1616 (int)stats
->channels
,
1617 (int)stats
->servers
,
1618 (int)stats
->routers
,
1619 (int)stats
->server_ops
,
1620 (int)stats
->router_ops
);
1622 purple_notify_info(gc
, NULL
,
1623 _("Network Statistics"), msg
,
1624 purple_request_cpar_from_connection(gc
));
1629 case SILC_COMMAND_PING
:
1631 if (status
!= SILC_STATUS_OK
) {
1632 purple_notify_error(gc
, _("Ping"), _("Ping failed"),
1633 silc_get_status_message(error
),
1634 purple_request_cpar_from_connection(gc
));
1638 purple_notify_info(gc
, _("Ping"), _("Ping reply received from server"),
1639 NULL
, purple_request_cpar_from_connection(gc
));
1643 case SILC_COMMAND_KILL
:
1644 if (status
!= SILC_STATUS_OK
) {
1645 purple_notify_error(gc
, _("Kill User"),
1646 _("Could not kill user"),
1647 silc_get_status_message(error
),
1648 purple_request_cpar_from_connection(gc
));
1653 case SILC_COMMAND_CMODE
:
1655 SilcChannelEntry channel_entry
;
1656 SilcDList channel_pubkeys
, list
;
1657 SilcArgumentDecodedList e
;
1659 if (status
!= SILC_STATUS_OK
)
1662 channel_entry
= va_arg(ap
, SilcChannelEntry
);
1663 (void)va_arg(ap
, SilcUInt32
);
1664 (void)va_arg(ap
, SilcPublicKey
);
1665 channel_pubkeys
= va_arg(ap
, SilcDList
);
1670 list
= silc_dlist_init();
1672 if (channel_pubkeys
) {
1673 silc_dlist_start(channel_pubkeys
);
1674 while ((e
= silc_dlist_get(channel_pubkeys
))) {
1675 if (e
->arg_type
== 0x00 ||
1676 e
->arg_type
== 0x03)
1677 silc_dlist_add(list
, silc_pkcs_public_key_copy(e
->argument
));
1680 silcpurple_chat_chauth_show(sg
, channel_entry
, list
);
1684 case SILC_COMMAND_WATCH
:
1685 if (status
!= SILC_STATUS_OK
) {
1686 purple_notify_error(gc
, _("WATCH"), _("Cannot watch user"),
1687 silc_get_status_message(error
),
1688 purple_request_cpar_from_connection(gc
));
1694 if (status
== SILC_STATUS_OK
)
1695 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command
);
1697 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command
,
1698 silc_get_status_message(error
));
1703 /* Generic command reply callback for silc_client_command_send. Simply
1704 calls the default command_reply client operation callback */
1706 SilcBool
silcpurple_command_reply(SilcClient client
, SilcClientConnection conn
,
1707 SilcCommand command
, SilcStatus status
,
1708 SilcStatus error
, void *context
, va_list ap
)
1710 silc_command_reply(client
, conn
, command
, status
, error
, ap
);
1717 SilcAskPassphrase ask_pass
;
1718 SilcGetAuthMeth get_auth
;
1721 } *SilcPurpleAskPassphrase
;
1724 silc_ask_auth_password_cb(const unsigned char *passphrase
,
1725 SilcUInt32 passphrase_len
, void *context
)
1727 SilcPurpleAskPassphrase internal
= context
;
1729 if (!passphrase
|| !(*passphrase
))
1730 internal
->u
.get_auth(SILC_AUTH_NONE
, NULL
, 0, internal
->context
);
1732 internal
->u
.get_auth(SILC_AUTH_PASSWORD
,
1733 (unsigned char *)passphrase
,
1734 passphrase_len
, internal
->context
);
1735 silc_free(internal
);
1738 /* Find authentication method and authentication data by hostname and
1739 port. The hostname may be IP address as well. The `auth_method' is
1740 the authentication method the remote connection requires. It is
1741 however possible that remote accepts also some other authentication
1742 method. Application should use the method that may have been
1743 configured for this connection. If none has been configured it should
1744 use the required `auth_method'. If the `auth_method' is
1745 SILC_AUTH_NONE, server does not require any authentication or the
1746 required authentication method is not known. The `completion'
1747 callback must be called to deliver the chosen authentication method
1748 and data. The `conn' may be NULL. */
1751 silc_get_auth_method(SilcClient client
, SilcClientConnection conn
,
1752 char *hostname
, SilcUInt16 port
,
1753 SilcAuthMethod auth_method
,
1754 SilcGetAuthMeth completion
, void *context
)
1756 PurpleConnection
*gc
= client
->application
;
1757 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1758 SilcPurpleAskPassphrase internal
;
1759 const char *password
;
1763 purple_connection_update_progress(gc
, _("Resuming session"), 4, 5);
1765 purple_connection_update_progress(gc
, _("Authenticating connection"), 4, 5);
1767 /* Check configuration if we have this connection configured. */
1768 if (auth_method
== SILC_AUTH_PUBLIC_KEY
&&
1769 purple_account_get_bool(sg
->account
, "pubkey-auth", FALSE
)) {
1770 completion(SILC_AUTH_PUBLIC_KEY
, NULL
, 0, context
);
1773 if (auth_method
== SILC_AUTH_PASSWORD
) {
1774 password
= purple_connection_get_password(gc
);
1775 if (password
&& *password
) {
1776 completion(SILC_AUTH_PASSWORD
, (unsigned char *)password
, strlen(password
), context
);
1780 /* Ask password from user */
1781 internal
= silc_calloc(1, sizeof(*internal
));
1784 internal
->u
.get_auth
= completion
;
1785 internal
->context
= context
;
1786 silc_ask_passphrase(client
, conn
, silc_ask_auth_password_cb
,
1791 completion(SILC_AUTH_NONE
, NULL
, 0, context
);
1795 /* Called to verify received public key. The `conn_type' indicates which
1796 entity (server or client) has sent the public key. If user decides to
1797 trust the key the application may save the key as trusted public key for
1798 later use. The `completion' must be called after the public key has
1802 silc_verify_public_key(SilcClient client
, SilcClientConnection conn
,
1803 SilcConnectionType conn_type
,
1804 SilcPublicKey public_key
,
1805 SilcVerifyPublicKey completion
, void *context
)
1807 PurpleConnection
*gc
= client
->application
;
1808 SilcPurple sg
= purple_connection_get_protocol_data(gc
);
1810 if (!sg
->conn
&& (conn_type
== SILC_CONN_SERVER
||
1811 conn_type
== SILC_CONN_ROUTER
)) {
1814 purple_connection_update_progress(gc
, _("Resuming session"), 3, 5);
1816 purple_connection_update_progress(gc
, _("Verifying server public key"),
1820 /* Verify public key */
1821 silcpurple_verify_public_key(client
, conn
, NULL
, conn_type
,
1822 public_key
, completion
, context
);
1826 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal
, const char *passphrase
)
1828 if (!passphrase
|| !(*passphrase
))
1829 internal
->u
.ask_pass(NULL
, 0, internal
->context
);
1831 internal
->u
.ask_pass((unsigned char *)passphrase
,
1832 strlen(passphrase
), internal
->context
);
1833 silc_free(internal
);
1836 /* Ask (interact, that is) a passphrase from user. The passphrase is
1837 returned to the library by calling the `completion' callback with
1838 the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1839 if not then the library will attempt to encode. */
1842 silc_ask_passphrase(SilcClient client
, SilcClientConnection conn
,
1843 SilcAskPassphrase completion
, void *context
)
1845 PurpleConnection
*gc
= client
->application
;
1846 SilcPurpleAskPassphrase internal
= silc_calloc(1, sizeof(*internal
));
1850 internal
->u
.ask_pass
= completion
;
1851 internal
->context
= context
;
1852 purple_request_input(gc
, _("Passphrase"), NULL
,
1853 _("Passphrase required"), NULL
, FALSE
, TRUE
, NULL
,
1854 _("OK"), G_CALLBACK(silc_ask_passphrase_cb
),
1855 _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb
),
1856 purple_request_cpar_from_connection(gc
), internal
);
1860 /* Called to indicate that incoming key agreement request has been
1861 received. If the application wants to perform key agreement it may
1862 call silc_client_perform_key_agreement to initiate key agreement or
1863 silc_client_send_key_agreement to provide connection point to the
1864 remote client in case the `hostname' is NULL. If key agreement is
1865 not desired this request can be ignored. The `protocol' is either
1866 value 0 for TCP or value 1 for UDP. */
1869 silc_key_agreement(SilcClient client
, SilcClientConnection conn
,
1870 SilcClientEntry client_entry
,
1871 const char *hostname
, SilcUInt16 protocol
,
1874 silcpurple_buddy_keyagr_request(client
, conn
, client_entry
,
1875 hostname
, port
, protocol
);
1879 /* Notifies application that file transfer protocol session is being
1880 requested by the remote client indicated by the `client_entry' from
1881 the `hostname' and `port'. The `session_id' is the file transfer
1882 session and it can be used to either accept or reject the file
1883 transfer request, by calling the silc_client_file_receive or
1884 silc_client_file_close, respectively. */
1887 silc_ftp(SilcClient client
, SilcClientConnection conn
,
1888 SilcClientEntry client_entry
, SilcUInt32 session_id
,
1889 const char *hostname
, SilcUInt16 port
)
1891 silcpurple_ftp_request(client
, conn
, client_entry
, session_id
,
1895 SilcClientOperations ops
= {
1897 silc_channel_message
,
1898 silc_private_message
,
1902 silc_get_auth_method
,
1903 silc_verify_public_key
,
1904 silc_ask_passphrase
,