Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / protocols / silc / ops.c
blobb74f255a59a8ebd04f03f0ed95569d81324e5c50
1 /*
3 silcpurple_ops.c
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.
20 #include "internal.h"
21 #include "image-store.h"
22 PURPLE_BEGIN_IGNORE_CAST_ALIGN
23 #include "silc.h"
24 PURPLE_END_IGNORE_CAST_ALIGN
25 #include "silcclient.h"
26 #include "silcpurple.h"
27 #include "wb.h"
29 static void
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);
36 static void
37 silc_private_message(SilcClient client, SilcClientConnection conn,
38 SilcClientEntry sender, SilcMessagePayload payload,
39 SilcMessageFlags flags, const unsigned char *message,
40 SilcUInt32 message_len);
41 static void
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
49 type. */
51 void silc_say(SilcClient client, SilcClientConnection conn,
52 SilcClientMessageType type, char *msg, ...)
54 char tmp[256];
55 va_list va;
56 PurpleConnection *gc = NULL;
57 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
59 va_start(va, msg);
60 silc_vsnprintf(tmp, sizeof(tmp), msg, va);
61 va_end(va);
63 if (type != SILC_CLIENT_MESSAGE_ERROR) {
64 purple_debug_misc("silc", "silc_say (%d) %s\n", type, tmp);
65 return;
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;
73 if (client != NULL)
74 gc = client->application;
76 if (gc != NULL)
77 purple_connection_error(gc, reason, tmp);
78 else
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. */
85 static SilcBool
86 silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
87 SilcClientEntry sender, SilcChannelEntry channel,
88 SilcMessagePayload payload, SilcChannelPrivateKey key,
89 SilcMessageFlags flags, SilcMime mime,
90 gboolean recursive)
92 PurpleConnection *gc = client->application;
93 SilcPurple sg = purple_connection_get_protocol_data(gc);
94 const char *type;
95 const unsigned char *data;
96 SilcUInt32 data_len;
97 PurpleMessageFlags cflags = 0;
98 PurpleChatConversation *chat = NULL;
99 SilcBool ret = FALSE;
101 if (!mime)
102 return FALSE;
104 /* Check for fragmented MIME message */
105 if (silc_mime_is_partial(mime)) {
106 if (!sg->mimeass)
107 sg->mimeass = silc_mime_assembler_alloc();
109 /* Defragment */
110 mime = silc_mime_assemble(sg->mimeass, mime);
111 if (!mime)
112 /* More fragments to come */
113 return FALSE;
115 /* Process the complete message */
116 return silcpurple_mime_message(client, conn, sender, channel,
117 payload, key, flags, mime,
118 FALSE);
121 /* Check for multipart message */
122 if (silc_mime_is_multipart(mime)) {
123 SilcMime p;
124 const char *mtype;
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
143 one we support. */
144 if (silcpurple_mime_message(client, conn, sender, channel,
145 payload, key, flags, p, TRUE)) {
146 ret = TRUE;
147 break;
152 goto out;
155 /* Get content type and MIME data */
156 type = silc_mime_get_field(mime, "Content-Type");
157 if (!type)
158 goto out;
159 data = silc_mime_get_data(mime, &data_len);
160 if (!data)
161 goto out;
163 /* Process according to content type */
165 /* Plain text */
166 if (strstr(type, "text/plain")) {
167 /* Default is UTF-8, don't check for other charsets */
168 if (!strstr(type, "utf-8"))
169 goto out;
171 if (channel)
172 silc_channel_message(client, conn, sender, channel,
173 payload, key,
174 SILC_MESSAGE_FLAG_UTF8, data,
175 data_len);
176 else
177 silc_private_message(client, conn, sender, payload,
178 SILC_MESSAGE_FLAG_UTF8, data,
179 data_len);
180 ret = TRUE;
181 goto out;
184 /* Image */
185 if (purple_str_has_prefix(type, "image/")) {
186 char tmp[32];
187 PurpleImage *img;
188 guint img_id;
190 /* Get channel chat (if message is for channel) */
191 if (key && channel) {
192 GList *l;
193 SilcPurplePrvgrp prv;
195 for (l = sg->grps; l; l = l->next)
196 if (((SilcPurplePrvgrp)l->data)->key == key) {
197 prv = l->data;
198 chat = purple_conversations_find_chat_with_account(
199 prv->channel, sg->account);
200 break;
203 if (channel && !chat)
204 chat = purple_conversations_find_chat_with_account(
205 channel->channel_name, sg->account);
206 if (channel && !chat)
207 goto out;
209 img = purple_image_new_from_data(g_memdup(data, data_len), data_len);
210 if (!img)
211 goto out;
212 img_id = purple_image_store_add_temporary(img);
213 if (!img_id)
214 goto out;
216 cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
217 g_snprintf(tmp, sizeof(tmp), "<img src=\""
218 PURPLE_IMAGE_STORE_PROTOCOL "%u\">", img_id);
220 if (channel) {
221 purple_serv_got_chat_in(gc,
222 purple_chat_conversation_get_id(chat),
223 sender->nickname, cflags, tmp, time(NULL));
224 } else {
225 purple_serv_got_im(gc, sender->nickname,
226 tmp, cflags, time(NULL));
229 goto out;
232 /* Whiteboard message */
233 if (strstr(type, "application/x-wb") &&
234 !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
235 if (channel)
236 silcpurple_wb_receive_ch(client, conn, sender, channel,
237 payload, flags, data, data_len);
238 else
239 silcpurple_wb_receive(client, conn, sender, payload,
240 flags, data, data_len);
241 ret = TRUE;
242 goto out;
245 out:
246 if (!recursive)
247 silc_mime_free(mime);
248 return ret;
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). */
257 static void
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;
268 char *msg, *tmp;
270 if (!message)
271 return;
273 if (key) {
274 GList *l;
275 SilcPurplePrvgrp prv;
277 for (l = sg->grps; l; l = l->next)
278 if (((SilcPurplePrvgrp)l->data)->key == key) {
279 prv = l->data;
280 chat = purple_conversations_find_chat_with_account(
281 prv->channel, sg->account);
282 break;
285 if (!chat)
286 chat = purple_conversations_find_chat_with_account(
287 channel->channel_name, sg->account);
288 if (!chat)
289 return;
291 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
292 purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
293 /* XXX */
296 if (flags & SILC_MESSAGE_FLAG_DATA) {
297 /* Process MIME message */
298 SilcMime mime;
299 mime = silc_mime_decode(NULL, message, message_len);
300 silcpurple_mime_message(client, conn, sender, channel, payload,
301 key, flags, mime, FALSE);
302 return;
305 if (flags & SILC_MESSAGE_FLAG_ACTION) {
306 msg = g_strdup_printf("/me %s",
307 (const char *)message);
308 if (!msg)
309 return;
311 tmp = g_markup_escape_text(msg, -1);
312 /* Send to Purple */
313 purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(chat),
314 sender->nickname, PURPLE_MESSAGE_RECV, tmp, time(NULL));
315 g_free(tmp);
316 g_free(msg);
317 return;
320 if (flags & SILC_MESSAGE_FLAG_NOTICE) {
321 msg = g_strdup_printf("(notice) <I>%s</I> %s",
322 sender->nickname, (const char *)message);
323 if (!msg)
324 return;
326 /* Send to Purple */
327 purple_conversation_write_system_message(
328 PURPLE_CONVERSATION(chat), msg, 0);
329 g_free(msg);
330 return;
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);
338 msg = salvaged;
340 tmp = g_markup_escape_text(msg, -1);
341 /* Send to Purple */
342 purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(chat),
343 sender->nickname, PURPLE_MESSAGE_RECV, tmp, time(NULL));
344 g_free(salvaged);
345 g_free(tmp);
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
354 message). */
356 static void
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;
365 char *msg, *tmp;
367 if (!message)
368 return;
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)) {
376 /* XXX */
379 if (flags & SILC_MESSAGE_FLAG_DATA) {
380 /* Process MIME message */
381 SilcMime mime;
382 mime = silc_mime_decode(NULL, message, message_len);
383 silcpurple_mime_message(client, conn, sender, NULL, payload,
384 NULL, flags, mime, FALSE);
385 return;
388 if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
389 msg = g_strdup_printf("/me %s",
390 (const char *)message);
391 if (!msg)
392 return;
394 /* Send to Purple */
395 tmp = g_markup_escape_text(msg, -1);
396 purple_serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
397 g_free(msg);
398 g_free(tmp);
399 return;
402 if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
403 msg = g_strdup_printf("(notice) <I>%s</I> %s",
404 sender->nickname, (const char *)message);
405 if (!msg)
406 return;
408 /* Send to Purple */
409 purple_conversation_write_system_message(convo, msg, 0);
410 g_free(msg);
411 return;
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);
419 msg = salvaged;
421 tmp = g_markup_escape_text(msg, -1);
422 /* Send to Purple */
423 purple_serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
424 g_free(salvaged);
425 g_free(tmp);
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). */
439 static void
440 silc_notify(SilcClient client, SilcClientConnection conn,
441 SilcNotifyType type, ...)
443 va_list va;
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;
451 SilcIdType idtype;
452 void *entry;
453 SilcUInt32 mode;
454 SilcHashTableList htl;
455 SilcChannelUser chu;
456 char buf[512], buf2[512], *tmp, *name;
457 SilcNotifyType notify;
458 PurpleBuddy *b;
459 SilcDList list;
461 va_start(va, type);
462 memset(buf, 0, sizeof(buf));
464 switch (type) {
466 case SILC_NOTIFY_TYPE_NONE:
467 break;
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);
480 break;
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)
488 break;
490 chat = purple_conversations_find_chat_with_account(
491 channel->channel_name, sg->account);
492 if (!chat)
493 break;
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);
501 break;
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);
509 if (!chat)
510 break;
512 /* Remove user from channel */
513 purple_chat_conversation_remove_user(chat,
514 client_entry->nickname, NULL);
516 break;
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);
527 if (!chat)
528 continue;
529 purple_chat_conversation_remove_user(chat,
530 client_entry->nickname,
531 tmp);
533 silc_hash_table_list_reset(&htl);
535 break;
537 case SILC_NOTIFY_TYPE_TOPIC_SET:
539 char *esc, *tmp2;
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);
547 if (!chat)
548 break;
550 if (!tmp)
551 break;
553 esc = g_markup_escape_text(tmp, -1);
554 tmp2 = purple_markup_linkify(esc);
555 g_free(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);
584 } else {
585 purple_chat_conversation_set_topic(chat, NULL, tmp);
588 g_free(tmp2);
590 break;
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))
599 break;
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);
606 if (!chat)
607 continue;
608 if (purple_chat_conversation_has_user(chat, client_entry->nickname))
609 purple_chat_conversation_rename_user(chat,
610 tmp, name);
612 silc_hash_table_list_reset(&htl);
614 break;
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);
629 if (!chat)
630 break;
632 if (idtype == SILC_ID_CLIENT)
633 name = ((SilcClientEntry)entry)->nickname;
634 else if (idtype == SILC_ID_SERVER)
635 name = ((SilcServerEntry)entry)->server_name;
636 else
637 name = ((SilcChannelEntry)entry)->channel_name;
638 if (!name)
639 break;
641 if (mode) {
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);
646 } else {
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);
652 break;
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);
665 if (!chat)
666 break;
668 if (idtype == SILC_ID_CLIENT)
669 name = ((SilcClientEntry)entry)->nickname;
670 else if (idtype == SILC_ID_SERVER)
671 name = ((SilcServerEntry)entry)->server_name;
672 else
673 name = ((SilcChannelEntry)entry)->channel_name;
674 if (!name)
675 break;
677 if (mode) {
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;
686 } else {
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);
694 break;
697 case SILC_NOTIFY_TYPE_MOTD:
698 tmp = va_arg(va, char *);
699 silc_free(sg->motd);
700 sg->motd = silc_memdup(tmp, strlen(tmp));
701 break;
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);
711 if (!chat)
712 break;
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,
719 tmp ? tmp : "");
720 purple_conversation_write_system_message(PURPLE_CONVERSATION(chat),
721 buf, 0);
722 purple_serv_got_chat_left(gc, purple_chat_conversation_get_id(chat));
723 } else {
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,
729 buf);
732 break;
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);
763 if (!chat)
764 continue;
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);
771 } else {
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);
794 if (!chat)
795 continue;
796 purple_chat_conversation_remove_user(chat,
797 client_entry->nickname, tmp);
799 silc_hash_table_list_reset(&htl);
802 break;
804 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
805 break;
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);
818 if (!chat)
819 continue;
820 purple_chat_conversation_remove_user(chat,
821 client_entry->nickname,
822 _("Server signoff"));
824 silc_hash_table_list_reset(&htl);
826 break;
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));
835 break;
837 case SILC_NOTIFY_TYPE_WATCH:
839 SilcPublicKey public_key;
840 unsigned char *pk;
841 SilcUInt32 pk_len;
842 char *fingerprint;
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);
850 b = NULL;
851 if (public_key) {
852 GSList *buddies;
853 const char *f;
854 gsize i;
856 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
857 if (!pk)
858 break;
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);
868 silc_free(pk);
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)) {
873 b = buddies->data;
874 f = purple_blist_node_get_string(PURPLE_BLIST_NODE(b), "public-key");
875 if (purple_strequal(f, buf))
876 goto cont;
877 b = NULL;
880 cont:
881 if (!b) {
882 /* Find buddy by nickname */
883 b = purple_blist_find_buddy(sg->account, client_entry->nickname);
884 if (!b) {
885 purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
886 client_entry->nickname);
887 break;
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) {
895 break;
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);
927 break;
929 default:
930 purple_debug_info("silc", "Unhandled notification: %d\n", type);
931 break;
934 va_end(va);
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. */
948 static void
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);
956 switch (command) {
958 case SILC_COMMAND_CMODE:
959 if (argc == 3 && purple_strequal((char *)argv[2], "+C"))
960 sg->chpk = TRUE;
961 else
962 sg->chpk = FALSE;
963 break;
965 default:
966 break;
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
982 arguments. */
984 static void
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;
993 switch (command) {
994 case SILC_COMMAND_JOIN:
996 SilcChannelEntry channel;
997 PurpleChatConversation *chat;
998 SilcHashTableList *user_list;
999 SilcChannelUser chu;
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));
1007 return;
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);
1021 if (!chat)
1022 return;
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);
1041 else
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);
1052 g_list_free(users);
1053 g_list_free(flags);
1055 /* Set topic */
1056 if (topic)
1057 purple_chat_conversation_set_topic(chat, NULL, topic);
1059 /* Set nick */
1060 purple_chat_conversation_set_nick(chat, conn->local_entry->nickname);
1062 break;
1064 case SILC_COMMAND_LEAVE:
1065 break;
1067 case SILC_COMMAND_USERS:
1068 break;
1070 case SILC_COMMAND_WHOIS:
1072 SilcUInt32 *user_modes;
1073 SilcDList channels;
1074 SilcClientEntry client_entry;
1075 char tmp[1024];
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));
1084 break;
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) {
1103 gchar *tmp2;
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);
1106 g_free(tmp2);
1107 } else
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);
1120 if (moodstr) {
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);
1124 g_free(moodstr);
1127 if (statusstr) {
1128 purple_notify_user_info_add_pair_plaintext(user_info, _("Status Text"), statusstr);
1129 g_free(statusstr);
1132 if (contactstr) {
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);
1136 g_free(contactstr);
1139 if (langstr) {
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);
1143 g_free(langstr);
1146 if (devicestr) {
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);
1150 g_free(devicestr);
1153 if (tzstr) {
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);
1157 g_free(tzstr);
1160 if (geostr) {
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);
1164 g_free(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;
1175 int i = 0;
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);
1183 if (m)
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);
1187 silc_free(m);
1189 purple_notify_user_info_add_pair_plaintext(user_info, _("Currently on"), tmp);
1192 if (client_entry->public_key) {
1193 char *fingerprint, *babbleprint;
1194 unsigned char *pk;
1195 SilcUInt32 pk_len;
1196 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1197 if (pk) {
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);
1204 silc_free(pk);
1208 purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
1209 purple_notify_user_info_destroy(user_info);
1211 break;
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));
1224 break;
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 *);
1231 if (!nickname)
1232 break;
1234 user_info = purple_notify_user_info_new();
1235 purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), nickname);
1236 if (realname)
1237 purple_notify_user_info_add_pair_plaintext(user_info, _("Real Name"), realname);
1238 if (username) {
1239 if (client_entry && *client_entry->hostname) {
1240 gchar *tmp;
1241 tmp = g_strdup_printf("%s@%s", username, client_entry->hostname);
1242 purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), tmp);
1243 g_free(tmp);
1244 } else
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;
1256 unsigned char *pk;
1257 SilcUInt32 pk_len;
1258 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1259 if (pk) {
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);
1266 silc_free(pk);
1270 purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
1271 purple_notify_user_info_destroy(user_info);
1273 break;
1275 case SILC_COMMAND_DETACH:
1277 const char *file;
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));
1284 return;
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));
1291 g_unlink(file);
1292 silc_file_writefile(file, (const char *)silc_buffer_data(detach_data),
1293 silc_buffer_len(detach_data));
1295 break;
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));
1305 return;
1308 channel = va_arg(ap, SilcChannelEntry);
1310 chat = purple_conversations_find_chat_with_account(
1311 channel->channel_name, sg->account);
1312 if (!chat) {
1313 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1314 channel->channel_name);
1315 break;
1318 /* Set topic */
1319 if (channel->topic)
1320 purple_chat_conversation_set_topic(chat, NULL, channel->topic);
1322 break;
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));
1335 return;
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);
1346 if (!chat)
1347 continue;
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,
1354 oldnick, newnick);
1355 purple_chat_conversation_set_nick(chat, newnick);
1358 silc_hash_table_list_reset(&htl);
1360 purple_connection_set_display_name(gc, newnick);
1362 break;
1364 case SILC_COMMAND_LIST:
1366 char *topic, *name;
1367 int usercount;
1368 PurpleRoomlistRoom *room;
1370 if (sg->roomlist_cancelled)
1371 break;
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;
1380 return;
1383 (void)va_arg(ap, SilcChannelEntry);
1384 name = va_arg(ap, char *);
1385 if (!name) {
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;
1392 return;
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;
1412 break;
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));
1423 return;
1426 (void)va_arg(ap, SilcUInt32);
1427 (void)va_arg(ap, void *);
1428 public_key = va_arg(ap, SilcPublicKey);
1430 if (!public_key)
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));
1436 break;
1438 case SILC_COMMAND_INFO:
1441 char *server_name;
1442 char *server_info;
1443 char tmp[256];
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));
1450 return;
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));
1464 break;
1466 case SILC_COMMAND_STATS:
1468 SilcClientStats *stats;
1469 char *msg;
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));
1476 return;
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));
1515 g_free(msg);
1517 break;
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));
1525 return;
1528 purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
1529 NULL, purple_request_cpar_from_connection(gc));
1531 break;
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));
1539 return;
1541 break;
1543 case SILC_COMMAND_CMODE:
1545 SilcChannelEntry channel_entry;
1546 SilcDList channel_pubkeys, list;
1547 SilcArgumentDecodedList e;
1549 if (status != SILC_STATUS_OK)
1550 return;
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);
1557 if (!sg->chpk)
1558 break;
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);
1572 break;
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));
1579 return;
1581 break;
1583 default:
1584 if (status == SILC_STATUS_OK)
1585 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
1586 else
1587 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
1588 silc_get_status_message(error));
1589 break;
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);
1601 return TRUE;
1605 typedef struct {
1606 union {
1607 SilcAskPassphrase ask_pass;
1608 SilcGetAuthMeth get_auth;
1609 } u;
1610 void *context;
1611 } *SilcPurpleAskPassphrase;
1613 static void
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);
1621 else
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. */
1640 static void
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;
1651 /* Progress */
1652 if (sg->resuming)
1653 purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
1654 else
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);
1661 return;
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);
1667 return;
1670 /* Ask password from user */
1671 internal = silc_calloc(1, sizeof(*internal));
1672 if (!internal)
1673 return;
1674 internal->u.get_auth = completion;
1675 internal->context = context;
1676 silc_ask_passphrase(client, conn, silc_ask_auth_password_cb,
1677 internal);
1678 return;
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
1689 been verified. */
1691 static void
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)) {
1702 /* Progress */
1703 if (sg->resuming)
1704 purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
1705 else
1706 purple_connection_update_progress(gc, _("Verifying server public key"),
1707 3, 5);
1710 /* Verify public key */
1711 silcpurple_verify_public_key(client, conn, NULL, conn_type,
1712 public_key, completion, context);
1715 static void
1716 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
1718 if (!passphrase || !(*passphrase))
1719 internal->u.ask_pass(NULL, 0, internal->context);
1720 else
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. */
1731 static void
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));
1738 if (!internal)
1739 return;
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. */
1758 static void
1759 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1760 SilcClientEntry client_entry,
1761 const char *hostname, SilcUInt16 protocol,
1762 SilcUInt16 port)
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. */
1776 static void
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,
1782 hostname, port);
1785 SilcClientOperations ops = {
1786 silc_say,
1787 silc_channel_message,
1788 silc_private_message,
1789 silc_notify,
1790 silc_command,
1791 silc_command_reply,
1792 silc_get_auth_method,
1793 silc_verify_public_key,
1794 silc_ask_passphrase,
1795 silc_key_agreement,
1796 silc_ftp