1 /*
2
3 silcpurple_ops.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2004 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silcincludes.h"
21 #include "silcclient.h"
22 #include "silcpurple.h"
23 #include "imgstore.h"
24 #include "wb.h"
25
26 #include "glibcompat.h"
27
28 static void
29 silc_channel_message(SilcClient client, SilcClientConnection conn,
30 SilcClientEntry sender, SilcChannelEntry channel,
31 SilcMessagePayload payload, SilcChannelPrivateKey key,
32 SilcMessageFlags flags, const unsigned char *message,
33 SilcUInt32 message_len);
34 static void
35 silc_private_message(SilcClient client, SilcClientConnection conn,
36 SilcClientEntry sender, SilcMessagePayload payload,
37 SilcMessageFlags flags, const unsigned char *message,
38 SilcUInt32 message_len);
39
40 /* Message sent to the application by library. `conn' associates the
41 message to a specific connection. `conn', however, may be NULL.
42 The `type' indicates the type of the message sent by the library.
43 The application can for example filter the message according the
44 type. */
45
46 static void
silc_say(SilcClient client,SilcClientConnection conn,SilcClientMessageType type,char * msg,...)47 silc_say(SilcClient client, SilcClientConnection conn,
48 SilcClientMessageType type, char *msg, ...)
49 {
50 /* Nothing */
51 }
52
53 #ifdef HAVE_SILCMIME_H
54 /* Processes incoming MIME message. Can be private message or channel
55 message. */
56
57 static void
silcpurple_mime_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcChannelEntry channel,SilcMessagePayload payload,SilcChannelPrivateKey key,SilcMessageFlags flags,SilcMime mime,gboolean recursive)58 silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
59 SilcClientEntry sender, SilcChannelEntry channel,
60 SilcMessagePayload payload, SilcChannelPrivateKey key,
61 SilcMessageFlags flags, SilcMime mime,
62 gboolean recursive)
63 {
64 PurpleConnection *gc = client->application;
65 SilcPurple sg = gc->proto_data;
66 const char *type;
67 const unsigned char *data;
68 SilcUInt32 data_len;
69 PurpleMessageFlags cflags = 0;
70 PurpleConversation *convo = NULL;
71
72 if (!mime)
73 return;
74
75 /* Check for fragmented MIME message */
76 if (silc_mime_is_partial(mime)) {
77 if (!sg->mimeass)
78 sg->mimeass = silc_mime_assembler_alloc();
79
80 /* Defragment */
81 mime = silc_mime_assemble(sg->mimeass, mime);
82 if (!mime)
83 /* More fragments to come */
84 return;
85
86 /* Process the complete message */
87 silcpurple_mime_message(client, conn, sender, channel,
88 payload, key, flags, mime, FALSE);
89 return;
90 }
91
92 /* Check for multipart message */
93 if (silc_mime_is_multipart(mime)) {
94 SilcMime p;
95 const char *mtype;
96 SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
97
98 /* Only "mixed" type supported */
99 if (!purple_strequal(mtype, "mixed"))
100 goto out;
101
102 silc_dlist_start(parts);
103 while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
104 /* Recursively process parts */
105 silcpurple_mime_message(client, conn, sender, channel,
106 payload, key, flags, p, TRUE);
107 }
108 goto out;
109 }
110
111 /* Get content type and MIME data */
112 type = silc_mime_get_field(mime, "Content-Type");
113 if (!type)
114 goto out;
115 data = silc_mime_get_data(mime, &data_len);
116 if (!data)
117 goto out;
118
119 /* Process according to content type */
120
121 /* Plain text */
122 if (strstr(type, "text/plain")) {
123 /* Default is UTF-8, don't check for other charsets */
124 if (!strstr(type, "utf-8"))
125 goto out;
126
127 if (channel)
128 silc_channel_message(client, conn, sender, channel,
129 payload, key,
130 SILC_MESSAGE_FLAG_UTF8, data,
131 data_len);
132 else
133 silc_private_message(client, conn, sender, payload,
134 SILC_MESSAGE_FLAG_UTF8, data,
135 data_len);
136 goto out;
137 }
138
139 /* Image */
140 if (strstr(type, "image/png") ||
141 strstr(type, "image/jpeg") ||
142 strstr(type, "image/gif") ||
143 strstr(type, "image/tiff")) {
144 char tmp[32];
145 int imgid;
146
147 /* Get channel convo (if message is for channel) */
148 if (key && channel) {
149 GList *l;
150 SilcPurplePrvgrp prv;
151
152 for (l = sg->grps; l; l = l->next)
153 if (((SilcPurplePrvgrp)l->data)->key == key) {
154 prv = l->data;
155 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
156 prv->channel, sg->account);
157 break;
158 }
159 }
160 if (channel && !convo)
161 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
162 channel->channel_name, sg->account);
163 if (channel && !convo)
164 goto out;
165
166 imgid = purple_imgstore_add_with_id(g_memdup2(data, data_len), data_len, "");
167 if (imgid) {
168 cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
169 g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
170
171 if (channel)
172 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
173 sender->nickname ?
174 sender->nickname :
175 "<unknown>", cflags,
176 tmp, time(NULL));
177 else
178 serv_got_im(gc, sender->nickname ?
179 sender->nickname : "<unknown>",
180 tmp, cflags, time(NULL));
181
182 purple_imgstore_unref_by_id(imgid);
183 cflags = 0;
184 }
185 goto out;
186 }
187
188 /* Whiteboard message */
189 if (strstr(type, "application/x-wb") &&
190 !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
191 if (channel)
192 silcpurple_wb_receive_ch(client, conn, sender, channel,
193 payload, flags, data, data_len);
194 else
195 silcpurple_wb_receive(client, conn, sender, payload,
196 flags, data, data_len);
197 goto out;
198 }
199
200 out:
201 if (!recursive)
202 silc_mime_free(mime);
203 }
204 #endif /* HAVE_SILCMIME_H */
205
206 /* Message for a channel. The `sender' is the sender of the message
207 The `channel' is the channel. The `message' is the message. Note
208 that `message' maybe NULL. The `flags' indicates message flags
209 and it is used to determine how the message can be interpreted
210 (like it may tell the message is multimedia message). */
211
212 static void
silc_channel_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcChannelEntry channel,SilcMessagePayload payload,SilcChannelPrivateKey key,SilcMessageFlags flags,const unsigned char * message,SilcUInt32 message_len)213 silc_channel_message(SilcClient client, SilcClientConnection conn,
214 SilcClientEntry sender, SilcChannelEntry channel,
215 SilcMessagePayload payload, SilcChannelPrivateKey key,
216 SilcMessageFlags flags, const unsigned char *message,
217 SilcUInt32 message_len)
218 {
219 PurpleConnection *gc = client->application;
220 SilcPurple sg = gc->proto_data;
221 PurpleConversation *convo = NULL;
222 char *msg, *tmp;
223
224 if (!message)
225 return;
226
227 if (key) {
228 GList *l;
229 SilcPurplePrvgrp prv;
230
231 for (l = sg->grps; l; l = l->next)
232 if (((SilcPurplePrvgrp)l->data)->key == key) {
233 prv = l->data;
234 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
235 prv->channel, sg->account);
236 break;
237 }
238 }
239 if (!convo)
240 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
241 channel->channel_name, sg->account);
242 if (!convo)
243 return;
244
245 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
246 purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
247 /* XXX */
248 }
249
250 if (flags & SILC_MESSAGE_FLAG_DATA) {
251 /* Process MIME message */
252 #ifdef HAVE_SILCMIME_H
253 SilcMime mime;
254 mime = silc_mime_decode(message, message_len);
255 silcpurple_mime_message(client, conn, sender, channel, payload,
256 key, flags, mime, FALSE);
257 #else
258 char type[128], enc[128];
259 unsigned char *data;
260 SilcUInt32 data_len;
261
262 memset(type, 0, sizeof(type));
263 memset(enc, 0, sizeof(enc));
264
265 if (!silc_mime_parse(message, message_len, NULL, 0,
266 type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
267 &data_len))
268 return;
269
270 if (purple_strequal(type, "application/x-wb") &&
271 purple_strequal(enc, "binary") &&
272 !purple_account_get_bool(sg->account, "block-wb", FALSE))
273 silcpurple_wb_receive_ch(client, conn, sender, channel,
274 payload, flags, data, data_len);
275 #endif
276 return;
277 }
278
279 if (flags & SILC_MESSAGE_FLAG_ACTION) {
280 msg = g_strdup_printf("/me %s",
281 (const char *)message);
282 if (!msg)
283 return;
284
285 tmp = g_markup_escape_text(msg, -1);
286 /* Send to Purple */
287 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
288 sender->nickname ?
289 sender->nickname : "<unknown>", 0,
290 tmp, time(NULL));
291 g_free(tmp);
292 g_free(msg);
293 return;
294 }
295
296 if (flags & SILC_MESSAGE_FLAG_NOTICE) {
297 msg = g_strdup_printf("(notice) <I>%s</I> %s",
298 sender->nickname ?
299 sender->nickname : "<unknown>",
300 (const char *)message);
301 if (!msg)
302 return;
303
304 /* Send to Purple */
305 purple_conversation_write(convo, NULL, (const char *)msg,
306 PURPLE_MESSAGE_SYSTEM, time(NULL));
307 g_free(msg);
308 return;
309 }
310
311 if (flags & SILC_MESSAGE_FLAG_UTF8) {
312 tmp = g_markup_escape_text((const char *)message, -1);
313 /* Send to Purple */
314 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
315 sender->nickname ?
316 sender->nickname : "<unknown>", 0,
317 tmp, time(NULL));
318 g_free(tmp);
319 }
320 }
321
322
323 /* Private message to the client. The `sender' is the sender of the
324 message. The message is `message'and maybe NULL. The `flags'
325 indicates message flags and it is used to determine how the message
326 can be interpreted (like it may tell the message is multimedia
327 message). */
328
329 static void
silc_private_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcMessagePayload payload,SilcMessageFlags flags,const unsigned char * message,SilcUInt32 message_len)330 silc_private_message(SilcClient client, SilcClientConnection conn,
331 SilcClientEntry sender, SilcMessagePayload payload,
332 SilcMessageFlags flags, const unsigned char *message,
333 SilcUInt32 message_len)
334 {
335 PurpleConnection *gc = client->application;
336 SilcPurple sg = gc->proto_data;
337 PurpleConversation *convo = NULL;
338 char *msg, *tmp;
339
340 if (!message)
341 return;
342
343 if (sender->nickname)
344 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
345 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
346 sender->nickname, sg->account);
347
348 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
349 purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
350 /* XXX */
351 }
352
353 if (flags & SILC_MESSAGE_FLAG_DATA) {
354 #ifdef HAVE_SILCMIME_H
355 /* Process MIME message */
356 SilcMime mime;
357 mime = silc_mime_decode(message, message_len);
358 silcpurple_mime_message(client, conn, sender, NULL, payload,
359 NULL, flags, mime, FALSE);
360 #else
361 char type[128], enc[128];
362 unsigned char *data;
363 SilcUInt32 data_len;
364
365 memset(type, 0, sizeof(type));
366 memset(enc, 0, sizeof(enc));
367
368 if (!silc_mime_parse(message, message_len, NULL, 0,
369 type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
370 &data_len))
371 return;
372
373 if (purple_strequal(type, "application/x-wb") &&
374 purple_strequal(enc, "binary") &&
375 !purple_account_get_bool(sg->account, "block-wb", FALSE))
376 silcpurple_wb_receive(client, conn, sender, payload,
377 flags, data, data_len);
378 #endif
379 return;
380 }
381
382 if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
383 msg = g_strdup_printf("/me %s",
384 (const char *)message);
385 if (!msg)
386 return;
387
388 tmp = g_markup_escape_text(msg, -1);
389 /* Send to Purple */
390 serv_got_im(gc, sender->nickname ?
391 sender->nickname : "<unknown>",
392 tmp, 0, time(NULL));
393 g_free(msg);
394 g_free(tmp);
395 return;
396 }
397
398 if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
399 msg = g_strdup_printf("(notice) <I>%s</I> %s",
400 sender->nickname ?
401 sender->nickname : "<unknown>",
402 (const char *)message);
403 if (!msg)
404 return;
405
406 /* Send to Purple */
407 purple_conversation_write(convo, NULL, (const char *)msg,
408 PURPLE_MESSAGE_SYSTEM, time(NULL));
409 g_free(msg);
410 return;
411 }
412
413 if (flags & SILC_MESSAGE_FLAG_UTF8) {
414 tmp = g_markup_escape_text((const char *)message, -1);
415 /* Send to Purple */
416 serv_got_im(gc, sender->nickname ?
417 sender->nickname : "<unknown>",
418 tmp, 0, time(NULL));
419 g_free(tmp);
420 }
421 }
422
423
424 /* Notify message to the client. The notify arguments are sent in the
425 same order as servers sends them. The arguments are same as received
426 from the server except for ID's. If ID is received application receives
427 the corresponding entry to the ID. For example, if Client ID is received
428 application receives SilcClientEntry. Also, if the notify type is
429 for channel the channel entry is sent to application (even if server
430 does not send it because client library gets the channel entry from
431 the Channel ID in the packet's header). */
432
433 static void
silc_notify(SilcClient client,SilcClientConnection conn,SilcNotifyType type,...)434 silc_notify(SilcClient client, SilcClientConnection conn,
435 SilcNotifyType type, ...)
436 {
437 va_list va;
438 PurpleConnection *gc = client->application;
439 SilcPurple sg = gc->proto_data;
440 PurpleConversation *convo;
441 SilcClientEntry client_entry, client_entry2;
442 SilcChannelEntry channel;
443 SilcServerEntry server_entry;
444 SilcIdType idtype;
445 void *entry;
446 SilcUInt32 mode;
447 SilcHashTableList htl;
448 SilcChannelUser chu;
449 char buf[512], buf2[512], *tmp, *name;
450 SilcNotifyType notify;
451 PurpleBuddy *b;
452 int i;
453
454 va_start(va, type);
455 memset(buf, 0, sizeof(buf));
456
457 switch (type) {
458
459 case SILC_NOTIFY_TYPE_NONE:
460 break;
461
462 case SILC_NOTIFY_TYPE_INVITE:
463 {
464 GHashTable *components;
465 va_arg(va, SilcChannelEntry);
466 name = va_arg(va, char *);
467 client_entry = va_arg(va, SilcClientEntry);
468
469 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
470 g_hash_table_insert(components, strdup("channel"), strdup(name));
471 serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
472 }
473 break;
474
475 case SILC_NOTIFY_TYPE_JOIN:
476 client_entry = va_arg(va, SilcClientEntry);
477 channel = va_arg(va, SilcChannelEntry);
478
479 /* If we joined channel, do nothing */
480 if (client_entry == conn->local_entry)
481 break;
482
483 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
484 channel->channel_name, sg->account);
485 if (!convo)
486 break;
487
488 /* Join user to channel */
489 g_snprintf(buf, sizeof(buf), "%s@%s",
490 client_entry->username, client_entry->hostname);
491 purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
492 g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
493
494 break;
495
496 case SILC_NOTIFY_TYPE_LEAVE:
497 client_entry = va_arg(va, SilcClientEntry);
498 channel = va_arg(va, SilcChannelEntry);
499
500 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
501 channel->channel_name, sg->account);
502 if (!convo)
503 break;
504
505 /* Remove user from channel */
506 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
507 client_entry->nickname, NULL);
508
509 break;
510
511 case SILC_NOTIFY_TYPE_SIGNOFF:
512 client_entry = va_arg(va, SilcClientEntry);
513 tmp = va_arg(va, char *);
514
515 if (!client_entry->nickname)
516 break;
517
518 /* Remove from all channels */
519 silc_hash_table_list(client_entry->channels, &htl);
520 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
521 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
522 chu->channel->channel_name, sg->account);
523 if (!convo)
524 continue;
525 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
526 client_entry->nickname,
527 tmp);
528 }
529 silc_hash_table_list_reset(&htl);
530
531 break;
532
533 case SILC_NOTIFY_TYPE_TOPIC_SET:
534 {
535 char *esc, *tmp2;
536 idtype = va_arg(va, int);
537 entry = va_arg(va, void *);
538 tmp = va_arg(va, char *);
539 channel = va_arg(va, SilcChannelEntry);
540
541 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
542 channel->channel_name, sg->account);
543 if (!convo)
544 break;
545
546 if (!tmp)
547 break;
548
549 esc = g_markup_escape_text(tmp, -1);
550 tmp2 = purple_markup_linkify(esc);
551 g_free(esc);
552
553 if (idtype == SILC_ID_CLIENT) {
554 client_entry = (SilcClientEntry)entry;
555 g_snprintf(buf, sizeof(buf),
556 _("%s has changed the topic of <I>%s</I> to: %s"),
557 client_entry->nickname, channel->channel_name, tmp2);
558 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
559 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
560 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
561 client_entry->nickname, tmp);
562 } else if (idtype == SILC_ID_SERVER) {
563 server_entry = (SilcServerEntry)entry;
564 g_snprintf(buf, sizeof(buf),
565 _("%s has changed the topic of <I>%s</I> to: %s"),
566 server_entry->server_name, channel->channel_name, tmp2);
567 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
568 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
569 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
570 server_entry->server_name, tmp);
571 } else if (idtype == SILC_ID_CHANNEL) {
572 channel = (SilcChannelEntry)entry;
573 g_snprintf(buf, sizeof(buf),
574 _("%s has changed the topic of <I>%s</I> to: %s"),
575 channel->channel_name, channel->channel_name, tmp2);
576 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
577 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
578 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
579 channel->channel_name, tmp);
580 } else {
581 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
582 }
583
584 g_free(tmp2);
585
586 break;
587
588 }
589 case SILC_NOTIFY_TYPE_NICK_CHANGE:
590 client_entry = va_arg(va, SilcClientEntry);
591 client_entry2 = va_arg(va, SilcClientEntry);
592
593 if (purple_strequal(client_entry->nickname, client_entry2->nickname))
594 break;
595
596 /* Change nick on all channels */
597 silc_hash_table_list(client_entry2->channels, &htl);
598 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
599 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
600 chu->channel->channel_name, sg->account);
601 if (!convo)
602 continue;
603 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
604 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
605 client_entry->nickname,
606 client_entry2->nickname);
607 }
608 silc_hash_table_list_reset(&htl);
609
610 break;
611
612 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
613 idtype = va_arg(va, int);
614 entry = va_arg(va, void *);
615 mode = va_arg(va, SilcUInt32);
616 (void)va_arg(va, char *);
617 (void)va_arg(va, char *);
618 (void)va_arg(va, char *);
619 (void)va_arg(va, SilcPublicKey);
620 (void)va_arg(va, SilcBuffer);
621 channel = va_arg(va, SilcChannelEntry);
622
623 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
624 channel->channel_name, sg->account);
625 if (!convo)
626 break;
627
628 if (idtype == SILC_ID_CLIENT)
629 name = ((SilcClientEntry)entry)->nickname;
630 else if (idtype == SILC_ID_SERVER)
631 name = ((SilcServerEntry)entry)->server_name;
632 else
633 name = ((SilcChannelEntry)entry)->channel_name;
634 if (!name)
635 break;
636
637 if (mode) {
638 silcpurple_get_chmode_string(mode, buf2, sizeof(buf2));
639 g_snprintf(buf, sizeof(buf),
640 _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
641 channel->channel_name, buf2);
642 } else {
643 g_snprintf(buf, sizeof(buf),
644 _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
645 channel->channel_name);
646 }
647 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
648 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
649 break;
650
651 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
652 {
653 PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
654 idtype = va_arg(va, int);
655 entry = va_arg(va, void *);
656 mode = va_arg(va, SilcUInt32);
657 client_entry2 = va_arg(va, SilcClientEntry);
658 channel = va_arg(va, SilcChannelEntry);
659
660 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
661 channel->channel_name, sg->account);
662 if (!convo)
663 break;
664
665 if (idtype == SILC_ID_CLIENT)
666 name = ((SilcClientEntry)entry)->nickname;
667 else if (idtype == SILC_ID_SERVER)
668 name = ((SilcServerEntry)entry)->server_name;
669 else
670 name = ((SilcChannelEntry)entry)->channel_name;
671 if (!name)
672 break;
673
674 if (mode) {
675 silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
676 g_snprintf(buf, sizeof(buf),
677 _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
678 client_entry2->nickname, buf2);
679 if (mode & SILC_CHANNEL_UMODE_CHANFO)
680 flags |= PURPLE_CBFLAGS_FOUNDER;
681 if (mode & SILC_CHANNEL_UMODE_CHANOP)
682 flags |= PURPLE_CBFLAGS_OP;
683 } else {
684 g_snprintf(buf, sizeof(buf),
685 _("<I>%s</I> removed all <I>%s's</I> modes"), name,
686 client_entry2->nickname);
687 }
688 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
689 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
690 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
691 break;
692 }
693
694 case SILC_NOTIFY_TYPE_MOTD:
695 tmp = va_arg(va, char *);
696 silc_free(sg->motd);
697 sg->motd = silc_memdup(tmp, strlen(tmp));
698 break;
699
700 case SILC_NOTIFY_TYPE_KICKED:
701 client_entry = va_arg(va, SilcClientEntry);
702 tmp = va_arg(va, char *);
703 client_entry2 = va_arg(va, SilcClientEntry);
704 channel = va_arg(va, SilcChannelEntry);
705
706 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
707 channel->channel_name, sg->account);
708 if (!convo)
709 break;
710
711 if (client_entry == conn->local_entry) {
712 /* Remove us from channel */
713 g_snprintf(buf, sizeof(buf),
714 _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
715 channel->channel_name, client_entry2->nickname,
716 tmp ? tmp : "");
717 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
718 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
719 serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
720 } else {
721 /* Remove user from channel */
722 g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
723 client_entry2->nickname, tmp ? tmp : "");
724 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
725 client_entry->nickname,
726 buf);
727 }
728
729 break;
730
731 case SILC_NOTIFY_TYPE_KILLED:
732 client_entry = va_arg(va, SilcClientEntry);
733 tmp = va_arg(va, char *);
734 idtype = va_arg(va, int);
735 entry = va_arg(va, SilcClientEntry);
736
737 if (!client_entry->nickname)
738 break;
739
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 : "");
756 }
757
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 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
762 chu->channel->channel_name, sg->account);
763 if (!convo)
764 continue;
765 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
766 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
767 serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
768 }
769 silc_hash_table_list_reset(&htl);
770
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 : "");
787 }
788
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 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
793 chu->channel->channel_name, sg->account);
794 if (!convo)
795 continue;
796 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
797 client_entry->nickname, tmp);
798 }
799 silc_hash_table_list_reset(&htl);
800 }
801
802 break;
803
804 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
805 break;
806
807 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
808 {
809 int i;
810 SilcClientEntry *clients;
811 SilcUInt32 clients_count;
812
813 (void)va_arg(va, void *);
814 clients = va_arg(va, SilcClientEntry *);
815 clients_count = va_arg(va, SilcUInt32);
816
817 for (i = 0; i < clients_count; i++) {
818 if (!clients[i]->nickname)
819 break;
820
821 /* Remove from all channels */
822 silc_hash_table_list(clients[i]->channels, &htl);
823 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
824 convo =
825 purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
826 chu->channel->channel_name, sg->account);
827 if (!convo)
828 continue;
829 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
830 clients[i]->nickname,
831 _("Server signoff"));
832 }
833 silc_hash_table_list_reset(&htl);
834 }
835 }
836 break;
837
838 case SILC_NOTIFY_TYPE_ERROR:
839 {
840 SilcStatus error = va_arg(va, int);
841 purple_notify_error(gc, "Error Notify",
842 silc_get_status_message(error),
843 NULL);
844 }
845 break;
846
847 case SILC_NOTIFY_TYPE_WATCH:
848 {
849 SilcPublicKey public_key;
850 unsigned char *pk;
851 SilcUInt32 pk_len;
852 char *fingerprint;
853
854 client_entry = va_arg(va, SilcClientEntry);
855 (void)va_arg(va, char *);
856 mode = va_arg(va, SilcUInt32);
857 notify = va_arg(va, int);
858 public_key = va_arg(va, SilcPublicKey);
859
860 b = NULL;
861 if (public_key) {
862 PurpleBlistNode *gnode, *cnode, *bnode;
863 const char *f;
864
865 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
866 if (!pk)
867 break;
868 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
869 for (i = 0; i < strlen(fingerprint); i++)
870 if (fingerprint[i] == ' ')
871 fingerprint[i] = '_';
872 g_snprintf(buf, sizeof(buf) - 1,
873 "%s" G_DIR_SEPARATOR_S "clientkeys"
874 G_DIR_SEPARATOR_S "clientkey_%s.pub",
875 silcpurple_silcdir(), fingerprint);
876 silc_free(fingerprint);
877 silc_free(pk);
878
879 /* Find buddy by associated public key */
880 for (gnode = purple_get_blist()->root; gnode;
881 gnode = gnode->next) {
882 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
883 continue;
884 for (cnode = gnode->child; cnode; cnode = cnode->next) {
885 if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode))
886 continue;
887 for (bnode = cnode->child; bnode;
888 bnode = bnode->next) {
889 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
890 continue;
891 b = (PurpleBuddy *)bnode;
892 if (b->account != gc->account)
893 continue;
894 f = purple_blist_node_get_string(bnode, "public-key");
895 if (f && purple_strequal(f, buf))
896 goto cont;
897 b = NULL;
898 }
899 }
900 }
901 }
902 cont:
903 if (!b) {
904 /* Find buddy by nickname */
905 b = purple_find_buddy(sg->account, client_entry->nickname);
906 if (!b) {
907 purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
908 client_entry->nickname);
909 break;
910 }
911 }
912
913 silc_free(b->proto_data);
914 b->proto_data = silc_memdup(client_entry->id,
915 sizeof(*client_entry->id));
916 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
917 break;
918 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
919 /* See if client was away and is now present */
920 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
921 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
922 SILC_UMODE_DETACHED)) &&
923 (client_entry->mode & SILC_UMODE_GONE ||
924 client_entry->mode & SILC_UMODE_INDISPOSED ||
925 client_entry->mode & SILC_UMODE_BUSY ||
926 client_entry->mode & SILC_UMODE_PAGE ||
927 client_entry->mode & SILC_UMODE_DETACHED)) {
928 client_entry->mode = mode;
929 purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
930 }
931 else if ((mode & SILC_UMODE_GONE) ||
932 (mode & SILC_UMODE_INDISPOSED) ||
933 (mode & SILC_UMODE_BUSY) ||
934 (mode & SILC_UMODE_PAGE) ||
935 (mode & SILC_UMODE_DETACHED)) {
936 client_entry->mode = mode;
937 purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
938 }
939 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
940 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
941 notify == SILC_NOTIFY_TYPE_KILLED) {
942 client_entry->mode = mode;
943 purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
944 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
945 client_entry->mode = mode;
946 purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
947 }
948 }
949 break;
950
951 default:
952 purple_debug_info("silc", "Unhandled notification: %d\n", type);
953 break;
954 }
955
956 va_end(va);
957 }
958
959
960 /* Command handler. This function is called always in the command function.
961 If error occurs it will be called as well. `conn' is the associated
962 client connection. `cmd_context' is the command context that was
963 originally sent to the command. `success' is FALSE if error occurred
964 during command. `command' is the command being processed. It must be
965 noted that this is not reply from server. This is merely called just
966 after application has called the command. Just to tell application
967 that the command really was processed. */
968
969 static void
silc_command(SilcClient client,SilcClientConnection conn,SilcClientCommandContext cmd_context,bool success,SilcCommand command,SilcStatus status)970 silc_command(SilcClient client, SilcClientConnection conn,
971 SilcClientCommandContext cmd_context, bool success,
972 SilcCommand command, SilcStatus status)
973 {
974 PurpleConnection *gc = client->application;
975 SilcPurple sg = gc->proto_data;
976
977 switch (command) {
978
979 case SILC_COMMAND_CMODE:
980 if (cmd_context->argc == 3 &&
981 purple_strequal((char *)cmd_context->argv[2], "+C"))
982 sg->chpk = TRUE;
983 else
984 sg->chpk = FALSE;
985 break;
986
987 default:
988 break;
989 }
990 }
991
992 #if 0
993 static void
994 silcpurple_whois_more(SilcClientEntry client_entry, gint id)
995 {
996 SilcAttributePayload attr;
997 SilcAttribute attribute;
998 char *buf;
999 GString *s;
1000 SilcVCardStruct vcard;
1001 int i;
1002
1003 if (id != 0)
1004 return;
1005
1006 memset(&vcard, 0, sizeof(vcard));
1007
1008 s = g_string_new("");
1009
1010 silc_dlist_start(client_entry->attrs);
1011 while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
1012 attribute = silc_attribute_get_attribute(attr);
1013 switch (attribute) {
1014
1015 case SILC_ATTRIBUTE_USER_INFO:
1016 if (!silc_attribute_get_object(attr, (void *)&vcard,
1017 sizeof(vcard)))
1018 continue;
1019 g_string_append_printf(s, "%s:\n\n", _("Personal Information"));
1020 if (vcard.full_name)
1021 g_string_append_printf(s, "%s:\t\t%s\n",
1022 _("Full Name"),
1023 vcard.full_name);
1024 if (vcard.first_name)
1025 g_string_append_printf(s, "%s:\t%s\n",
1026 _("First Name"),
1027 vcard.first_name);
1028 if (vcard.middle_names)
1029 g_string_append_printf(s, "%s:\t%s\n",
1030 _("Middle Name"),
1031 vcard.middle_names);
1032 if (vcard.family_name)
1033 g_string_append_printf(s, "%s:\t%s\n",
1034 _("Family Name"),
1035 vcard.family_name);
1036 if (vcard.nickname)
1037 g_string_append_printf(s, "%s:\t\t%s\n",
1038 _("Nickname"),
1039 vcard.nickname);
1040 if (vcard.bday)
1041 g_string_append_printf(s, "%s:\t\t%s\n",
1042 _("Birth Day"),
1043 vcard.bday);
1044 if (vcard.title)
1045 g_string_append_printf(s, "%s:\t\t%s\n",
1046 _("Job Title"),
1047 vcard.title);
1048 if (vcard.role)
1049 g_string_append_printf(s, "%s:\t\t%s\n",
1050 _("Job Role"),
1051 vcard.role);
1052 if (vcard.org_name)
1053 g_string_append_printf(s, "%s:\t%s\n",
1054 _("Organization"),
1055 vcard.org_name);
1056 if (vcard.org_unit)
1057 g_string_append_printf(s, "%s:\t\t%s\n",
1058 _("Unit"),
1059 vcard.org_unit);
1060 if (vcard.url)
1061 g_string_append_printf(s, "%s:\t%s\n",
1062 _("Homepage"),
1063 vcard.url);
1064 if (vcard.label)
1065 g_string_append_printf(s, "%s:\t%s\n",
1066 _("Address"),
1067 vcard.label);
1068 for (i = 0; i < vcard.num_tels; i++) {
1069 if (vcard.tels[i].telnum)
1070 g_string_append_printf(s, "%s:\t\t\t%s\n",
1071 _("Phone"),
1072 vcard.tels[i].telnum);
1073 }
1074 for (i = 0; i < vcard.num_emails; i++) {
1075 if (vcard.emails[i].address)
1076 g_string_append_printf(s, "%s:\t\t%s\n",
1077 _("Email"),
1078 vcard.emails[i].address);
1079 }
1080 if (vcard.note)
1081 g_string_append_printf(s, "\n%s:\t\t%s\n",
1082 _("Note"),
1083 vcard.note);
1084 break;
1085 }
1086 }
1087
1088 buf = g_string_free(s, FALSE);
1089 purple_notify_info(NULL, _("User Information"), _("User Information"),
1090 buf);
1091 g_free(buf);
1092 }
1093 #endif
1094
1095 /* Command reply handler. This function is called always in the command reply
1096 function. If error occurs it will be called as well. Normal scenario
1097 is that it will be called after the received command data has been parsed
1098 and processed. The function is used to pass the received command data to
1099 the application.
1100
1101 `conn' is the associated client connection. `cmd_payload' is the command
1102 payload data received from server and it can be ignored. It is provided
1103 if the application would like to re-parse the received command data,
1104 however, it must be noted that the data is parsed already by the library
1105 thus the payload can be ignored. `success' is FALSE if error occurred.
1106 In this case arguments are not sent to the application. The `status' is
1107 the command reply status server returned. The `command' is the command
1108 reply being processed. The function has variable argument list and each
1109 command defines the number and type of arguments it passes to the
1110 application (on error they are not sent). */
1111
1112 static void
silc_command_reply(SilcClient client,SilcClientConnection conn,SilcCommandPayload cmd_payload,bool success,SilcCommand command,SilcStatus status,...)1113 silc_command_reply(SilcClient client, SilcClientConnection conn,
1114 SilcCommandPayload cmd_payload, bool success,
1115 SilcCommand command, SilcStatus status, ...)
1116 {
1117 PurpleConnection *gc = client->application;
1118 SilcPurple sg = gc->proto_data;
1119 PurpleConversation *convo;
1120 va_list vp;
1121
1122 va_start(vp, status);
1123
1124 switch (command) {
1125 case SILC_COMMAND_JOIN:
1126 {
1127 SilcChannelEntry channel_entry;
1128
1129 if (!success) {
1130 purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
1131 silc_get_status_message(status));
1132 return;
1133 }
1134
1135 (void)va_arg(vp, char *);
1136 channel_entry = va_arg(vp, SilcChannelEntry);
1137
1138 /* Resolve users on channel */
1139 silc_client_get_clients_by_channel(client, conn, channel_entry,
1140 silcpurple_chat_join_done,
1141 channel_entry);
1142 }
1143 break;
1144
1145 case SILC_COMMAND_LEAVE:
1146 break;
1147
1148 case SILC_COMMAND_USERS:
1149 break;
1150
1151 case SILC_COMMAND_WHOIS:
1152 {
1153 SilcUInt32 idle, mode;
1154 SilcBuffer channels, user_modes;
1155 SilcClientEntry client_entry;
1156 char tmp[1024], *tmp2;
1157 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1158 PurpleNotifyUserInfo *user_info;
1159
1160 if (!success) {
1161 purple_notify_error(gc, _("User Information"),
1162 _("Cannot get user information"),
1163 silc_get_status_message(status));
1164 break;
1165 }
1166
1167 client_entry = va_arg(vp, SilcClientEntry);
1168 if (!client_entry->nickname)
1169 break;
1170 (void)va_arg(vp, char *);
1171 (void)va_arg(vp, char *);
1172 (void)va_arg(vp, char *);
1173 channels = va_arg(vp, SilcBuffer);
1174 mode = va_arg(vp, SilcUInt32);
1175 idle = va_arg(vp, SilcUInt32);
1176 (void)va_arg(vp, unsigned char *);
1177 user_modes = va_arg(vp, SilcBuffer);
1178
1179 user_info = purple_notify_user_info_new();
1180 tmp2 = g_markup_escape_text(client_entry->nickname, -1);
1181 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
1182 g_free(tmp2);
1183 if (client_entry->realname) {
1184 tmp2 = g_markup_escape_text(client_entry->realname, -1);
1185 purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
1186 g_free(tmp2);
1187 }
1188 if (client_entry->username) {
1189 tmp2 = g_markup_escape_text(client_entry->username, -1);
1190 if (client_entry->hostname) {
1191 gchar *tmp3;
1192 tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
1193 purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1194 g_free(tmp3);
1195 } else
1196 purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
1197 g_free(tmp2);
1198 }
1199
1200 if (client_entry->mode) {
1201 memset(tmp, 0, sizeof(tmp));
1202 silcpurple_get_umode_string(client_entry->mode,
1203 tmp, sizeof(tmp) - strlen(tmp));
1204 purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1205 }
1206
1207 silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1208 if (moodstr) {
1209 purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1210 g_free(moodstr);
1211 }
1212
1213 if (statusstr) {
1214 tmp2 = g_markup_escape_text(statusstr, -1);
1215 purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
1216 g_free(statusstr);
1217 g_free(tmp2);
1218 }
1219
1220 if (contactstr) {
1221 purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1222 g_free(contactstr);
1223 }
1224
1225 if (langstr) {
1226 purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1227 g_free(langstr);
1228 }
1229
1230 if (devicestr) {
1231 purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1232 g_free(devicestr);
1233 }
1234
1235 if (tzstr) {
1236 purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1237 g_free(tzstr);
1238 }
1239
1240 if (geostr) {
1241 purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1242 g_free(geostr);
1243 }
1244
1245 if (client_entry->server)
1246 purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1247
1248 if (channels && user_modes) {
1249 SilcUInt32 *umodes;
1250 SilcDList list =
1251 silc_channel_payload_parse_list(channels->data,
1252 channels->len);
1253 if (list && silc_get_mode_list(user_modes,
1254 silc_dlist_count(list),
1255 &umodes)) {
1256 SilcChannelPayload entry;
1257 int i = 0;
1258
1259 memset(tmp, 0, sizeof(tmp));
1260 silc_dlist_start(list);
1261 while ((entry = silc_dlist_get(list))
1262 != SILC_LIST_END) {
1263 SilcUInt32 name_len;
1264 char *m = silc_client_chumode_char(umodes[i++]);
1265 char *name = (char *)silc_channel_get_name(entry, &name_len);
1266 if (m)
1267 silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
1268 silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
1269 silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
1270 silc_free(m);
1271
1272 }
1273 tmp2 = g_markup_escape_text(tmp, -1);
1274 purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
1275 g_free(tmp2);
1276 silc_free(umodes);
1277 }
1278 }
1279
1280 if (client_entry->public_key) {
1281 char *fingerprint, *babbleprint;
1282 unsigned char *pk;
1283 SilcUInt32 pk_len;
1284 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1285 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1286 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1287 purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1288 purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1289 silc_free(fingerprint);
1290 silc_free(babbleprint);
1291 silc_free(pk);
1292 }
1293
1294 #if 0 /* XXX for now, let's not show attrs here */
1295 if (client_entry->attrs)
1296 purple_request_action(gc, _("User Information"),
1297 _("User Information"),
1298 buf, 1, client_entry, 2,
1299 _("OK"), G_CALLBACK(silcpurple_whois_more),
1300 _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
1301 else
1302 #endif
1303 purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
1304 purple_notify_user_info_destroy(user_info);
1305 }
1306 break;
1307
1308 case SILC_COMMAND_WHOWAS:
1309 {
1310 SilcClientEntry client_entry;
1311 char *nickname, *realname, *username, *tmp;
1312 PurpleNotifyUserInfo *user_info;
1313
1314 if (!success) {
1315 purple_notify_error(gc, _("User Information"),
1316 _("Cannot get user information"),
1317 silc_get_status_message(status));
1318 break;
1319 }
1320
1321 client_entry = va_arg(vp, SilcClientEntry);
1322 nickname = va_arg(vp, char *);
1323 username = va_arg(vp, char *);
1324 realname = va_arg(vp, char *);
1325 if (!nickname)
1326 break;
1327
1328 user_info = purple_notify_user_info_new();
1329 tmp = g_markup_escape_text(nickname, -1);
1330 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
1331 g_free(tmp);
1332 if (realname) {
1333 tmp = g_markup_escape_text(realname, -1);
1334 purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
1335 g_free(tmp);
1336 }
1337 if (username) {
1338 tmp = g_markup_escape_text(username, -1);
1339 if (client_entry && client_entry->hostname) {
1340 gchar *tmp3;
1341 tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
1342 purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1343 g_free(tmp3);
1344 } else
1345 purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
1346 g_free(tmp);
1347 }
1348 if (client_entry && client_entry->server)
1349 purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1350
1351
1352 if (client_entry && client_entry->public_key) {
1353 char *fingerprint, *babbleprint;
1354 unsigned char *pk;
1355 SilcUInt32 pk_len;
1356 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1357 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1358 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1359 purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1360 purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1361 silc_free(fingerprint);
1362 silc_free(babbleprint);
1363 silc_free(pk);
1364 }
1365
1366 purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
1367 purple_notify_user_info_destroy(user_info);
1368 }
1369 break;
1370
1371 case SILC_COMMAND_DETACH:
1372 if (!success) {
1373 purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
1374 silc_get_status_message(status));
1375 return;
1376 }
1377 break;
1378
1379 case SILC_COMMAND_TOPIC:
1380 {
1381 SilcChannelEntry channel;
1382
1383 if (!success) {
1384 purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
1385 silc_get_status_message(status));
1386 return;
1387 }
1388
1389 channel = va_arg(vp, SilcChannelEntry);
1390
1391 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1392 channel->channel_name, sg->account);
1393 if (!convo) {
1394 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1395 channel->channel_name);
1396 break;
1397 }
1398
1399 /* Set topic */
1400 if (channel->topic)
1401 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
1402 }
1403 break;
1404
1405 case SILC_COMMAND_NICK:
1406 {
1407 /* I don't think we should need to do this because the server should
1408 * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
1409 * nick, but it isn't, so we deal with it here instead. Stu. */
1410 SilcClientEntry local_entry;
1411 SilcHashTableList htl;
1412 SilcChannelUser chu;
1413 const char *oldnick;
1414
1415 if (!success) {
1416 purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
1417 silc_get_status_message(status));
1418 return;
1419 }
1420
1421 local_entry = va_arg(vp, SilcClientEntry);
1422
1423 /* Change nick on all channels */
1424 silc_hash_table_list(local_entry->channels, &htl);
1425 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1426 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1427 chu->channel->channel_name, sg->account);
1428 if (!convo)
1429 continue;
1430 oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
1431 if (!purple_strequal(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
1432 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
1433 oldnick, local_entry->nickname);
1434 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
1435 }
1436 }
1437 silc_hash_table_list_reset(&htl);
1438
1439 purple_connection_set_display_name(gc, local_entry->nickname);
1440 }
1441 break;
1442
1443 case SILC_COMMAND_LIST:
1444 {
1445 char *topic, *name;
1446 int usercount;
1447 PurpleRoomlistRoom *room;
1448
1449 if (sg->roomlist_cancelled)
1450 break;
1451
1452 if (!success) {
1453 purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
1454 silc_get_status_message(status));
1455 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1456 purple_roomlist_unref(sg->roomlist);
1457 sg->roomlist = NULL;
1458 return;
1459 }
1460
1461 (void)va_arg(vp, SilcChannelEntry);
1462 name = va_arg(vp, char *);
1463 if (!name) {
1464 purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
1465 silc_get_status_message(status));
1466 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1467 purple_roomlist_unref(sg->roomlist);
1468 sg->roomlist = NULL;
1469 return;
1470 }
1471 topic = va_arg(vp, char *);
1472 usercount = va_arg(vp, int);
1473
1474 room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
1475 purple_roomlist_room_add_field(sg->roomlist, room, name);
1476 purple_roomlist_room_add_field(sg->roomlist, room,
1477 SILC_32_TO_PTR(usercount));
1478 purple_roomlist_room_add_field(sg->roomlist, room,
1479 topic ? topic : "");
1480 purple_roomlist_room_add(sg->roomlist, room);
1481
1482 if (status == SILC_STATUS_LIST_END ||
1483 status == SILC_STATUS_OK) {
1484 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1485 purple_roomlist_unref(sg->roomlist);
1486 sg->roomlist = NULL;
1487 }
1488 }
1489 break;
1490
1491 case SILC_COMMAND_GETKEY:
1492 {
1493 SilcPublicKey public_key;
1494
1495 if (!success) {
1496 purple_notify_error(gc, _("Get Public Key"),
1497 _("Cannot fetch the public key"),
1498 silc_get_status_message(status));
1499 return;
1500 }
1501
1502 (void)va_arg(vp, SilcUInt32);
1503 (void)va_arg(vp, void *);
1504 public_key = va_arg(vp, SilcPublicKey);
1505
1506 if (!public_key)
1507 purple_notify_error(gc, _("Get Public Key"),
1508 _("Cannot fetch the public key"),
1509 _("No public key was received"));
1510 }
1511 break;
1512
1513 case SILC_COMMAND_INFO:
1514 {
1515
1516 char *server_name;
1517 char *server_info;
1518 char tmp[256];
1519
1520 if (!success) {
1521 purple_notify_error(gc, _("Server Information"),
1522 _("Cannot get server information"),
1523 silc_get_status_message(status));
1524 return;
1525 }
1526
1527 (void)va_arg(vp, SilcServerEntry);
1528 server_name = va_arg(vp, char *);
1529 server_info = va_arg(vp, char *);
1530
1531 if (server_name && server_info) {
1532 g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
1533 server_name, server_info);
1534 purple_notify_info(gc, NULL, _("Server Information"), tmp);
1535 }
1536 }
1537 break;
1538
1539 case SILC_COMMAND_STATS:
1540 {
1541 SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
1542 my_router_ops, cell_clients, cell_channels, cell_servers,
1543 clients, channels, servers, routers, server_ops, router_ops;
1544 SilcUInt32 buffer_length;
1545 SilcBufferStruct buf;
1546
1547 unsigned char *server_stats;
1548 char *msg;
1549
1550 if (!success) {
1551 purple_notify_error(gc, _("Server Statistics"),
1552 _("Cannot get server statistics"),
1553 silc_get_status_message(status));
1554 return;
1555 }
1556
1557 server_stats = va_arg(vp, unsigned char *);
1558 buffer_length = va_arg(vp, SilcUInt32);
1559 if (!server_stats || !buffer_length) {
1560 purple_notify_error(gc, _("Server Statistics"),
1561 _("No server statistics available"), NULL);
1562 break;
1563 }
1564 silc_buffer_set(&buf, server_stats, buffer_length);
1565 silc_buffer_unformat(&buf,
1566 SILC_STR_UI_INT(&starttime),
1567 SILC_STR_UI_INT(&uptime),
1568 SILC_STR_UI_INT(&my_clients),
1569 SILC_STR_UI_INT(&my_channels),
1570 SILC_STR_UI_INT(&my_server_ops),
1571 SILC_STR_UI_INT(&my_router_ops),
1572 SILC_STR_UI_INT(&cell_clients),
1573 SILC_STR_UI_INT(&cell_channels),
1574 SILC_STR_UI_INT(&cell_servers),
1575 SILC_STR_UI_INT(&clients),
1576 SILC_STR_UI_INT(&channels),
1577 SILC_STR_UI_INT(&servers),
1578 SILC_STR_UI_INT(&routers),
1579 SILC_STR_UI_INT(&server_ops),
1580 SILC_STR_UI_INT(&router_ops),
1581 SILC_STR_END);
1582
1583 msg = g_strdup_printf(_("Local server start time: %s\n"
1584 "Local server uptime: %s\n"
1585 "Local server clients: %d\n"
1586 "Local server channels: %d\n"
1587 "Local server operators: %d\n"
1588 "Local router operators: %d\n"
1589 "Local cell clients: %d\n"
1590 "Local cell channels: %d\n"
1591 "Local cell servers: %d\n"
1592 "Total clients: %d\n"
1593 "Total channels: %d\n"
1594 "Total servers: %d\n"
1595 "Total routers: %d\n"
1596 "Total server operators: %d\n"
1597 "Total router operators: %d\n"),
1598 silc_get_time(starttime),
1599 purple_str_seconds_to_string((int)uptime),
1600 (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
1601 (int)cell_clients, (int)cell_channels, (int)cell_servers,
1602 (int)clients, (int)channels, (int)servers, (int)routers,
1603 (int)server_ops, (int)router_ops);
1604
1605 purple_notify_info(gc, NULL,
1606 _("Network Statistics"), msg);
1607 g_free(msg);
1608 }
1609 break;
1610
1611 case SILC_COMMAND_PING:
1612 {
1613 if (!success) {
1614 purple_notify_error(gc, _("Ping"), _("Ping failed"),
1615 silc_get_status_message(status));
1616 return;
1617 }
1618
1619 purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
1620 NULL);
1621 }
1622 break;
1623
1624 case SILC_COMMAND_KILL:
1625 if (!success) {
1626 purple_notify_error(gc, _("Kill User"),
1627 _("Could not kill user"),
1628 silc_get_status_message(status));
1629 return;
1630 }
1631 break;
1632
1633 case SILC_COMMAND_CMODE:
1634 {
1635 SilcChannelEntry channel_entry;
1636 SilcBuffer channel_pubkeys;
1637
1638 if (!success)
1639 return;
1640
1641 channel_entry = va_arg(vp, SilcChannelEntry);
1642 (void)va_arg(vp, SilcUInt32);
1643 (void)va_arg(vp, SilcPublicKey);
1644 channel_pubkeys = va_arg(vp, SilcBuffer);
1645
1646 if (sg->chpk)
1647 silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
1648 }
1649 break;
1650
1651 default:
1652 if (success)
1653 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
1654 else
1655 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
1656 silc_get_status_message(status));
1657 break;
1658 }
1659
1660 va_end(vp);
1661 }
1662
1663
1664 /* Called to indicate that connection was either successfully established
1665 or connecting failed. This is also the first time application receives
1666 the SilcClientConnection object which it should save somewhere.
1667 If the `success' is FALSE the application must always call the function
1668 silc_client_close_connection. */
1669
1670 static void
silc_connected(SilcClient client,SilcClientConnection conn,SilcClientConnectionStatus status)1671 silc_connected(SilcClient client, SilcClientConnection conn,
1672 SilcClientConnectionStatus status)
1673 {
1674 PurpleConnection *gc = client->application;
1675 SilcPurple sg;
1676
1677 if (gc == NULL) {
1678 silc_client_close_connection(client, conn);
1679 return;
1680 }
1681 sg = gc->proto_data;
1682
1683 switch (status) {
1684 case SILC_CLIENT_CONN_SUCCESS:
1685 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1686 purple_connection_set_state(gc, PURPLE_CONNECTED);
1687
1688 /* Send the server our buddy list */
1689 silcpurple_send_buddylist(gc);
1690
1691 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1692
1693 /* Send any UMODEs configured for account */
1694 if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
1695 silc_client_command_call(sg->client, sg->conn, NULL,
1696 "UMODE", "+P", NULL);
1697 }
1698
1699 return;
1700 break;
1701 case SILC_CLIENT_CONN_ERROR:
1702 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1703 _("Error during connecting to SILC Server"));
1704 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1705 break;
1706
1707 case SILC_CLIENT_CONN_ERROR_KE:
1708 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
1709 _("Key Exchange failed"));
1710 break;
1711
1712 case SILC_CLIENT_CONN_ERROR_AUTH:
1713 purple_connection_error_reason(gc,
1714 PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
1715 _("Authentication failed"));
1716 break;
1717
1718 case SILC_CLIENT_CONN_ERROR_RESUME:
1719 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
1720 _("Resuming detached session failed. "
1721 "Press Reconnect to create new connection."));
1722 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1723 break;
1724
1725 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
1726 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1727 _("Connection timed out"));
1728 break;
1729 }
1730
1731 /* Error */
1732 sg->conn = NULL;
1733 silc_client_close_connection(client, conn);
1734 }
1735
1736
1737 /* Called to indicate that connection was disconnected to the server.
1738 The `status' may tell the reason of the disconnection, and if the
1739 `message' is non-NULL it may include the disconnection message
1740 received from server. */
1741
1742 static void
silc_disconnected(SilcClient client,SilcClientConnection conn,SilcStatus status,const char * message)1743 silc_disconnected(SilcClient client, SilcClientConnection conn,
1744 SilcStatus status, const char *message)
1745 {
1746 PurpleConnection *gc = client->application;
1747 SilcPurple sg = gc->proto_data;
1748
1749 if (sg->resuming && !sg->detaching)
1750 g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1751
1752 sg->conn = NULL;
1753
1754 /* Close the connection */
1755 if (!sg->detaching)
1756 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1757 _("Disconnected by server"));
1758 else
1759 /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
1760 purple_account_disconnect(purple_connection_get_account(gc));
1761 }
1762
1763
1764 typedef struct {
1765 SilcGetAuthMeth completion;
1766 void *context;
1767 } *SilcPurpleGetAuthMethod;
1768
1769 /* Callback called when we've received the authentication method information
1770 from the server after we've requested it. */
1771
silc_get_auth_method_callback(SilcClient client,SilcClientConnection conn,SilcAuthMethod auth_meth,void * context)1772 static void silc_get_auth_method_callback(SilcClient client,
1773 SilcClientConnection conn,
1774 SilcAuthMethod auth_meth,
1775 void *context)
1776 {
1777 SilcPurpleGetAuthMethod internal = context;
1778
1779 switch (auth_meth) {
1780 case SILC_AUTH_NONE:
1781 /* No authentication required. */
1782 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1783 break;
1784
1785 case SILC_AUTH_PASSWORD:
1786 /* By returning NULL here the library will ask the passphrase from us
1787 by calling the silc_ask_passphrase. */
1788 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1789 break;
1790
1791 case SILC_AUTH_PUBLIC_KEY:
1792 /* Do not get the authentication data now, the library will generate
1793 it using our default key, if we do not provide it here. */
1794 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1795 break;
1796 }
1797
1798 silc_free(internal);
1799 }
1800
1801 /* Find authentication method and authentication data by hostname and
1802 port. The hostname may be IP address as well. When the authentication
1803 method has been resolved the `completion' callback with the found
1804 authentication method and authentication data is called. The `conn'
1805 may be NULL. */
1806
1807 static void
silc_get_auth_method(SilcClient client,SilcClientConnection conn,char * hostname,SilcUInt16 port,SilcGetAuthMeth completion,void * context)1808 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1809 char *hostname, SilcUInt16 port,
1810 SilcGetAuthMeth completion, void *context)
1811 {
1812 PurpleConnection *gc = client->application;
1813 SilcPurple sg = gc->proto_data;
1814 SilcPurpleGetAuthMethod internal;
1815 const char *password;
1816
1817 /* Progress */
1818 if (sg->resuming)
1819 purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
1820 else
1821 purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
1822
1823 /* Check configuration if we have this connection configured. If we
1824 have then return that data immediately, as it's faster way. */
1825 if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
1826 completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
1827 return;
1828 }
1829 password = purple_connection_get_password(gc);
1830 if (password && *password) {
1831 completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
1832 return;
1833 }
1834
1835 /* Resolve the authentication method from server, as we may not know it. */
1836 internal = silc_calloc(1, sizeof(*internal));
1837 if (!internal)
1838 return;
1839 internal->completion = completion;
1840 internal->context = context;
1841 silc_client_request_authentication_method(client, conn,
1842 silc_get_auth_method_callback,
1843 internal);
1844 }
1845
1846
1847 /* Verifies received public key. The `conn_type' indicates which entity
1848 (server, client etc.) has sent the public key. If user decides to trust
1849 the application may save the key as trusted public key for later
1850 use. The `completion' must be called after the public key has been
1851 verified. */
1852
1853 static void
silc_verify_public_key(SilcClient client,SilcClientConnection conn,SilcSocketType conn_type,unsigned char * pk,SilcUInt32 pk_len,SilcSKEPKType pk_type,SilcVerifyPublicKey completion,void * context)1854 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1855 SilcSocketType conn_type, unsigned char *pk,
1856 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1857 SilcVerifyPublicKey completion, void *context)
1858 {
1859 PurpleConnection *gc = client->application;
1860 SilcPurple sg = gc->proto_data;
1861
1862 if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
1863 conn_type == SILC_SOCKET_TYPE_ROUTER)) {
1864 /* Progress */
1865 if (sg->resuming)
1866 purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
1867 else
1868 purple_connection_update_progress(gc, _("Verifying server public key"),
1869 3, 5);
1870 }
1871
1872 /* Verify public key */
1873 silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
1874 pk_len, pk_type, completion, context);
1875 }
1876
1877 typedef struct {
1878 SilcAskPassphrase completion;
1879 void *context;
1880 } *SilcPurpleAskPassphrase;
1881
1882 static void
silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal,const char * passphrase)1883 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
1884 {
1885 if (!passphrase || !(*passphrase))
1886 internal->completion(NULL, 0, internal->context);
1887 else
1888 internal->completion((unsigned char *)passphrase,
1889 strlen(passphrase), internal->context);
1890 silc_free(internal);
1891 }
1892
1893 /* Ask (interact, that is) a passphrase from user. The passphrase is
1894 returned to the library by calling the `completion' callback with
1895 the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1896 if not then the library will attempt to encode. */
1897
1898 static void
silc_ask_passphrase(SilcClient client,SilcClientConnection conn,SilcAskPassphrase completion,void * context)1899 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1900 SilcAskPassphrase completion, void *context)
1901 {
1902 PurpleConnection *gc = client->application;
1903 SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal));
1904
1905 if (!internal)
1906 return;
1907 internal->completion = completion;
1908 internal->context = context;
1909 purple_request_input(gc, _("Passphrase"), NULL,
1910 _("Passphrase required"), NULL, FALSE, TRUE, NULL,
1911 _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
1912 _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
1913 purple_connection_get_account(gc), NULL, NULL, internal);
1914 }
1915
1916
1917 /* Notifies application that failure packet was received. This is called
1918 if there is some protocol active in the client. The `protocol' is the
1919 protocol context. The `failure' is opaque pointer to the failure
1920 indication. Note, that the `failure' is protocol dependant and
1921 application must explicitly cast it to correct type. Usually `failure'
1922 is 32 bit failure type (see protocol specs for all protocol failure
1923 types). */
1924
1925 static void
silc_failure(SilcClient client,SilcClientConnection conn,SilcProtocol protocol,void * failure)1926 silc_failure(SilcClient client, SilcClientConnection conn,
1927 SilcProtocol protocol, void *failure)
1928 {
1929 PurpleConnection *gc = client->application;
1930 char buf[128];
1931
1932 memset(buf, 0, sizeof(buf));
1933
1934 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1935 SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
1936
1937 if (status == SILC_SKE_STATUS_BAD_VERSION)
1938 g_snprintf(buf, sizeof(buf),
1939 _("Failure: Version mismatch, upgrade your client"));
1940 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1941 g_snprintf(buf, sizeof(buf),
1942 _("Failure: Remote does not trust/support your public key"));
1943 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1944 g_snprintf(buf, sizeof(buf),
1945 _("Failure: Remote does not support proposed KE group"));
1946 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1947 g_snprintf(buf, sizeof(buf),
1948 _("Failure: Remote does not support proposed cipher"));
1949 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1950 g_snprintf(buf, sizeof(buf),
1951 _("Failure: Remote does not support proposed PKCS"));
1952 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1953 g_snprintf(buf, sizeof(buf),
1954 _("Failure: Remote does not support proposed hash function"));
1955 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1956 g_snprintf(buf, sizeof(buf),
1957 _("Failure: Remote does not support proposed HMAC"));
1958 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1959 g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
1960 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1961 g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
1962
1963 /* Show the error on the progress bar. A more generic error message
1964 is going to be showed to user after this in the silc_connected. */
1965 purple_connection_update_progress(gc, buf, 2, 5);
1966 }
1967
1968 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1969 SilcUInt32 err = SILC_PTR_TO_32(failure);
1970
1971 if (err == SILC_AUTH_FAILED)
1972 g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
1973
1974 /* Show the error on the progress bar. A more generic error message
1975 is going to be showed to user after this in the silc_connected. */
1976 purple_connection_update_progress(gc, buf, 4, 5);
1977 }
1978 }
1979
1980 /* Asks whether the user would like to perform the key agreement protocol.
1981 This is called after we have received an key agreement packet or an
1982 reply to our key agreement packet. This returns TRUE if the user wants
1983 the library to perform the key agreement protocol and FALSE if it is not
1984 desired (application may start it later by calling the function
1985 silc_client_perform_key_agreement). If TRUE is returned also the
1986 `completion' and `context' arguments must be set by the application. */
1987
1988 static bool
silc_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,const char * hostname,SilcUInt16 port,SilcKeyAgreementCallback * completion,void ** context)1989 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1990 SilcClientEntry client_entry, const char *hostname,
1991 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1992 void **context)
1993 {
1994 silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
1995 *completion = NULL;
1996 *context = NULL;
1997 return FALSE;
1998 }
1999
2000
2001 /* Notifies application that file transfer protocol session is being
2002 requested by the remote client indicated by the `client_entry' from
2003 the `hostname' and `port'. The `session_id' is the file transfer
2004 session and it can be used to either accept or reject the file
2005 transfer request, by calling the silc_client_file_receive or
2006 silc_client_file_close, respectively. */
2007
2008 static void
silc_ftp(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcUInt32 session_id,const char * hostname,SilcUInt16 port)2009 silc_ftp(SilcClient client, SilcClientConnection conn,
2010 SilcClientEntry client_entry, SilcUInt32 session_id,
2011 const char *hostname, SilcUInt16 port)
2012 {
2013 silcpurple_ftp_request(client, conn, client_entry, session_id,
2014 hostname, port);
2015 }
2016
2017
2018 /* Delivers SILC session detachment data indicated by `detach_data' to the
2019 application. If application has issued SILC_COMMAND_DETACH command
2020 the client session in the SILC network is not quit. The client remains
2021 in the network but is detached. The detachment data may be used later
2022 to resume the session in the SILC Network. The appliation is
2023 responsible of saving the `detach_data', to for example in a file.
2024
2025 The detachment data can be given as argument to the functions
2026 silc_client_connect_to_server, or silc_client_add_connection when
2027 creating connection to remote server, inside SilcClientConnectionParams
2028 structure. If it is provided the client library will attempt to resume
2029 the session in the network. After the connection is created
2030 successfully, the application is responsible of setting the user
2031 interface for user into the same state it was before detaching (showing
2032 same channels, channel modes, etc). It can do this by fetching the
2033 information (like joined channels) from the client library. */
2034
2035 static void
silc_detach(SilcClient client,SilcClientConnection conn,const unsigned char * detach_data,SilcUInt32 detach_data_len)2036 silc_detach(SilcClient client, SilcClientConnection conn,
2037 const unsigned char *detach_data, SilcUInt32 detach_data_len)
2038 {
2039 PurpleConnection *gc = client->application;
2040 SilcPurple sg = gc->proto_data;
2041 const char *file;
2042
2043 /* Save the detachment data to file. */
2044 file = silcpurple_session_file(purple_account_get_username(sg->account));
2045 g_unlink(file);
2046 silc_file_writefile(file, (char *)detach_data, detach_data_len);
2047 }
2048
2049 SilcClientOperations ops = {
2050 silc_say,
2051 silc_channel_message,
2052 silc_private_message,
2053 silc_notify,
2054 silc_command,
2055 silc_command_reply,
2056 silc_connected,
2057 silc_disconnected,
2058 silc_get_auth_method,
2059 silc_verify_public_key,
2060 silc_ask_passphrase,
2061 silc_failure,
2062 silc_key_agreement,
2063 silc_ftp,
2064 silc_detach
2065 };
2066