Merged pidgin/main into default
[pidgin-git.git] / libpurple / protocols / silc / ops.c
blob3b0d9681a997b001062c4950572e8b4bc38500b9
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;
970 #if 0
971 static void
972 silcpurple_whois_more(SilcClientEntry client_entry, gint id)
974 SilcAttributePayload attr;
975 SilcAttribute attribute;
976 GString *s;
977 SilcVCardStruct vcard;
978 int i;
980 if (id != 0)
981 return;
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);
990 switch (attribute) {
992 case SILC_ATTRIBUTE_USER_INFO:
993 if (!silc_attribute_get_object(attr, (void *)&vcard,
994 sizeof(vcard)))
995 continue;
996 g_string_append_printf(s, "%s:\n\n", _("Personal Information"));
997 if (vcard.full_name)
998 g_string_append_printf(s, "%s:\t\t%s\n",
999 _("Full Name"),
1000 vcard.full_name);
1001 if (vcard.first_name)
1002 g_string_append_printf(s, "%s:\t%s\n",
1003 _("First Name"),
1004 vcard.first_name);
1005 if (vcard.middle_names)
1006 g_string_append_printf(s, "%s:\t%s\n",
1007 _("Middle Name"),
1008 vcard.middle_names);
1009 if (vcard.family_name)
1010 g_string_append_printf(s, "%s:\t%s\n",
1011 _("Family Name"),
1012 vcard.family_name);
1013 if (vcard.nickname)
1014 g_string_append_printf(s, "%s:\t\t%s\n",
1015 _("Nickname"),
1016 vcard.nickname);
1017 if (vcard.bday)
1018 g_string_append_printf(s, "%s:\t\t%s\n",
1019 _("Birth Day"),
1020 vcard.bday);
1021 if (vcard.title)
1022 g_string_append_printf(s, "%s:\t\t%s\n",
1023 _("Job Title"),
1024 vcard.title);
1025 if (vcard.role)
1026 g_string_append_printf(s, "%s:\t\t%s\n",
1027 _("Job Role"),
1028 vcard.role);
1029 if (vcard.org_name)
1030 g_string_append_printf(s, "%s:\t%s\n",
1031 _("Organization"),
1032 vcard.org_name);
1033 if (vcard.org_unit)
1034 g_string_append_printf(s, "%s:\t\t%s\n",
1035 _("Unit"),
1036 vcard.org_unit);
1037 if (vcard.url)
1038 g_string_append_printf(s, "%s:\t%s\n",
1039 _("Homepage"),
1040 vcard.url);
1041 if (vcard.label)
1042 g_string_append_printf(s, "%s:\t%s\n",
1043 _("Address"),
1044 vcard.label);
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",
1048 _("Phone"),
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",
1054 _("Email"),
1055 vcard.emails[i].address);
1057 if (vcard.note)
1058 g_string_append_printf(s, "\n%s:\t\t%s\n",
1059 _("Note"),
1060 vcard.note);
1061 break;
1065 purple_notify_info(NULL, _("User Information"), _("User Information"),
1066 s->str);
1067 g_string_free(s, TRUE);
1069 #endif
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
1083 arguments. */
1085 static void
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;
1094 switch (command) {
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));
1108 return;
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);
1122 if (!chat)
1123 return;
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);
1142 else
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);
1153 g_list_free(users);
1154 g_list_free(flags);
1156 /* Set topic */
1157 if (topic)
1158 purple_chat_conversation_set_topic(chat, NULL, topic);
1160 /* Set nick */
1161 purple_chat_conversation_set_nick(chat, conn->local_entry->nickname);
1163 break;
1165 case SILC_COMMAND_LEAVE:
1166 break;
1168 case SILC_COMMAND_USERS:
1169 break;
1171 case SILC_COMMAND_WHOIS:
1173 SilcUInt32 *user_modes;
1174 SilcDList channels;
1175 SilcClientEntry client_entry;
1176 char tmp[1024];
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));
1185 break;
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) {
1204 gchar *tmp2;
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);
1207 g_free(tmp2);
1208 } else
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);
1221 if (moodstr) {
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);
1225 g_free(moodstr);
1228 if (statusstr) {
1229 purple_notify_user_info_add_pair_plaintext(user_info, _("Status Text"), statusstr);
1230 g_free(statusstr);
1233 if (contactstr) {
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);
1237 g_free(contactstr);
1240 if (langstr) {
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);
1244 g_free(langstr);
1247 if (devicestr) {
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);
1251 g_free(devicestr);
1254 if (tzstr) {
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);
1258 g_free(tzstr);
1261 if (geostr) {
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);
1265 g_free(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;
1276 int i = 0;
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);
1284 if (m)
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);
1288 silc_free(m);
1290 purple_notify_user_info_add_pair_plaintext(user_info, _("Currently on"), tmp);
1293 if (client_entry->public_key) {
1294 char *fingerprint, *babbleprint;
1295 unsigned char *pk;
1296 SilcUInt32 pk_len;
1297 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1298 if (pk) {
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);
1305 silc_free(pk);
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);
1316 else
1317 #endif /* 0 */
1318 purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
1319 purple_notify_user_info_destroy(user_info);
1321 break;
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));
1334 break;
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 *);
1341 if (!nickname)
1342 break;
1344 user_info = purple_notify_user_info_new();
1345 purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), nickname);
1346 if (realname)
1347 purple_notify_user_info_add_pair_plaintext(user_info, _("Real Name"), realname);
1348 if (username) {
1349 if (client_entry && *client_entry->hostname) {
1350 gchar *tmp;
1351 tmp = g_strdup_printf("%s@%s", username, client_entry->hostname);
1352 purple_notify_user_info_add_pair_plaintext(user_info, _("Username"), tmp);
1353 g_free(tmp);
1354 } else
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;
1366 unsigned char *pk;
1367 SilcUInt32 pk_len;
1368 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1369 if (pk) {
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);
1376 silc_free(pk);
1380 purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
1381 purple_notify_user_info_destroy(user_info);
1383 break;
1385 case SILC_COMMAND_DETACH:
1387 const char *file;
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));
1394 return;
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));
1401 g_unlink(file);
1402 silc_file_writefile(file, (const char *)silc_buffer_data(detach_data),
1403 silc_buffer_len(detach_data));
1405 break;
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));
1415 return;
1418 channel = va_arg(ap, SilcChannelEntry);
1420 chat = purple_conversations_find_chat_with_account(
1421 channel->channel_name, sg->account);
1422 if (!chat) {
1423 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1424 channel->channel_name);
1425 break;
1428 /* Set topic */
1429 if (channel->topic)
1430 purple_chat_conversation_set_topic(chat, NULL, channel->topic);
1432 break;
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));
1445 return;
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);
1456 if (!chat)
1457 continue;
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,
1464 oldnick, newnick);
1465 purple_chat_conversation_set_nick(chat, newnick);
1468 silc_hash_table_list_reset(&htl);
1470 purple_connection_set_display_name(gc, newnick);
1472 break;
1474 case SILC_COMMAND_LIST:
1476 char *topic, *name;
1477 int usercount;
1478 PurpleRoomlistRoom *room;
1480 if (sg->roomlist_cancelled)
1481 break;
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;
1490 return;
1493 (void)va_arg(ap, SilcChannelEntry);
1494 name = va_arg(ap, char *);
1495 if (!name) {
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;
1502 return;
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;
1522 break;
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));
1533 return;
1536 (void)va_arg(ap, SilcUInt32);
1537 (void)va_arg(ap, void *);
1538 public_key = va_arg(ap, SilcPublicKey);
1540 if (!public_key)
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));
1546 break;
1548 case SILC_COMMAND_INFO:
1551 char *server_name;
1552 char *server_info;
1553 char tmp[256];
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));
1560 return;
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));
1574 break;
1576 case SILC_COMMAND_STATS:
1578 SilcClientStats *stats;
1579 char *msg;
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));
1586 return;
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));
1625 g_free(msg);
1627 break;
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));
1635 return;
1638 purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
1639 NULL, purple_request_cpar_from_connection(gc));
1641 break;
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));
1649 return;
1651 break;
1653 case SILC_COMMAND_CMODE:
1655 SilcChannelEntry channel_entry;
1656 SilcDList channel_pubkeys, list;
1657 SilcArgumentDecodedList e;
1659 if (status != SILC_STATUS_OK)
1660 return;
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);
1667 if (!sg->chpk)
1668 break;
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);
1682 break;
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));
1689 return;
1691 break;
1693 default:
1694 if (status == SILC_STATUS_OK)
1695 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
1696 else
1697 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
1698 silc_get_status_message(error));
1699 break;
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);
1711 return TRUE;
1715 typedef struct {
1716 union {
1717 SilcAskPassphrase ask_pass;
1718 SilcGetAuthMeth get_auth;
1719 } u;
1720 void *context;
1721 } *SilcPurpleAskPassphrase;
1723 static void
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);
1731 else
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. */
1750 static void
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;
1761 /* Progress */
1762 if (sg->resuming)
1763 purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
1764 else
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);
1771 return;
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);
1777 return;
1780 /* Ask password from user */
1781 internal = silc_calloc(1, sizeof(*internal));
1782 if (!internal)
1783 return;
1784 internal->u.get_auth = completion;
1785 internal->context = context;
1786 silc_ask_passphrase(client, conn, silc_ask_auth_password_cb,
1787 internal);
1788 return;
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
1799 been verified. */
1801 static void
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)) {
1812 /* Progress */
1813 if (sg->resuming)
1814 purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
1815 else
1816 purple_connection_update_progress(gc, _("Verifying server public key"),
1817 3, 5);
1820 /* Verify public key */
1821 silcpurple_verify_public_key(client, conn, NULL, conn_type,
1822 public_key, completion, context);
1825 static void
1826 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
1828 if (!passphrase || !(*passphrase))
1829 internal->u.ask_pass(NULL, 0, internal->context);
1830 else
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. */
1841 static void
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));
1848 if (!internal)
1849 return;
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. */
1868 static void
1869 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1870 SilcClientEntry client_entry,
1871 const char *hostname, SilcUInt16 protocol,
1872 SilcUInt16 port)
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. */
1886 static void
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,
1892 hostname, port);
1895 SilcClientOperations ops = {
1896 silc_say,
1897 silc_channel_message,
1898 silc_private_message,
1899 silc_notify,
1900 silc_command,
1901 silc_command_reply,
1902 silc_get_auth_method,
1903 silc_verify_public_key,
1904 silc_ask_passphrase,
1905 silc_key_agreement,
1906 silc_ftp