1 /*
2
3 silcpurple_ops.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2004 - 2007 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 "internal.h"
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "silcpurple.h"
24 #include "imgstore.h"
25 #include "wb.h"
26
27 #include "glibcompat.h"
28
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);
44
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. */
50
silc_say(SilcClient client,SilcClientConnection conn,SilcClientMessageType type,char * msg,...)51 void silc_say(SilcClient client, SilcClientConnection conn,
52 SilcClientMessageType type, char *msg, ...)
53 {
54 char tmp[256];
55 va_list va;
56 PurpleConnection *gc = NULL;
57 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
58
59 va_start(va, msg);
60 silc_vsnprintf(tmp, sizeof(tmp), msg, va);
61 va_end(va);
62
63 if (type != SILC_CLIENT_MESSAGE_ERROR) {
64 purple_debug_misc("silc", "silc_say (%d) %s\n", type, tmp);
65 return;
66 }
67
68 purple_debug_error("silc", "silc_say error: %s\n", tmp);
69
70 if (purple_strequal(tmp, "Authentication failed"))
71 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
72
73 if (client != NULL)
74 gc = client->application;
75
76 if (gc != NULL)
77 purple_connection_error_reason(gc, reason, tmp);
78 else
79 purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp);
80 }
81
82 /* Processes incoming MIME message. Can be private message or channel
83 message. Returns TRUE if the message `mime' was displayed. */
84
85 static SilcBool
silcpurple_mime_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcChannelEntry channel,SilcMessagePayload payload,SilcChannelPrivateKey key,SilcMessageFlags flags,SilcMime mime,gboolean recursive)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)
91 {
92 PurpleConnection *gc = client->application;
93 SilcPurple sg = gc->proto_data;
94 const char *type;
95 const unsigned char *data;
96 SilcUInt32 data_len;
97 PurpleMessageFlags cflags = 0;
98 PurpleConversation *convo = NULL;
99 SilcBool ret = FALSE;
100
101 if (!mime)
102 return FALSE;
103
104 /* Check for fragmented MIME message */
105 if (silc_mime_is_partial(mime)) {
106 if (!sg->mimeass)
107 sg->mimeass = silc_mime_assembler_alloc();
108
109 /* Defragment */
110 mime = silc_mime_assemble(sg->mimeass, mime);
111 if (!mime)
112 /* More fragments to come */
113 return FALSE;
114
115 /* Process the complete message */
116 return silcpurple_mime_message(client, conn, sender, channel,
117 payload, key, flags, mime,
118 FALSE);
119 }
120
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);
126
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);
134 }
135 }
136
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;
148 }
149 }
150 }
151
152 goto out;
153 }
154
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;
162
163 /* Process according to content type */
164
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;
170
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;
182 }
183
184 /* Image */
185 if (strstr(type, "image/png") ||
186 strstr(type, "image/jpeg") ||
187 strstr(type, "image/gif") ||
188 strstr(type, "image/tiff")) {
189 char tmp[32];
190 int imgid;
191
192 /* Get channel convo (if message is for channel) */
193 if (key && channel) {
194 GList *l;
195 SilcPurplePrvgrp prv;
196
197 for (l = sg->grps; l; l = l->next)
198 if (((SilcPurplePrvgrp)l->data)->key == key) {
199 prv = l->data;
200 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
201 prv->channel, sg->account);
202 break;
203 }
204 }
205 if (channel && !convo)
206 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
207 channel->channel_name, sg->account);
208 if (channel && !convo)
209 goto out;
210
211 imgid = purple_imgstore_add_with_id(g_memdup2(data, data_len), data_len, "");
212 if (imgid) {
213 cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
214 g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
215
216 if (channel)
217 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
218 sender->nickname, cflags,
219 tmp, time(NULL));
220 else
221 serv_got_im(gc, sender->nickname,
222 tmp, cflags, time(NULL));
223
224 purple_imgstore_unref_by_id(imgid);
225 ret = TRUE;
226 }
227 goto out;
228 }
229
230 /* Whiteboard message */
231 if (strstr(type, "application/x-wb") &&
232 !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
233 if (channel)
234 silcpurple_wb_receive_ch(client, conn, sender, channel,
235 payload, flags, data, data_len);
236 else
237 silcpurple_wb_receive(client, conn, sender, payload,
238 flags, data, data_len);
239 ret = TRUE;
240 goto out;
241 }
242
243 out:
244 if (!recursive)
245 silc_mime_free(mime);
246 return ret;
247 }
248
249 /* Message for a channel. The `sender' is the sender of the message
250 The `channel' is the channel. The `message' is the message. Note
251 that `message' maybe NULL. The `flags' indicates message flags
252 and it is used to determine how the message can be interpreted
253 (like it may tell the message is multimedia message). */
254
255 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)256 silc_channel_message(SilcClient client, SilcClientConnection conn,
257 SilcClientEntry sender, SilcChannelEntry channel,
258 SilcMessagePayload payload,
259 SilcChannelPrivateKey key, SilcMessageFlags flags,
260 const unsigned char *message,
261 SilcUInt32 message_len)
262 {
263 PurpleConnection *gc = client->application;
264 SilcPurple sg = gc->proto_data;
265 PurpleConversation *convo = NULL;
266 char *msg, *tmp;
267
268 if (!message)
269 return;
270
271 if (key) {
272 GList *l;
273 SilcPurplePrvgrp prv;
274
275 for (l = sg->grps; l; l = l->next)
276 if (((SilcPurplePrvgrp)l->data)->key == key) {
277 prv = l->data;
278 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
279 prv->channel, sg->account);
280 break;
281 }
282 }
283 if (!convo)
284 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
285 channel->channel_name, sg->account);
286 if (!convo)
287 return;
288
289 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
290 purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
291 /* XXX */
292 }
293
294 if (flags & SILC_MESSAGE_FLAG_DATA) {
295 /* Process MIME message */
296 SilcMime mime;
297 mime = silc_mime_decode(NULL, message, message_len);
298 silcpurple_mime_message(client, conn, sender, channel, payload,
299 key, flags, mime, FALSE);
300 return;
301 }
302
303 if (flags & SILC_MESSAGE_FLAG_ACTION) {
304 msg = g_strdup_printf("/me %s",
305 (const char *)message);
306 if (!msg)
307 return;
308
309 tmp = g_markup_escape_text(msg, -1);
310 /* Send to Purple */
311 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
312 sender->nickname, 0, tmp, time(NULL));
313 g_free(tmp);
314 g_free(msg);
315 return;
316 }
317
318 if (flags & SILC_MESSAGE_FLAG_NOTICE) {
319 msg = g_strdup_printf("(notice) <I>%s</I> %s",
320 sender->nickname, (const char *)message);
321 if (!msg)
322 return;
323
324 /* Send to Purple */
325 purple_conversation_write(convo, NULL, (const char *)msg,
326 PURPLE_MESSAGE_SYSTEM, time(NULL));
327 g_free(msg);
328 return;
329 }
330
331 if (flags & SILC_MESSAGE_FLAG_UTF8) {
332 const char *msg = (const char *)message;
333 char *salvaged = NULL;
334 if (!g_utf8_validate((const char *)message, -1, NULL)) {
335 salvaged = purple_utf8_salvage((const char *)message);
336 msg = salvaged;
337 }
338 tmp = g_markup_escape_text(msg, -1);
339 /* Send to Purple */
340 serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
341 sender->nickname, 0, tmp, time(NULL));
342 g_free(salvaged);
343 g_free(tmp);
344 }
345 }
346
347
348 /* Private message to the client. The `sender' is the sender of the
349 message. The message is `message'and maybe NULL. The `flags'
350 indicates message flags and it is used to determine how the message
351 can be interpreted (like it may tell the message is multimedia
352 message). */
353
354 static void
silc_private_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcMessagePayload payload,SilcMessageFlags flags,const unsigned char * message,SilcUInt32 message_len)355 silc_private_message(SilcClient client, SilcClientConnection conn,
356 SilcClientEntry sender, SilcMessagePayload payload,
357 SilcMessageFlags flags, const unsigned char *message,
358 SilcUInt32 message_len)
359 {
360 PurpleConnection *gc = client->application;
361 SilcPurple sg = gc->proto_data;
362 PurpleConversation *convo;
363 char *msg, *tmp;
364
365 if (!message)
366 return;
367
368 /* XXX - Should this be PURPLE_CONV_TYPE_IM? */
369 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
370 sender->nickname, sg->account);
371
372 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
373 purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
374 /* XXX */
375 }
376
377 if (flags & SILC_MESSAGE_FLAG_DATA) {
378 /* Process MIME message */
379 SilcMime mime;
380 mime = silc_mime_decode(NULL, message, message_len);
381 silcpurple_mime_message(client, conn, sender, NULL, payload,
382 NULL, flags, mime, FALSE);
383 return;
384 }
385
386 if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
387 msg = g_strdup_printf("/me %s",
388 (const char *)message);
389 if (!msg)
390 return;
391
392 /* Send to Purple */
393 tmp = g_markup_escape_text(msg, -1);
394 serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
395 g_free(msg);
396 g_free(tmp);
397 return;
398 }
399
400 if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
401 msg = g_strdup_printf("(notice) <I>%s</I> %s",
402 sender->nickname, (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 const char *msg = (const char *)message;
415 char *salvaged = NULL;
416 if (!g_utf8_validate((const char *)message, -1, NULL)) {
417 salvaged = purple_utf8_salvage((const char *)message);
418 msg = salvaged;
419 }
420 tmp = g_markup_escape_text(msg, -1);
421 /* Send to Purple */
422 serv_got_im(gc, sender->nickname, tmp, 0, time(NULL));
423 g_free(salvaged);
424 g_free(tmp);
425 }
426 }
427
428
429 /* Notify message to the client. The notify arguments are sent in the
430 same order as servers sends them. The arguments are same as received
431 from the server except for ID's. If ID is received application receives
432 the corresponding entry to the ID. For example, if Client ID is received
433 application receives SilcClientEntry. Also, if the notify type is
434 for channel the channel entry is sent to application (even if server
435 does not send it because client library gets the channel entry from
436 the Channel ID in the packet's header). */
437
438 static void
silc_notify(SilcClient client,SilcClientConnection conn,SilcNotifyType type,...)439 silc_notify(SilcClient client, SilcClientConnection conn,
440 SilcNotifyType type, ...)
441 {
442 va_list va;
443 PurpleConnection *gc = client->application;
444 SilcPurple sg = gc->proto_data;
445 PurpleAccount *account = purple_connection_get_account(gc);
446 PurpleConversation *convo;
447 SilcClientEntry client_entry, client_entry2;
448 SilcChannelEntry channel;
449 SilcServerEntry server_entry;
450 SilcIdType idtype;
451 void *entry;
452 SilcUInt32 mode;
453 SilcHashTableList htl;
454 SilcChannelUser chu;
455 char buf[512], buf2[512], *tmp, *name;
456 SilcNotifyType notify;
457 PurpleBuddy *b;
458 SilcDList list;
459
460 va_start(va, type);
461 memset(buf, 0, sizeof(buf));
462
463 switch (type) {
464
465 case SILC_NOTIFY_TYPE_NONE:
466 break;
467
468 case SILC_NOTIFY_TYPE_INVITE:
469 {
470 GHashTable *components;
471 (void)va_arg(va, SilcChannelEntry);
472 name = va_arg(va, char *);
473 client_entry = va_arg(va, SilcClientEntry);
474
475 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
476 g_hash_table_insert(components, g_strdup("channel"), g_strdup(name));
477 serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
478 }
479 break;
480
481 case SILC_NOTIFY_TYPE_JOIN:
482 client_entry = va_arg(va, SilcClientEntry);
483 channel = va_arg(va, SilcChannelEntry);
484
485 /* If we joined channel, do nothing */
486 if (client_entry == conn->local_entry)
487 break;
488
489 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
490 channel->channel_name, sg->account);
491 if (!convo)
492 break;
493
494 /* Join user to channel */
495 g_snprintf(buf, sizeof(buf), "%s@%s",
496 client_entry->username, client_entry->hostname);
497 purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
498 client_entry->nickname, buf, PURPLE_CBFLAGS_NONE, TRUE);
499
500 break;
501
502 case SILC_NOTIFY_TYPE_LEAVE:
503 client_entry = va_arg(va, SilcClientEntry);
504 channel = va_arg(va, SilcChannelEntry);
505
506 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
507 channel->channel_name, sg->account);
508 if (!convo)
509 break;
510
511 /* Remove user from channel */
512 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
513 client_entry->nickname, NULL);
514
515 break;
516
517 case SILC_NOTIFY_TYPE_SIGNOFF:
518 client_entry = va_arg(va, SilcClientEntry);
519 tmp = va_arg(va, char *);
520
521 /* Remove from all channels */
522 silc_hash_table_list(client_entry->channels, &htl);
523 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
524 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
525 chu->channel->channel_name, sg->account);
526 if (!convo)
527 continue;
528 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
529 client_entry->nickname,
530 tmp);
531 }
532 silc_hash_table_list_reset(&htl);
533
534 break;
535
536 case SILC_NOTIFY_TYPE_TOPIC_SET:
537 {
538 char *esc, *tmp2;
539 idtype = va_arg(va, int);
540 entry = va_arg(va, void *);
541 tmp = va_arg(va, char *);
542 channel = va_arg(va, SilcChannelEntry);
543
544 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
545 channel->channel_name, sg->account);
546 if (!convo)
547 break;
548
549 if (!tmp)
550 break;
551
552 esc = g_markup_escape_text(tmp, -1);
553 tmp2 = purple_markup_linkify(esc);
554 g_free(esc);
555
556 if (idtype == SILC_ID_CLIENT) {
557 client_entry = (SilcClientEntry)entry;
558 g_snprintf(buf, sizeof(buf),
559 _("%s has changed the topic of <I>%s</I> to: %s"),
560 client_entry->nickname, channel->channel_name, tmp2);
561 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
562 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
563 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
564 client_entry->nickname, tmp);
565 } else if (idtype == SILC_ID_SERVER) {
566 server_entry = (SilcServerEntry)entry;
567 g_snprintf(buf, sizeof(buf),
568 _("%s has changed the topic of <I>%s</I> to: %s"),
569 server_entry->server_name, channel->channel_name, tmp2);
570 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
571 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
572 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
573 server_entry->server_name, tmp);
574 } else if (idtype == SILC_ID_CHANNEL) {
575 channel = (SilcChannelEntry)entry;
576 g_snprintf(buf, sizeof(buf),
577 _("%s has changed the topic of <I>%s</I> to: %s"),
578 channel->channel_name, channel->channel_name, tmp2);
579 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
580 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
581 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
582 channel->channel_name, tmp);
583 } else {
584 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
585 }
586
587 g_free(tmp2);
588
589 break;
590
591 }
592 case SILC_NOTIFY_TYPE_NICK_CHANGE:
593 client_entry = va_arg(va, SilcClientEntry);
594 tmp = va_arg(va, char *); /* Old nick */
595 name = va_arg(va, char *); /* New nick */
596
597 if (purple_strequal(tmp, name))
598 break;
599
600 /* Change nick on all channels */
601 silc_hash_table_list(client_entry->channels, &htl);
602 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
603 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
604 chu->channel->channel_name, sg->account);
605 if (!convo)
606 continue;
607 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
608 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
609 tmp, name);
610 }
611 silc_hash_table_list_reset(&htl);
612
613 break;
614
615 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
616 idtype = va_arg(va, int);
617 entry = va_arg(va, void *);
618 mode = va_arg(va, SilcUInt32);
619 (void)va_arg(va, char *);
620 (void)va_arg(va, char *);
621 (void)va_arg(va, char *);
622 (void)va_arg(va, SilcPublicKey);
623 (void)va_arg(va, SilcDList);
624 channel = va_arg(va, SilcChannelEntry);
625
626 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
627 channel->channel_name, sg->account);
628 if (!convo)
629 break;
630
631 if (idtype == SILC_ID_CLIENT)
632 name = ((SilcClientEntry)entry)->nickname;
633 else if (idtype == SILC_ID_SERVER)
634 name = ((SilcServerEntry)entry)->server_name;
635 else
636 name = ((SilcChannelEntry)entry)->channel_name;
637 if (!name)
638 break;
639
640 if (mode) {
641 silcpurple_get_chmode_string(mode, buf2, sizeof(buf2));
642 g_snprintf(buf, sizeof(buf),
643 _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
644 channel->channel_name, buf2);
645 } else {
646 g_snprintf(buf, sizeof(buf),
647 _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
648 channel->channel_name);
649 }
650 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
651 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
652 break;
653
654 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
655 {
656 PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_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);
662
663 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
664 channel->channel_name, sg->account);
665 if (!convo)
666 break;
667
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;
676
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_CBFLAGS_FOUNDER;
684 if (mode & SILC_CHANNEL_UMODE_CHANOP)
685 flags |= PURPLE_CBFLAGS_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);
690 }
691 purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
692 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
693 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
694 break;
695 }
696
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;
702
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);
708
709 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
710 channel->channel_name, sg->account);
711 if (!convo)
712 break;
713
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_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
721 buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
722 serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
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_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
728 client_entry->nickname,
729 buf);
730 }
731
732 break;
733
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);
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 (void)va_arg(va, void *);
809 list = va_arg(va, SilcDList);
810
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 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
817 chu->channel->channel_name, sg->account);
818 if (!convo)
819 continue;
820 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
821 client_entry->nickname,
822 _("Server signoff"));
823 }
824 silc_hash_table_list_reset(&htl);
825 }
826 break;
827
828 case SILC_NOTIFY_TYPE_ERROR:
829 {
830 SilcStatus error = va_arg(va, int);
831 purple_notify_error(gc, "Error Notify",
832 silc_get_status_message(error),
833 NULL);
834 }
835 break;
836
837 case SILC_NOTIFY_TYPE_WATCH:
838 {
839 SilcPublicKey public_key;
840 unsigned char *pk;
841 SilcUInt32 pk_len;
842 char *fingerprint;
843
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);
849
850 b = NULL;
851 if (public_key) {
852 GSList *buddies;
853 const char *f;
854 gsize i;
855
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);
869
870 /* Find buddy by associated public key */
871 for (buddies = purple_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;
878 }
879 }
880 cont:
881 if (!b) {
882 /* Find buddy by nickname */
883 b = purple_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;
888 }
889 }
890
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_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
908 }
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_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
916 }
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_prpl_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_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
925 }
926 }
927 break;
928
929 default:
930 purple_debug_info("silc", "Unhandled notification: %d\n", type);
931 break;
932 }
933
934 va_end(va);
935 }
936
937
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. */
947
948 static void
silc_command(SilcClient client,SilcClientConnection conn,SilcBool success,SilcCommand command,SilcStatus status,SilcUInt32 argc,unsigned char ** argv)949 silc_command(SilcClient client, SilcClientConnection conn,
950 SilcBool success, SilcCommand command, SilcStatus status,
951 SilcUInt32 argc, unsigned char **argv)
952 {
953 PurpleConnection *gc = client->application;
954 SilcPurple sg = gc->proto_data;
955
956 switch (command) {
957
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;
964
965 default:
966 break;
967 }
968 }
969
970 #if 0
971 static void
972 silcpurple_whois_more(SilcClientEntry client_entry, gint id)
973 {
974 SilcAttributePayload attr;
975 SilcAttribute attribute;
976 GString *s;
977 SilcVCardStruct vcard;
978 int i;
979
980 if (id != 0)
981 return;
982
983 memset(&vcard, 0, sizeof(vcard));
984
985 s = g_string_new("");
986
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) {
991
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);
1050 }
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);
1056 }
1057 if (vcard.note)
1058 g_string_append_printf(s, "\n%s:\t\t%s\n",
1059 _("Note"),
1060 vcard.note);
1061 break;
1062 }
1063 }
1064
1065 purple_notify_info(NULL, _("User Information"), _("User Information"),
1066 s->str);
1067 g_string_free(s, TRUE);
1068 }
1069 #endif
1070
1071
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).
1080
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. */
1084
1085 static void
silc_command_reply(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,va_list ap)1086 silc_command_reply(SilcClient client, SilcClientConnection conn,
1087 SilcCommand command, SilcStatus status,
1088 SilcStatus error, va_list ap)
1089 {
1090 PurpleConnection *gc = client->application;
1091 SilcPurple sg = gc->proto_data;
1092 PurpleConversation *convo;
1093
1094 switch (command) {
1095 case SILC_COMMAND_JOIN:
1096 {
1097 SilcChannelEntry channel;
1098 PurpleConversation *convo;
1099 SilcHashTableList *user_list;
1100 SilcChannelUser chu;
1101 GList *users = NULL, *flags = NULL;
1102 char tmp[256], *topic;
1103
1104 if (status != SILC_STATUS_OK) {
1105 purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
1106 silc_get_status_message(error));
1107 return;
1108 }
1109
1110 (void)va_arg(ap, char *);
1111 channel = va_arg(ap, SilcChannelEntry);
1112 (void)va_arg(ap, SilcUInt32);
1113 user_list = va_arg(ap, SilcHashTableList *);
1114 topic = va_arg(ap, char *);
1115
1116 /* Add channel to Purple */
1117 channel->context = SILC_32_TO_PTR(++sg->channel_ids);
1118 serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
1119 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1120 channel->channel_name, sg->account);
1121 if (!convo)
1122 return;
1123
1124 /* Add all users to channel */
1125 while (silc_hash_table_get(user_list, NULL, (void *)&chu)) {
1126 PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE;
1127 chu->context = SILC_32_TO_PTR(sg->channel_ids);
1128
1129 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO)
1130 f |= PURPLE_CBFLAGS_FOUNDER;
1131 if (chu->mode & SILC_CHANNEL_UMODE_CHANOP)
1132 f |= PURPLE_CBFLAGS_OP;
1133 users = g_list_append(users, chu->client->nickname);
1134 flags = g_list_append(flags, GINT_TO_POINTER(f));
1135
1136 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
1137 if (chu->client == conn->local_entry)
1138 g_snprintf(tmp, sizeof(tmp),
1139 _("You are channel founder on <I>%s</I>"),
1140 channel->channel_name);
1141 else
1142 g_snprintf(tmp, sizeof(tmp),
1143 _("Channel founder on <I>%s</I> is <I>%s</I>"),
1144 channel->channel_name, chu->client->nickname);
1145
1146 purple_conversation_write(convo, NULL, tmp,
1147 PURPLE_MESSAGE_SYSTEM, time(NULL));
1148 }
1149 }
1150
1151 purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE);
1152 g_list_free(users);
1153 g_list_free(flags);
1154
1155 /* Set topic */
1156 if (topic)
1157 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic);
1158
1159 /* Set nick */
1160 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname);
1161 }
1162 break;
1163
1164 case SILC_COMMAND_LEAVE:
1165 break;
1166
1167 case SILC_COMMAND_USERS:
1168 break;
1169
1170 case SILC_COMMAND_WHOIS:
1171 {
1172 SilcUInt32 *user_modes;
1173 SilcDList channels;
1174 SilcClientEntry client_entry;
1175 char tmp[1024], *tmp2;
1176 char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1177 PurpleNotifyUserInfo *user_info;
1178
1179 if (status != SILC_STATUS_OK) {
1180 purple_notify_error(gc, _("User Information"),
1181 _("Cannot get user information"),
1182 silc_get_status_message(error));
1183 break;
1184 }
1185
1186 client_entry = va_arg(ap, SilcClientEntry);
1187 (void)va_arg(ap, char *);
1188 (void)va_arg(ap, char *);
1189 (void)va_arg(ap, char *);
1190 channels = va_arg(ap, SilcDList);
1191 (void)va_arg(ap, SilcUInt32);
1192 va_arg(ap, SilcUInt32); /* idle */
1193 (void)va_arg(ap, unsigned char *);
1194 user_modes = va_arg(ap, SilcUInt32 *);
1195
1196 user_info = purple_notify_user_info_new();
1197 tmp2 = g_markup_escape_text(client_entry->nickname, -1);
1198 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
1199 g_free(tmp2);
1200 if (client_entry->realname) {
1201 tmp2 = g_markup_escape_text(client_entry->realname, -1);
1202 purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
1203 g_free(tmp2);
1204 }
1205 tmp2 = g_markup_escape_text(client_entry->username, -1);
1206 if (*client_entry->hostname) {
1207 gchar *tmp3;
1208 tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
1209 purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1210 g_free(tmp3);
1211 } else
1212 purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
1213 g_free(tmp2);
1214
1215 if (client_entry->mode) {
1216 memset(tmp, 0, sizeof(tmp));
1217 silcpurple_get_umode_string(client_entry->mode,
1218 tmp, sizeof(tmp) - strlen(tmp));
1219 purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1220 }
1221
1222 silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1223 if (moodstr) {
1224 purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1225 g_free(moodstr);
1226 }
1227
1228 if (statusstr) {
1229 tmp2 = g_markup_escape_text(statusstr, -1);
1230 purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
1231 g_free(statusstr);
1232 g_free(tmp2);
1233 }
1234
1235 if (contactstr) {
1236 purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1237 g_free(contactstr);
1238 }
1239
1240 if (langstr) {
1241 purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1242 g_free(langstr);
1243 }
1244
1245 if (devicestr) {
1246 purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1247 g_free(devicestr);
1248 }
1249
1250 if (tzstr) {
1251 purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1252 g_free(tzstr);
1253 }
1254
1255 if (geostr) {
1256 purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1257 g_free(geostr);
1258 }
1259
1260 if (*client_entry->server)
1261 purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1262
1263 if (channels && user_modes) {
1264 SilcChannelPayload entry;
1265 int i = 0;
1266
1267 memset(tmp, 0, sizeof(tmp));
1268 silc_dlist_start(channels);
1269 while ((entry = silc_dlist_get(channels))) {
1270 SilcUInt32 name_len;
1271 char *m = silc_client_chumode_char(user_modes[i++]);
1272 char *name = (char *)silc_channel_get_name(entry, &name_len);
1273 if (m)
1274 silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
1275 silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
1276 silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
1277 silc_free(m);
1278 }
1279 tmp2 = g_markup_escape_text(tmp, -1);
1280 purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
1281 g_free(tmp2);
1282 }
1283
1284 if (client_entry->public_key) {
1285 char *fingerprint, *babbleprint;
1286 unsigned char *pk;
1287 SilcUInt32 pk_len;
1288 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1289 if (pk) {
1290 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1291 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1292 purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1293 purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1294 silc_free(fingerprint);
1295 silc_free(babbleprint);
1296 silc_free(pk);
1297 }
1298 }
1299
1300 #if 0 /* XXX for now, let's not show attrs here */
1301 if (client_entry->attrs)
1302 purple_request_action(gc, _("User Information"),
1303 _("User Information"),
1304 buf, 1, client_entry, 2,
1305 _("OK"), G_CALLBACK(silcpurple_whois_more),
1306 _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
1307 else
1308 #endif /* 0 */
1309 purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
1310 purple_notify_user_info_destroy(user_info);
1311 }
1312 break;
1313
1314 case SILC_COMMAND_WHOWAS:
1315 {
1316 SilcClientEntry client_entry;
1317 char *nickname, *realname, *username, *tmp;
1318 PurpleNotifyUserInfo *user_info;
1319
1320 if (status != SILC_STATUS_OK) {
1321 purple_notify_error(gc, _("User Information"),
1322 _("Cannot get user information"),
1323 silc_get_status_message(error));
1324 break;
1325 }
1326
1327 client_entry = va_arg(ap, SilcClientEntry);
1328 nickname = va_arg(ap, char *);
1329 username = va_arg(ap, char *);
1330 realname = va_arg(ap, char *);
1331 if (!nickname)
1332 break;
1333
1334 user_info = purple_notify_user_info_new();
1335 tmp = g_markup_escape_text(nickname, -1);
1336 purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
1337 g_free(tmp);
1338 if (realname) {
1339 tmp = g_markup_escape_text(realname, -1);
1340 purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
1341 g_free(tmp);
1342 }
1343 if (username) {
1344 tmp = g_markup_escape_text(username, -1);
1345 if (client_entry && *client_entry->hostname) {
1346 gchar *tmp3;
1347 tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
1348 purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1349 g_free(tmp3);
1350 } else
1351 purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
1352 g_free(tmp);
1353 }
1354 if (client_entry && *client_entry->server)
1355 purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1356
1357
1358 if (client_entry && client_entry->public_key) {
1359 char *fingerprint, *babbleprint;
1360 unsigned char *pk;
1361 SilcUInt32 pk_len;
1362 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1363 if (pk) {
1364 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1365 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1366 purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1367 purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1368 silc_free(fingerprint);
1369 silc_free(babbleprint);
1370 silc_free(pk);
1371 }
1372 }
1373
1374 purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
1375 purple_notify_user_info_destroy(user_info);
1376 }
1377 break;
1378
1379 case SILC_COMMAND_DETACH:
1380 {
1381 const char *file;
1382 SilcBuffer detach_data;
1383
1384 if (status != SILC_STATUS_OK) {
1385 purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
1386 silc_get_status_message(error));
1387 return;
1388 }
1389
1390 detach_data = va_arg(ap, SilcBuffer);
1391
1392 /* Save the detachment data to file. */
1393 file = silcpurple_session_file(purple_account_get_username(sg->account));
1394 g_unlink(file);
1395 silc_file_writefile(file, (const char *)silc_buffer_data(detach_data),
1396 silc_buffer_len(detach_data));
1397 }
1398 break;
1399
1400 case SILC_COMMAND_TOPIC:
1401 {
1402 SilcChannelEntry channel;
1403
1404 if (status != SILC_STATUS_OK) {
1405 purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
1406 silc_get_status_message(error));
1407 return;
1408 }
1409
1410 channel = va_arg(ap, SilcChannelEntry);
1411
1412 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1413 channel->channel_name, sg->account);
1414 if (!convo) {
1415 purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1416 channel->channel_name);
1417 break;
1418 }
1419
1420 /* Set topic */
1421 if (channel->topic)
1422 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
1423 }
1424 break;
1425
1426 case SILC_COMMAND_NICK:
1427 {
1428 SilcClientEntry local_entry;
1429 SilcHashTableList htl;
1430 SilcChannelUser chu;
1431 const char *oldnick, *newnick;
1432
1433 if (status != SILC_STATUS_OK) {
1434 purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
1435 silc_get_status_message(error));
1436 return;
1437 }
1438
1439 local_entry = va_arg(ap, SilcClientEntry);
1440 newnick = va_arg(ap, char *);
1441
1442 /* Change nick on all channels */
1443 silc_hash_table_list(local_entry->channels, &htl);
1444 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1445 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1446 chu->channel->channel_name, sg->account);
1447 if (!convo)
1448 continue;
1449 oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
1450 if (!purple_strequal(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) {
1451 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
1452 oldnick, newnick);
1453 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick);
1454 }
1455 }
1456 silc_hash_table_list_reset(&htl);
1457
1458 purple_connection_set_display_name(gc, newnick);
1459 }
1460 break;
1461
1462 case SILC_COMMAND_LIST:
1463 {
1464 char *topic, *name;
1465 int usercount;
1466 PurpleRoomlistRoom *room;
1467
1468 if (sg->roomlist_cancelled)
1469 break;
1470
1471 if (error != SILC_STATUS_OK) {
1472 purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
1473 silc_get_status_message(error));
1474 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1475 purple_roomlist_unref(sg->roomlist);
1476 sg->roomlist = NULL;
1477 return;
1478 }
1479
1480 (void)va_arg(ap, SilcChannelEntry);
1481 name = va_arg(ap, char *);
1482 if (!name) {
1483 purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
1484 _("Network is empty"));
1485 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1486 purple_roomlist_unref(sg->roomlist);
1487 sg->roomlist = NULL;
1488 return;
1489 }
1490 topic = va_arg(ap, char *);
1491 usercount = va_arg(ap, int);
1492
1493 room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
1494 purple_roomlist_room_add_field(sg->roomlist, room, name);
1495 purple_roomlist_room_add_field(sg->roomlist, room,
1496 SILC_32_TO_PTR(usercount));
1497 purple_roomlist_room_add_field(sg->roomlist, room,
1498 topic ? topic : "");
1499 purple_roomlist_room_add(sg->roomlist, room);
1500
1501 if (status == SILC_STATUS_LIST_END ||
1502 status == SILC_STATUS_OK) {
1503 purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1504 purple_roomlist_unref(sg->roomlist);
1505 sg->roomlist = NULL;
1506 }
1507 }
1508 break;
1509
1510 case SILC_COMMAND_GETKEY:
1511 {
1512 SilcPublicKey public_key;
1513
1514 if (status != SILC_STATUS_OK) {
1515 purple_notify_error(gc, _("Get Public Key"),
1516 _("Cannot fetch the public key"),
1517 silc_get_status_message(error));
1518 return;
1519 }
1520
1521 (void)va_arg(ap, SilcUInt32);
1522 (void)va_arg(ap, void *);
1523 public_key = va_arg(ap, SilcPublicKey);
1524
1525 if (!public_key)
1526 purple_notify_error(gc, _("Get Public Key"),
1527 _("Cannot fetch the public key"),
1528 _("No public key was received"));
1529 }
1530 break;
1531
1532 case SILC_COMMAND_INFO:
1533 {
1534
1535 char *server_name;
1536 char *server_info;
1537 char tmp[256];
1538
1539 if (status != SILC_STATUS_OK) {
1540 purple_notify_error(gc, _("Server Information"),
1541 _("Cannot get server information"),
1542 silc_get_status_message(error));
1543 return;
1544 }
1545
1546 (void)va_arg(ap, SilcServerEntry);
1547 server_name = va_arg(ap, char *);
1548 server_info = va_arg(ap, char *);
1549
1550 if (server_name && server_info) {
1551 g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
1552 server_name, server_info);
1553 purple_notify_info(gc, NULL, _("Server Information"), tmp);
1554 }
1555 }
1556 break;
1557
1558 case SILC_COMMAND_STATS:
1559 {
1560 SilcClientStats *stats;
1561 char *msg;
1562
1563 if (status != SILC_STATUS_OK) {
1564 purple_notify_error(gc, _("Server Statistics"),
1565 _("Cannot get server statistics"),
1566 silc_get_status_message(error));
1567 return;
1568 }
1569
1570 stats = va_arg(ap, SilcClientStats *);
1571
1572 msg = g_strdup_printf(_("Local server start time: %s\n"
1573 "Local server uptime: %s\n"
1574 "Local server clients: %d\n"
1575 "Local server channels: %d\n"
1576 "Local server operators: %d\n"
1577 "Local router operators: %d\n"
1578 "Local cell clients: %d\n"
1579 "Local cell channels: %d\n"
1580 "Local cell servers: %d\n"
1581 "Total clients: %d\n"
1582 "Total channels: %d\n"
1583 "Total servers: %d\n"
1584 "Total routers: %d\n"
1585 "Total server operators: %d\n"
1586 "Total router operators: %d\n"),
1587 silc_time_string(stats->starttime),
1588 purple_str_seconds_to_string((int)stats->uptime),
1589 (int)stats->my_clients,
1590 (int)stats->my_channels,
1591 (int)stats->my_server_ops,
1592 (int)stats->my_router_ops,
1593 (int)stats->cell_clients,
1594 (int)stats->cell_channels,
1595 (int)stats->cell_servers,
1596 (int)stats->clients,
1597 (int)stats->channels,
1598 (int)stats->servers,
1599 (int)stats->routers,
1600 (int)stats->server_ops,
1601 (int)stats->router_ops);
1602
1603 purple_notify_info(gc, NULL,
1604 _("Network Statistics"), msg);
1605 g_free(msg);
1606 }
1607 break;
1608
1609 case SILC_COMMAND_PING:
1610 {
1611 if (status != SILC_STATUS_OK) {
1612 purple_notify_error(gc, _("Ping"), _("Ping failed"),
1613 silc_get_status_message(error));
1614 return;
1615 }
1616
1617 purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
1618 NULL);
1619 }
1620 break;
1621
1622 case SILC_COMMAND_KILL:
1623 if (status != SILC_STATUS_OK) {
1624 purple_notify_error(gc, _("Kill User"),
1625 _("Could not kill user"),
1626 silc_get_status_message(error));
1627 return;
1628 }
1629 break;
1630
1631 case SILC_COMMAND_CMODE:
1632 {
1633 SilcChannelEntry channel_entry;
1634 SilcDList channel_pubkeys, list;
1635 SilcArgumentDecodedList e;
1636
1637 if (status != SILC_STATUS_OK)
1638 return;
1639
1640 channel_entry = va_arg(ap, SilcChannelEntry);
1641 (void)va_arg(ap, SilcUInt32);
1642 (void)va_arg(ap, SilcPublicKey);
1643 channel_pubkeys = va_arg(ap, SilcDList);
1644
1645 if (!sg->chpk)
1646 break;
1647
1648 list = silc_dlist_init();
1649
1650 if (channel_pubkeys) {
1651 silc_dlist_start(channel_pubkeys);
1652 while ((e = silc_dlist_get(channel_pubkeys))) {
1653 if (e->arg_type == 0x00 ||
1654 e->arg_type == 0x03)
1655 silc_dlist_add(list, silc_pkcs_public_key_copy(e->argument));
1656 }
1657 }
1658 silcpurple_chat_chauth_show(sg, channel_entry, list);
1659 }
1660 break;
1661
1662 case SILC_COMMAND_WATCH:
1663 if (status != SILC_STATUS_OK) {
1664 purple_notify_error(gc, _("WATCH"), _("Cannot watch user"),
1665 silc_get_status_message(error));
1666 return;
1667 }
1668 break;
1669
1670 default:
1671 if (status == SILC_STATUS_OK)
1672 purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
1673 else
1674 purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
1675 silc_get_status_message(error));
1676 break;
1677 }
1678 }
1679
1680 /* Generic command reply callback for silc_client_command_send. Simply
1681 calls the default command_reply client operation callback */
1682
silcpurple_command_reply(SilcClient client,SilcClientConnection conn,SilcCommand command,SilcStatus status,SilcStatus error,void * context,va_list ap)1683 SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn,
1684 SilcCommand command, SilcStatus status,
1685 SilcStatus error, void *context, va_list ap)
1686 {
1687 silc_command_reply(client, conn, command, status, error, ap);
1688 return TRUE;
1689 }
1690
1691
1692 typedef struct {
1693 union {
1694 SilcAskPassphrase ask_pass;
1695 SilcGetAuthMeth get_auth;
1696 } u;
1697 void *context;
1698 } *SilcPurpleAskPassphrase;
1699
1700 static void
silc_ask_auth_password_cb(const unsigned char * passphrase,SilcUInt32 passphrase_len,void * context)1701 silc_ask_auth_password_cb(const unsigned char *passphrase,
1702 SilcUInt32 passphrase_len, void *context)
1703 {
1704 SilcPurpleAskPassphrase internal = context;
1705
1706 if (!passphrase || !(*passphrase))
1707 internal->u.get_auth(SILC_AUTH_NONE, NULL, 0, internal->context);
1708 else
1709 internal->u.get_auth(SILC_AUTH_PASSWORD,
1710 (unsigned char *)passphrase,
1711 passphrase_len, internal->context);
1712 silc_free(internal);
1713 }
1714
1715 /* Find authentication method and authentication data by hostname and
1716 port. The hostname may be IP address as well. The `auth_method' is
1717 the authentication method the remote connection requires. It is
1718 however possible that remote accepts also some other authentication
1719 method. Application should use the method that may have been
1720 configured for this connection. If none has been configured it should
1721 use the required `auth_method'. If the `auth_method' is
1722 SILC_AUTH_NONE, server does not require any authentication or the
1723 required authentication method is not known. The `completion'
1724 callback must be called to deliver the chosen authentication method
1725 and data. The `conn' may be NULL. */
1726
1727 static void
silc_get_auth_method(SilcClient client,SilcClientConnection conn,char * hostname,SilcUInt16 port,SilcAuthMethod auth_method,SilcGetAuthMeth completion,void * context)1728 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1729 char *hostname, SilcUInt16 port,
1730 SilcAuthMethod auth_method,
1731 SilcGetAuthMeth completion, void *context)
1732 {
1733 PurpleConnection *gc = client->application;
1734 SilcPurple sg = gc->proto_data;
1735 SilcPurpleAskPassphrase internal;
1736 const char *password;
1737
1738 /* Progress */
1739 if (sg->resuming)
1740 purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
1741 else
1742 purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
1743
1744 /* Check configuration if we have this connection configured. */
1745 if (auth_method == SILC_AUTH_PUBLIC_KEY &&
1746 purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
1747 completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
1748 return;
1749 }
1750 if (auth_method == SILC_AUTH_PASSWORD) {
1751 password = purple_connection_get_password(gc);
1752 if (password && *password) {
1753 completion(SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
1754 return;
1755 }
1756
1757 /* Ask password from user */
1758 internal = silc_calloc(1, sizeof(*internal));
1759 if (!internal)
1760 return;
1761 internal->u.get_auth = completion;
1762 internal->context = context;
1763 silc_ask_passphrase(client, conn, silc_ask_auth_password_cb,
1764 internal);
1765 return;
1766 }
1767
1768 completion(SILC_AUTH_NONE, NULL, 0, context);
1769 }
1770
1771
1772 /* Called to verify received public key. The `conn_type' indicates which
1773 entity (server or client) has sent the public key. If user decides to
1774 trust the key the application may save the key as trusted public key for
1775 later use. The `completion' must be called after the public key has
1776 been verified. */
1777
1778 static void
silc_verify_public_key(SilcClient client,SilcClientConnection conn,SilcConnectionType conn_type,SilcPublicKey public_key,SilcVerifyPublicKey completion,void * context)1779 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1780 SilcConnectionType conn_type,
1781 SilcPublicKey public_key,
1782 SilcVerifyPublicKey completion, void *context)
1783 {
1784 PurpleConnection *gc = client->application;
1785 SilcPurple sg = gc->proto_data;
1786
1787 if (!sg->conn && (conn_type == SILC_CONN_SERVER ||
1788 conn_type == SILC_CONN_ROUTER)) {
1789 /* Progress */
1790 if (sg->resuming)
1791 purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
1792 else
1793 purple_connection_update_progress(gc, _("Verifying server public key"),
1794 3, 5);
1795 }
1796
1797 /* Verify public key */
1798 silcpurple_verify_public_key(client, conn, NULL, conn_type,
1799 public_key, completion, context);
1800 }
1801
1802 static void
silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal,const char * passphrase)1803 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
1804 {
1805 if (!passphrase || !(*passphrase))
1806 internal->u.ask_pass(NULL, 0, internal->context);
1807 else
1808 internal->u.ask_pass((unsigned char *)passphrase,
1809 strlen(passphrase), internal->context);
1810 silc_free(internal);
1811 }
1812
1813 /* Ask (interact, that is) a passphrase from user. The passphrase is
1814 returned to the library by calling the `completion' callback with
1815 the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1816 if not then the library will attempt to encode. */
1817
1818 static void
silc_ask_passphrase(SilcClient client,SilcClientConnection conn,SilcAskPassphrase completion,void * context)1819 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1820 SilcAskPassphrase completion, void *context)
1821 {
1822 PurpleConnection *gc = client->application;
1823 SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal));
1824
1825 if (!internal)
1826 return;
1827 internal->u.ask_pass = completion;
1828 internal->context = context;
1829 purple_request_input(gc, _("Passphrase"), NULL,
1830 _("Passphrase required"), NULL, FALSE, TRUE, NULL,
1831 _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
1832 _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
1833 purple_connection_get_account(gc), NULL, NULL, internal);
1834 }
1835
1836
1837 /* Called to indicate that incoming key agreement request has been
1838 received. If the application wants to perform key agreement it may
1839 call silc_client_perform_key_agreement to initiate key agreement or
1840 silc_client_send_key_agreement to provide connection point to the
1841 remote client in case the `hostname' is NULL. If key agreement is
1842 not desired this request can be ignored. The `protocol' is either
1843 value 0 for TCP or value 1 for UDP. */
1844
1845 static void
silc_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,const char * hostname,SilcUInt16 protocol,SilcUInt16 port)1846 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1847 SilcClientEntry client_entry,
1848 const char *hostname, SilcUInt16 protocol,
1849 SilcUInt16 port)
1850 {
1851 silcpurple_buddy_keyagr_request(client, conn, client_entry,
1852 hostname, port, protocol);
1853 }
1854
1855
1856 /* Notifies application that file transfer protocol session is being
1857 requested by the remote client indicated by the `client_entry' from
1858 the `hostname' and `port'. The `session_id' is the file transfer
1859 session and it can be used to either accept or reject the file
1860 transfer request, by calling the silc_client_file_receive or
1861 silc_client_file_close, respectively. */
1862
1863 static void
silc_ftp(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcUInt32 session_id,const char * hostname,SilcUInt16 port)1864 silc_ftp(SilcClient client, SilcClientConnection conn,
1865 SilcClientEntry client_entry, SilcUInt32 session_id,
1866 const char *hostname, SilcUInt16 port)
1867 {
1868 silcpurple_ftp_request(client, conn, client_entry, session_id,
1869 hostname, port);
1870 }
1871
1872 SilcClientOperations ops = {
1873 silc_say,
1874 silc_channel_message,
1875 silc_private_message,
1876 silc_notify,
1877 silc_command,
1878 silc_command_reply,
1879 silc_get_auth_method,
1880 silc_verify_public_key,
1881 silc_ask_passphrase,
1882 silc_key_agreement,
1883 silc_ftp
1884 };
1885