comparison src/protocols/silc/ops.c @ 8849:50d0f76639e7

[gaim-migrate @ 9616] Let there be SILC. committer: Tailor Script <tailor@pidgin.im>
author Ethan Blanton <elb@pidgin.im>
date Sat, 01 May 2004 19:34:44 +0000
parents
children 4f7c365c5c5a
comparison
equal deleted inserted replaced
8848:56e6b9bdcdbc 8849:50d0f76639e7
1 /*
2
3 silcgaim_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 "silcgaim.h"
23
24 /* Message sent to the application by library. `conn' associates the
25 message to a specific connection. `conn', however, may be NULL.
26 The `type' indicates the type of the message sent by the library.
27 The application can for example filter the message according the
28 type. */
29
30 static void
31 silc_say(SilcClient client, SilcClientConnection conn,
32 SilcClientMessageType type, char *msg, ...)
33 {
34 /* Nothing */
35 }
36
37
38 /* Message for a channel. The `sender' is the sender of the message
39 The `channel' is the channel. The `message' is the message. Note
40 that `message' maybe NULL. The `flags' indicates message flags
41 and it is used to determine how the message can be interpreted
42 (like it may tell the message is multimedia message). */
43
44 static void
45 silc_channel_message(SilcClient client, SilcClientConnection conn,
46 SilcClientEntry sender, SilcChannelEntry channel,
47 SilcMessagePayload payload, SilcChannelPrivateKey key,
48 SilcMessageFlags flags, const unsigned char *message,
49 SilcUInt32 message_len)
50 {
51 GaimConnection *gc = client->application;
52 SilcGaim sg = gc->proto_data;
53 GaimConversation *convo = NULL;
54 char *msg;
55
56 if (!message)
57 return;
58
59 if (key) {
60 GList *l;
61 SilcGaimPrvgrp prv;
62
63 for (l = sg->grps; l; l = l->next)
64 if (((SilcGaimPrvgrp)l->data)->key == key) {
65 prv = l->data;
66 convo = gaim_find_conversation_with_account(prv->channel,
67 sg->account);
68 break;
69 }
70 }
71 if (!convo)
72 convo = gaim_find_conversation_with_account(channel->channel_name,
73 sg->account);
74 if (!convo)
75 return;
76
77 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
78 gaim_prefs_get_bool("/plugins/prpl/silc/verify_chat")) {
79 /* XXX */
80 }
81
82 if (flags & SILC_MESSAGE_FLAG_DATA) {
83 /* XXX */
84 return;
85 }
86
87 if (flags & SILC_MESSAGE_FLAG_ACTION) {
88 msg = g_strdup_printf("<I>%s</I> %s",
89 sender->nickname ?
90 sender->nickname : "<unknown>",
91 (const char *)message);
92 if (!msg)
93 return;
94
95 /* Send to Gaim */
96 gaim_conversation_write(convo, NULL, (const char *)msg,
97 GAIM_MESSAGE_SYSTEM, time(NULL));
98 g_free(msg);
99 return;
100 }
101
102 if (flags & SILC_MESSAGE_FLAG_NOTICE) {
103 msg = g_strdup_printf("(notice) <I>%s</I> %s",
104 sender->nickname ?
105 sender->nickname : "<unknown>",
106 (const char *)message);
107 if (!msg)
108 return;
109
110 /* Send to Gaim */
111 gaim_conversation_write(convo, NULL, (const char *)msg,
112 GAIM_MESSAGE_SYSTEM, time(NULL));
113 g_free(msg);
114 return;
115 }
116
117 if (flags & SILC_MESSAGE_FLAG_UTF8)
118 /* Send to Gaim */
119 serv_got_chat_in(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)),
120 sender->nickname ?
121 sender->nickname : "<unknown>", 0,
122 (const char *)message, time(NULL));
123 }
124
125
126 /* Private message to the client. The `sender' is the sender of the
127 message. The message is `message'and maybe NULL. The `flags'
128 indicates message flags and it is used to determine how the message
129 can be interpreted (like it may tell the message is multimedia
130 message). */
131
132 static void
133 silc_private_message(SilcClient client, SilcClientConnection conn,
134 SilcClientEntry sender, SilcMessagePayload payload,
135 SilcMessageFlags flags, const unsigned char *message,
136 SilcUInt32 message_len)
137 {
138 GaimConnection *gc = client->application;
139 SilcGaim sg = gc->proto_data;
140 GaimConversation *convo = NULL;
141 char *msg;
142
143 if (!message)
144 return;
145
146 if (sender->nickname)
147 convo = gaim_find_conversation_with_account(sender->nickname, sg->account);
148
149 if (flags & SILC_MESSAGE_FLAG_SIGNED &&
150 gaim_prefs_get_bool("/plugins/prpl/silc/verify_im")) {
151 /* XXX */
152 }
153
154 if (flags & SILC_MESSAGE_FLAG_DATA) {
155 /* XXX */
156 return;
157 }
158
159 if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
160 msg = g_strdup_printf("<I>%s</I> %s",
161 sender->nickname ?
162 sender->nickname : "<unknown>",
163 (const char *)message);
164 if (!msg)
165 return;
166
167 /* Send to Gaim */
168 gaim_conversation_write(convo, NULL, (const char *)msg,
169 GAIM_MESSAGE_SYSTEM, time(NULL));
170 g_free(msg);
171 return;
172 }
173
174 if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
175 msg = g_strdup_printf("(notice) <I>%s</I> %s",
176 sender->nickname ?
177 sender->nickname : "<unknown>",
178 (const char *)message);
179 if (!msg)
180 return;
181
182 /* Send to Gaim */
183 gaim_conversation_write(convo, NULL, (const char *)msg,
184 GAIM_MESSAGE_SYSTEM, time(NULL));
185 g_free(msg);
186 return;
187 }
188
189 if (flags & SILC_MESSAGE_FLAG_UTF8)
190 /* Send to Gaim */
191 serv_got_im(gc, sender->nickname ?
192 sender->nickname : "<unknown>",
193 (const char *)message, 0, time(NULL));
194 }
195
196
197 /* Notify message to the client. The notify arguments are sent in the
198 same order as servers sends them. The arguments are same as received
199 from the server except for ID's. If ID is received application receives
200 the corresponding entry to the ID. For example, if Client ID is received
201 application receives SilcClientEntry. Also, if the notify type is
202 for channel the channel entry is sent to application (even if server
203 does not send it because client library gets the channel entry from
204 the Channel ID in the packet's header). */
205
206 static void
207 silc_notify(SilcClient client, SilcClientConnection conn,
208 SilcNotifyType type, ...)
209 {
210 va_list va;
211 GaimConnection *gc = client->application;
212 SilcGaim sg = gc->proto_data;
213 GaimConversation *convo;
214 SilcClientEntry client_entry, client_entry2;
215 SilcChannelEntry channel;
216 SilcServerEntry server_entry;
217 SilcIdType idtype;
218 void *entry;
219 SilcUInt32 mode;
220 SilcHashTableList htl;
221 SilcChannelUser chu;
222 char buf[512], buf2[512], *tmp, *name;
223 SilcBuffer buffer;
224 SilcNotifyType notify;
225 GaimBuddy *b;
226 int i;
227
228 va_start(va, type);
229 memset(buf, 0, sizeof(buf));
230
231 switch (type) {
232
233 case SILC_NOTIFY_TYPE_NONE:
234 break;
235
236 case SILC_NOTIFY_TYPE_INVITE:
237 {
238 GHashTable *components;
239 channel = va_arg(va, SilcChannelEntry);
240 name = va_arg(va, char *);
241 client_entry = va_arg(va, SilcClientEntry);
242
243 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
244 g_hash_table_insert(components, strdup("channel"), name);
245 serv_got_chat_invite(gc, name, client_entry->nickname, NULL, NULL);
246 }
247 break;
248
249 case SILC_NOTIFY_TYPE_JOIN:
250 client_entry = va_arg(va, SilcClientEntry);
251 channel = va_arg(va, SilcChannelEntry);
252
253 /* If we joined channel, do nothing */
254 if (client_entry == conn->local_entry)
255 break;
256
257 convo = gaim_find_conversation_with_account(channel->channel_name,
258 sg->account);
259 if (!convo)
260 break;
261
262 /* Join user to channel */
263 g_snprintf(buf, sizeof(buf), _("%s@%s"),
264 client_entry->username, client_entry->hostname);
265 gaim_conv_chat_add_user(GAIM_CONV_CHAT(convo),
266 g_strdup(client_entry->nickname), buf);
267
268 break;
269
270 case SILC_NOTIFY_TYPE_LEAVE:
271 client_entry = va_arg(va, SilcClientEntry);
272 channel = va_arg(va, SilcChannelEntry);
273
274 convo = gaim_find_conversation_with_account(channel->channel_name,
275 sg->account);
276 if (!convo)
277 break;
278
279 /* Remove user from channel */
280 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo),
281 client_entry->nickname, NULL);
282
283 break;
284
285 case SILC_NOTIFY_TYPE_SIGNOFF:
286 client_entry = va_arg(va, SilcClientEntry);
287 tmp = va_arg(va, char *);
288
289 if (!client_entry->nickname)
290 break;
291
292 /* Remove from all channels */
293 silc_hash_table_list(client_entry->channels, &htl);
294 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
295 convo = gaim_find_conversation_with_account(chu->channel->channel_name,
296 sg->account);
297 if (!convo)
298 continue;
299 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo),
300 client_entry->nickname,
301 tmp);
302 }
303 silc_hash_table_list_reset(&htl);
304
305 break;
306
307 case SILC_NOTIFY_TYPE_TOPIC_SET:
308 idtype = va_arg(va, int);
309 entry = va_arg(va, void *);
310 tmp = va_arg(va, char *);
311 channel = va_arg(va, SilcChannelEntry);
312
313 convo = gaim_find_conversation_with_account(channel->channel_name,
314 sg->account);
315 if (!convo)
316 break;
317
318 if (!tmp)
319 break;
320
321 if (idtype == SILC_ID_CLIENT) {
322 client_entry = (SilcClientEntry)entry;
323 g_snprintf(buf, sizeof(buf),
324 _("%s has changed the topic of <I>%s</I> to: %s"),
325 client_entry->nickname, channel->channel_name, tmp);
326 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), client_entry->nickname,
327 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
328 } else if (idtype == SILC_ID_SERVER) {
329 server_entry = (SilcServerEntry)entry;
330 g_snprintf(buf, sizeof(buf),
331 _("%s has changed the topic of <I>%s</I> to: %s"),
332 server_entry->server_name, channel->channel_name, tmp);
333 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), server_entry->server_name,
334 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
335 } else if (idtype == SILC_ID_CHANNEL) {
336 channel = (SilcChannelEntry)entry;
337 g_snprintf(buf, sizeof(buf),
338 _("%s has changed the topic of <I>%s</I> to: %s"),
339 channel->channel_name, channel->channel_name, tmp);
340 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), channel->channel_name,
341 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
342 }
343
344 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(convo), NULL, tmp);
345
346 break;
347
348 case SILC_NOTIFY_TYPE_NICK_CHANGE:
349 client_entry = va_arg(va, SilcClientEntry);
350 client_entry2 = va_arg(va, SilcClientEntry);
351
352 if (!strcmp(client_entry->nickname, client_entry2->nickname))
353 break;
354
355 /* Change nick on all channels */
356 silc_hash_table_list(client_entry2->channels, &htl);
357 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
358 convo = gaim_find_conversation_with_account(chu->channel->channel_name,
359 sg->account);
360 if (!convo)
361 continue;
362 gaim_conv_chat_rename_user(GAIM_CONV_CHAT(convo),
363 client_entry->nickname,
364 client_entry2->nickname);
365 }
366 silc_hash_table_list_reset(&htl);
367
368 break;
369
370 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
371 idtype = va_arg(va, int);
372 entry = va_arg(va, void *);
373 mode = va_arg(va, SilcUInt32);
374 (void)va_arg(va, char *);
375 (void)va_arg(va, char *);
376 (void)va_arg(va, char *);
377 (void)va_arg(va, SilcPublicKey);
378 buffer = va_arg(va, SilcBuffer);
379 channel = va_arg(va, SilcChannelEntry);
380
381 convo = gaim_find_conversation_with_account(channel->channel_name,
382 sg->account);
383 if (!convo)
384 break;
385
386 if (idtype == SILC_ID_CLIENT)
387 name = ((SilcClientEntry)entry)->nickname;
388 else if (idtype == SILC_ID_SERVER)
389 name = ((SilcServerEntry)entry)->server_name;
390 else
391 name = ((SilcChannelEntry)entry)->channel_name;
392 if (!name)
393 break;
394
395 if (mode) {
396 silcgaim_get_chmode_string(mode, buf2, sizeof(buf2));
397 g_snprintf(buf, sizeof(buf),
398 _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
399 channel->channel_name, buf2);
400 } else {
401 g_snprintf(buf, sizeof(buf),
402 _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
403 channel->channel_name);
404 }
405 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), channel->channel_name,
406 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
407 break;
408
409 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
410 idtype = va_arg(va, int);
411 entry = va_arg(va, void *);
412 mode = va_arg(va, SilcUInt32);
413 client_entry2 = va_arg(va, SilcClientEntry);
414 channel = va_arg(va, SilcChannelEntry);
415
416 convo = gaim_find_conversation_with_account(channel->channel_name,
417 sg->account);
418 if (!convo)
419 break;
420
421 if (idtype == SILC_ID_CLIENT)
422 name = ((SilcClientEntry)entry)->nickname;
423 else if (idtype == SILC_ID_SERVER)
424 name = ((SilcServerEntry)entry)->server_name;
425 else
426 name = ((SilcChannelEntry)entry)->channel_name;
427 if (!name)
428 break;
429
430 if (mode) {
431 silcgaim_get_chumode_string(mode, buf2, sizeof(buf2));
432 g_snprintf(buf, sizeof(buf),
433 _("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
434 client_entry2->nickname, buf2);
435 } else {
436 g_snprintf(buf, sizeof(buf),
437 _("<I>%s</I> removed all <I>%s's</I> modes"), name,
438 client_entry2->nickname);
439 }
440 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), channel->channel_name,
441 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
442 break;
443
444 case SILC_NOTIFY_TYPE_MOTD:
445 tmp = va_arg(va, char *);
446 silc_free(sg->motd);
447 sg->motd = silc_memdup(tmp, strlen(tmp));
448 break;
449
450 case SILC_NOTIFY_TYPE_KICKED:
451 client_entry = va_arg(va, SilcClientEntry);
452 tmp = va_arg(va, char *);
453 client_entry2 = va_arg(va, SilcClientEntry);
454 channel = va_arg(va, SilcChannelEntry);
455
456 convo = gaim_find_conversation_with_account(channel->channel_name,
457 sg->account);
458 if (!convo)
459 break;
460
461 if (client_entry == conn->local_entry) {
462 /* Remove us from channel */
463 g_snprintf(buf, sizeof(buf),
464 _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
465 channel->channel_name, client_entry2->nickname,
466 tmp ? tmp : "");
467 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), client_entry->nickname,
468 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
469 serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)));
470 } else {
471 /* Remove user from channel */
472 g_snprintf(buf, sizeof(buf), ("Kicked by %s (%s)"),
473 client_entry2->nickname, tmp ? tmp : "");
474 gaim_conv_chat_rename_user(GAIM_CONV_CHAT(convo),
475 client_entry->nickname,
476 buf);
477 }
478
479 break;
480
481 case SILC_NOTIFY_TYPE_KILLED:
482 client_entry = va_arg(va, SilcClientEntry);
483 tmp = va_arg(va, char *);
484 idtype = va_arg(va, int);
485 entry = va_arg(va, SilcClientEntry);
486
487 if (!client_entry->nickname)
488 break;
489
490 if (client_entry == conn->local_entry) {
491 if (idtype == SILC_ID_CLIENT) {
492 client_entry2 = (SilcClientEntry)entry;
493 g_snprintf(buf, sizeof(buf),
494 _("You have been killed by %s (%s)"),
495 client_entry2->nickname, tmp ? tmp : "");
496 } else if (idtype == SILC_ID_SERVER) {
497 server_entry = (SilcServerEntry)entry;
498 g_snprintf(buf, sizeof(buf),
499 _("You have been killed by %s (%s)"),
500 server_entry->server_name, tmp ? tmp : "");
501 } else if (idtype == SILC_ID_CHANNEL) {
502 channel = (SilcChannelEntry)entry;
503 g_snprintf(buf, sizeof(buf),
504 _("You have been killed by %s (%s)"),
505 channel->channel_name, tmp ? tmp : "");
506 }
507
508 /* Remove us from all channels */
509 silc_hash_table_list(client_entry->channels, &htl);
510 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
511 convo = gaim_find_conversation_with_account(chu->channel->channel_name,
512 sg->account);
513 if (!convo)
514 continue;
515 gaim_conv_chat_write(GAIM_CONV_CHAT(convo), client_entry->nickname,
516 buf, GAIM_MESSAGE_SYSTEM, time(NULL));
517 serv_got_chat_left(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(convo)));
518 }
519 silc_hash_table_list_reset(&htl);
520
521 } else {
522 if (idtype == SILC_ID_CLIENT) {
523 client_entry2 = (SilcClientEntry)entry;
524 g_snprintf(buf, sizeof(buf),
525 _("Killed by %s (%s)"),
526 client_entry2->nickname, tmp ? tmp : "");
527 } else if (idtype == SILC_ID_SERVER) {
528 server_entry = (SilcServerEntry)entry;
529 g_snprintf(buf, sizeof(buf),
530 _("Killed by %s (%s)"),
531 server_entry->server_name, tmp ? tmp : "");
532 } else if (idtype == SILC_ID_CHANNEL) {
533 channel = (SilcChannelEntry)entry;
534 g_snprintf(buf, sizeof(buf),
535 _("Killed by %s (%s)"),
536 channel->channel_name, tmp ? tmp : "");
537 }
538
539 /* Remove user from all channels */
540 silc_hash_table_list(client_entry->channels, &htl);
541 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
542 convo = gaim_find_conversation_with_account(chu->channel->channel_name,
543 sg->account);
544 if (!convo)
545 continue;
546 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo),
547 client_entry->nickname, tmp);
548 }
549 silc_hash_table_list_reset(&htl);
550 }
551
552 break;
553
554 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
555 break;
556
557 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
558 {
559 int i;
560 SilcClientEntry *clients;
561 SilcUInt32 clients_count;
562
563 (void)va_arg(va, void *);
564 clients = va_arg(va, SilcClientEntry *);
565 clients_count = va_arg(va, SilcUInt32);
566
567 for (i = 0; i < clients_count; i++) {
568 if (!clients[i]->nickname)
569 break;
570
571 /* Remove from all channels */
572 silc_hash_table_list(clients[i]->channels, &htl);
573 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
574 convo =
575 gaim_find_conversation_with_account(chu->channel->channel_name,
576 sg->account);
577 if (!convo)
578 continue;
579 gaim_conv_chat_remove_user(GAIM_CONV_CHAT(convo),
580 clients[i]->nickname,
581 _("Server signoff"));
582 }
583 silc_hash_table_list_reset(&htl);
584 }
585 }
586 break;
587
588 case SILC_NOTIFY_TYPE_ERROR:
589 {
590 SilcStatus error = va_arg(va, int);
591 gaim_notify_error(gc, "Error Notify",
592 silc_get_status_message(error),
593 NULL);
594 }
595 break;
596
597 case SILC_NOTIFY_TYPE_WATCH:
598 {
599 SilcPublicKey public_key;
600 unsigned char *pk;
601 SilcUInt32 pk_len;
602 char *fingerprint;
603
604 client_entry = va_arg(va, SilcClientEntry);
605 (void)va_arg(va, char *);
606 mode = va_arg(va, SilcUInt32);
607 notify = va_arg(va, int);
608 public_key = va_arg(va, SilcPublicKey);
609
610 b = NULL;
611 if (public_key) {
612 GaimBlistNode *gnode, *cnode, *bnode;
613 const char *f;
614
615 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
616 if (!pk)
617 break;
618 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
619 for (i = 0; i < strlen(fingerprint); i++)
620 if (fingerprint[i] == ' ')
621 fingerprint[i] = '_';
622 g_snprintf(buf, sizeof(buf) - 1,
623 "%s" G_DIR_SEPARATOR_S "clientkeys"
624 G_DIR_SEPARATOR_S "clientkey_%s.pub",
625 silcgaim_silcdir(), fingerprint);
626 silc_free(fingerprint);
627 silc_free(pk);
628
629 /* Find buddy by associated public key */
630 for (gnode = gaim_get_blist()->root; gnode;
631 gnode = gnode->next) {
632 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
633 continue;
634 for (cnode = gnode->child; cnode; cnode = cnode->next) {
635 if( !GAIM_BLIST_NODE_IS_CONTACT(cnode))
636 continue;
637 for (bnode = cnode->child; bnode;
638 bnode = bnode->next) {
639 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
640 continue;
641 b = (GaimBuddy *)bnode;
642 if (b->account != gc->account)
643 continue;
644 f = gaim_blist_node_get_string(bnode, "public-key");
645 if (!strcmp(f, buf))
646 goto cont;
647 }
648 }
649 }
650 }
651 cont:
652 if (!b) {
653 /* Find buddy by nickname */
654 b = gaim_find_buddy(sg->account, client_entry->nickname);
655 if (!b) {
656 fprintf(stderr, "WATCH for %s, unknown buddy",
657 client_entry->nickname);
658 break;
659 }
660 }
661
662 silc_free(b->proto_data);
663 b->proto_data = silc_memdup(client_entry->id,
664 sizeof(*client_entry->id));
665 if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
666 break;
667 } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
668 /* See if client was away and is now present */
669 if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
670 SILC_UMODE_BUSY | SILC_UMODE_PAGE |
671 SILC_UMODE_DETACHED)) &&
672 (client_entry->mode & SILC_UMODE_GONE ||
673 client_entry->mode & SILC_UMODE_INDISPOSED ||
674 client_entry->mode & SILC_UMODE_BUSY ||
675 client_entry->mode & SILC_UMODE_PAGE ||
676 client_entry->mode & SILC_UMODE_DETACHED)) {
677 client_entry->mode = mode;
678 gaim_blist_update_buddy_presence(b, GAIM_BUDDY_ONLINE);
679 }
680 else if ((mode & SILC_UMODE_GONE) ||
681 (mode & SILC_UMODE_INDISPOSED) ||
682 (mode & SILC_UMODE_BUSY) ||
683 (mode & SILC_UMODE_PAGE) ||
684 (mode & SILC_UMODE_DETACHED)) {
685 client_entry->mode = mode;
686 gaim_blist_update_buddy_presence(b, GAIM_BUDDY_OFFLINE);
687 }
688 } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
689 notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
690 notify == SILC_NOTIFY_TYPE_KILLED) {
691 client_entry->mode = mode;
692 gaim_blist_update_buddy_presence(b, GAIM_BUDDY_OFFLINE);
693 } else if (notify == SILC_NOTIFY_TYPE_NONE) {
694 client_entry->mode = mode;
695 gaim_blist_update_buddy_presence(b, GAIM_BUDDY_ONLINE);
696 }
697 }
698 break;
699
700 default:
701 break;
702 }
703
704 va_end(va);
705 }
706
707
708 /* Command handler. This function is called always in the command function.
709 If error occurs it will be called as well. `conn' is the associated
710 client connection. `cmd_context' is the command context that was
711 originally sent to the command. `success' is FALSE if error occurred
712 during command. `command' is the command being processed. It must be
713 noted that this is not reply from server. This is merely called just
714 after application has called the command. Just to tell application
715 that the command really was processed. */
716
717 static void
718 silc_command(SilcClient client, SilcClientConnection conn,
719 SilcClientCommandContext cmd_context, bool success,
720 SilcCommand command, SilcStatus status)
721 {
722 GaimConnection *gc = client->application;
723 SilcGaim sg = gc->proto_data;
724
725 switch (command) {
726
727 case SILC_COMMAND_CMODE:
728 if (cmd_context->argc == 3 &&
729 !strcmp(cmd_context->argv[2], "+C"))
730 sg->chpk = TRUE;
731 else
732 sg->chpk = FALSE;
733 break;
734
735 default:
736 break;
737 }
738 }
739
740
741 static void
742 silcgaim_whois_more(SilcClientEntry client_entry, gint id)
743 {
744 SilcAttributePayload attr;
745 SilcAttribute attribute;
746 char *buf;
747 GString *s;
748 SilcVCardStruct vcard;
749 int i;
750
751 if (id != 0)
752 return;
753
754 memset(&vcard, 0, sizeof(vcard));
755
756 s = g_string_new("");
757
758 silc_dlist_start(client_entry->attrs);
759 while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
760 attribute = silc_attribute_get_attribute(attr);
761 switch (attribute) {
762
763 case SILC_ATTRIBUTE_USER_INFO:
764 if (!silc_attribute_get_object(attr, (void *)&vcard,
765 sizeof(vcard)))
766 continue;
767 g_string_append_printf(s, _("Personal Information:\n\n"));
768 if (vcard.full_name)
769 g_string_append_printf(s, _("Full Name:\t\t%s\n"),
770 vcard.full_name);
771 if (vcard.first_name)
772 g_string_append_printf(s, _("First Name:\t%s\n"),
773 vcard.first_name);
774 if (vcard.middle_names)
775 g_string_append_printf(s, _("Middle Names:\t%s\n"),
776 vcard.middle_names);
777 if (vcard.family_name)
778 g_string_append_printf(s, _("Family Name:\t%s\n"),
779 vcard.family_name);
780 if (vcard.nickname)
781 g_string_append_printf(s, _("Nickname:\t\t%s\n"),
782 vcard.nickname);
783 if (vcard.bday)
784 g_string_append_printf(s, _("Birth Day:\t\t%s\n"),
785 vcard.bday);
786 if (vcard.title)
787 g_string_append_printf(s, _("Job Title:\t\t%s\n"),
788 vcard.title);
789 if (vcard.role)
790 g_string_append_printf(s, _("Job Role:\t\t%s\n"),
791 vcard.role);
792 if (vcard.org_name)
793 g_string_append_printf(s, _("Organization:\t%s\n"),
794 vcard.org_name);
795 if (vcard.org_unit)
796 g_string_append_printf(s, _("Unit:\t\t%s\n"),
797 vcard.org_unit);
798 if (vcard.url)
799 g_string_append_printf(s, _("Homepage:\t%s\n"),
800 vcard.url);
801 if (vcard.label)
802 g_string_append_printf(s, _("Address:\t%s\n"),
803 vcard.label);
804 for (i = 0; i < vcard.num_tels; i++) {
805 if (vcard.tels[i].telnum)
806 g_string_append_printf(s, _("Tel:\t\t\t%s\n"),
807 vcard.tels[i].telnum);
808 }
809 for (i = 0; i < vcard.num_emails; i++) {
810 if (vcard.emails[i].address)
811 g_string_append_printf(s, _("EMail:\t\t%s\n"),
812 vcard.emails[i].address);
813 }
814 if (vcard.note)
815 g_string_append_printf(s, _("\nNote:\t\t%s\n"),
816 vcard.note);
817 break;
818 }
819 }
820
821 buf = g_string_free(s, FALSE);
822 gaim_notify_info(NULL, _("User Information"), _("User Information"),
823 buf);
824 g_free(buf);
825 }
826
827 /* Command reply handler. This function is called always in the command reply
828 function. If error occurs it will be called as well. Normal scenario
829 is that it will be called after the received command data has been parsed
830 and processed. The function is used to pass the received command data to
831 the application.
832
833 `conn' is the associated client connection. `cmd_payload' is the command
834 payload data received from server and it can be ignored. It is provided
835 if the application would like to re-parse the received command data,
836 however, it must be noted that the data is parsed already by the library
837 thus the payload can be ignored. `success' is FALSE if error occurred.
838 In this case arguments are not sent to the application. The `status' is
839 the command reply status server returned. The `command' is the command
840 reply being processed. The function has variable argument list and each
841 command defines the number and type of arguments it passes to the
842 application (on error they are not sent). */
843
844 static void
845 silc_command_reply(SilcClient client, SilcClientConnection conn,
846 SilcCommandPayload cmd_payload, bool success,
847 SilcCommand command, SilcStatus status, ...)
848 {
849 GaimConnection *gc = client->application;
850 SilcGaim sg = gc->proto_data;
851 GaimConversation *convo;
852 va_list vp;
853
854 va_start(vp, status);
855
856 switch (command) {
857 case SILC_COMMAND_JOIN:
858 {
859 SilcChannelEntry channel_entry;
860
861 if (!success) {
862 gaim_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
863 silc_get_status_message(status));
864 return;
865 }
866
867 (void)va_arg(vp, char *);
868 channel_entry = va_arg(vp, SilcChannelEntry);
869
870 /* Resolve users on channel */
871 silc_client_get_clients_by_channel(client, conn, channel_entry,
872 silcgaim_chat_join_done,
873 channel_entry);
874 }
875 break;
876
877 case SILC_COMMAND_LEAVE:
878 break;
879
880 case SILC_COMMAND_USERS:
881 break;
882
883 case SILC_COMMAND_WHOIS:
884 {
885 SilcUInt32 idle, mode;
886 SilcBuffer channels, user_modes;
887 SilcClientEntry client_entry;
888 char *buf, tmp[1024];
889 GString *s;
890
891 if (!success) {
892 gaim_notify_error(gc, _("User Information"),
893 _("Cannot get user information"),
894 silc_get_status_message(status));
895 break;
896 }
897
898 client_entry = va_arg(vp, SilcClientEntry);
899 if (!client_entry->nickname)
900 break;
901 (void)va_arg(vp, char *);
902 (void)va_arg(vp, char *);
903 (void)va_arg(vp, char *);
904 channels = va_arg(vp, SilcBuffer);
905 mode = va_arg(vp, SilcUInt32);
906 idle = va_arg(vp, SilcUInt32);
907 (void)va_arg(vp, unsigned char *);
908 user_modes = va_arg(vp, SilcBuffer);
909
910 s = g_string_new("");
911 g_string_append_printf(s, _("Nickname:\t\t%s\n"), client_entry->nickname);
912 if (client_entry->realname)
913 g_string_append_printf(s, _("Real Name:\t%s\n"), client_entry->realname);
914 if (client_entry->username)
915 g_string_append_printf(s, _("Username:\t\t%s\n"), client_entry->username);
916 if (client_entry->hostname)
917 g_string_append_printf(s, _("Hostname:\t\t%s\n"), client_entry->hostname);
918 if (client_entry->server)
919 g_string_append_printf(s, _("Server:\t\t%s\n"), client_entry->server);
920
921 if (mode) {
922 memset(tmp, 0, sizeof(tmp));
923 silcgaim_get_umode_string(mode, tmp, sizeof(tmp) - 1);
924 g_string_append_printf(s, _("User Mode:\t%s\n"), tmp);
925 }
926
927 if (channels && user_modes) {
928 SilcUInt32 *umodes;
929 SilcDList list =
930 silc_channel_payload_parse_list(channels->data,
931 channels->len);
932 if (list && silc_get_mode_list(user_modes,
933 silc_dlist_count(list),
934 &umodes)) {
935 SilcChannelPayload entry;
936 int i = 0;
937
938 g_string_append_printf(s, _("\nChannels:\n"));
939 memset(tmp, 0, sizeof(tmp));
940 silc_dlist_start(list);
941 while ((entry = silc_dlist_get(list))
942 != SILC_LIST_END) {
943 SilcUInt32 name_len;
944 char *m = silc_client_chumode_char(umodes[i++]);
945 char *name = silc_channel_get_name(entry, &name_len);
946 if (m)
947 silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
948 silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
949 silc_strncat(tmp, sizeof(tmp) - 1, " ", 1);
950 silc_free(m);
951
952 }
953 g_string_append_printf(s, _("%s\n"), tmp);
954 silc_free(umodes);
955 }
956 }
957
958 if (client_entry->public_key) {
959 char *fingerprint, *babbleprint;
960 unsigned char *pk;
961 SilcUInt32 pk_len;
962 pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
963 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
964 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
965 g_string_append_printf(s, _("\nPublic Key Fingerprint:\n%s\n\n"), fingerprint);
966 g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
967 silc_free(fingerprint);
968 silc_free(babbleprint);
969 silc_free(pk);
970 }
971
972 buf = g_string_free(s, FALSE);
973 #if 0 /* XXX for now, let's not show attrs here */
974 if (client_entry->attrs)
975 gaim_request_action(NULL, _("User Information"),
976 _("User Information"),
977 buf, 1, client_entry, 2,
978 _("OK"), G_CALLBACK(silcgaim_whois_more),
979 _("More..."), G_CALLBACK(silcgaim_whois_more));
980 else
981 #endif
982 gaim_notify_info(NULL, _("User Information"),
983 _("User Information"), buf);
984 g_free(buf);
985 }
986 break;
987
988 case SILC_COMMAND_DETACH:
989 if (!success) {
990 gaim_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
991 silc_get_status_message(status));
992 return;
993 }
994 break;
995
996 case SILC_COMMAND_TOPIC:
997 {
998 SilcChannelEntry channel;
999
1000 if (!success) {
1001 gaim_notify_error(gc, _("Topic"), _("Cannot set topic"),
1002 silc_get_status_message(status));
1003 return;
1004 }
1005
1006 channel = va_arg(vp, SilcChannelEntry);
1007
1008 convo = gaim_find_conversation_with_account(channel->channel_name,
1009 sg->account);
1010 if (!convo)
1011 break;
1012
1013 /* Set topic */
1014 if (channel->topic)
1015 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(convo), NULL, channel->topic);
1016 }
1017 break;
1018
1019 case SILC_COMMAND_LIST:
1020 {
1021 char *topic, *name;
1022 int usercount;
1023 GaimRoomlistRoom *room;
1024
1025 if (sg->roomlist_canceled)
1026 break;
1027
1028 if (!success) {
1029 gaim_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
1030 silc_get_status_message(status));
1031 gaim_roomlist_set_in_progress(sg->roomlist, FALSE);
1032 gaim_roomlist_unref(sg->roomlist);
1033 sg->roomlist = NULL;
1034 return;
1035 }
1036
1037 (void)va_arg(vp, SilcChannelEntry);
1038 name = va_arg(vp, char *);
1039 topic = va_arg(vp, char *);
1040 usercount = va_arg(vp, int);
1041
1042 room = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
1043 gaim_roomlist_room_add_field(sg->roomlist, room, name);
1044 gaim_roomlist_room_add_field(sg->roomlist, room,
1045 SILC_32_TO_PTR(usercount));
1046 gaim_roomlist_room_add_field(sg->roomlist, room,
1047 topic ? topic : "");
1048 gaim_roomlist_room_add(sg->roomlist, room);
1049
1050 if (status == SILC_STATUS_LIST_END ||
1051 status == SILC_STATUS_OK) {
1052 gaim_roomlist_set_in_progress(sg->roomlist, FALSE);
1053 gaim_roomlist_unref(sg->roomlist);
1054 sg->roomlist = NULL;
1055 }
1056 }
1057 break;
1058
1059 case SILC_COMMAND_GETKEY:
1060 {
1061 SilcPublicKey public_key;
1062
1063 if (!success) {
1064 gaim_notify_error(gc, _("Get Public Key"),
1065 _("Cannot fetch the public key"),
1066 silc_get_status_message(status));
1067 return;
1068 }
1069
1070 (void)va_arg(vp, SilcUInt32);
1071 (void)va_arg(vp, void *);
1072 public_key = va_arg(vp, SilcPublicKey);
1073
1074 if (!public_key)
1075 gaim_notify_error(gc, _("Get Public Key"),
1076 _("Cannot fetch the public key"),
1077 _("No public key was received"));
1078 }
1079 break;
1080
1081 case SILC_COMMAND_INFO:
1082 {
1083
1084 SilcServerEntry server_entry;
1085 char *server_name;
1086 char *server_info;
1087 char tmp[256];
1088
1089 if (!success) {
1090 gaim_notify_error(gc, _("Server Information"),
1091 _("Cannot get server information"),
1092 silc_get_status_message(status));
1093 return;
1094 }
1095
1096 server_entry = va_arg(vp, SilcServerEntry);
1097 server_name = va_arg(vp, char *);
1098 server_info = va_arg(vp, char *);
1099
1100 if (server_name && server_info) {
1101 g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
1102 server_name, server_info);
1103 gaim_notify_info(NULL, _("Server Information"),
1104 _("Server Information"), tmp);
1105 }
1106 }
1107 break;
1108
1109 case SILC_COMMAND_KILL:
1110 if (!success) {
1111 gaim_notify_error(gc, _("Kill User"),
1112 _("Could not kill user"),
1113 silc_get_status_message(status));
1114 return;
1115 }
1116 break;
1117
1118 case SILC_COMMAND_CMODE:
1119 {
1120 SilcChannelEntry channel_entry;
1121 SilcBuffer channel_pubkeys;
1122
1123 if (!success)
1124 return;
1125
1126 channel_entry = va_arg(vp, SilcChannelEntry);
1127 (void)va_arg(vp, SilcUInt32);
1128 (void)va_arg(vp, SilcPublicKey);
1129 channel_pubkeys = va_arg(vp, SilcBuffer);
1130
1131 if (sg->chpk)
1132 silcgaim_chat_chauth_show(sg, channel_entry, channel_pubkeys);
1133 }
1134 break;
1135
1136 default:
1137 break;
1138 }
1139
1140 va_end(vp);
1141 }
1142
1143
1144 /* Called to indicate that connection was either successfully established
1145 or connecting failed. This is also the first time application receives
1146 the SilcClientConnection objecet which it should save somewhere.
1147 If the `success' is FALSE the application must always call the function
1148 silc_client_close_connection. */
1149
1150 static void
1151 silc_connected(SilcClient client, SilcClientConnection conn,
1152 SilcClientConnectionStatus status)
1153 {
1154 GaimConnection *gc = client->application;
1155 SilcGaim sg = gc->proto_data;
1156 gboolean reject_watch, block_invites, block_ims;
1157
1158 if (!gc) {
1159 sg->conn = NULL;
1160 silc_client_close_connection(client, conn);
1161 return;
1162 }
1163
1164 switch (status) {
1165 case SILC_CLIENT_CONN_SUCCESS:
1166 case SILC_CLIENT_CONN_SUCCESS_RESUME:
1167 gaim_connection_set_state(gc, GAIM_CONNECTED);
1168 serv_finish_login(gc);
1169 unlink(silcgaim_session_file(gaim_account_get_username(sg->account)));
1170
1171 /* Send any UMODEs configured for account */
1172 reject_watch = gaim_account_get_bool(sg->account, "reject-watch", FALSE);
1173 block_invites = gaim_account_get_bool(sg->account, "block-invites", FALSE);
1174 block_ims = gaim_account_get_bool(sg->account, "block-ims", FALSE);
1175 if (reject_watch || block_invites || block_ims) {
1176 char m[5];
1177 g_snprintf(m, sizeof(m), "+%s%s%s",
1178 reject_watch ? "w" : "",
1179 block_invites ? "I" : "",
1180 block_ims ? "P" : "");
1181 silc_client_command_call(sg->client, sg->conn, NULL,
1182 "UMODE", m, NULL);
1183 }
1184
1185 return;
1186 break;
1187
1188 case SILC_CLIENT_CONN_ERROR:
1189 gaim_connection_error(gc, _("Error during connecting to SILC Server"));
1190 unlink(silcgaim_session_file(gaim_account_get_username(sg->account)));
1191 break;
1192
1193 case SILC_CLIENT_CONN_ERROR_KE:
1194 gaim_connection_error(gc, _("Key Exchange failed"));
1195 break;
1196
1197 case SILC_CLIENT_CONN_ERROR_AUTH:
1198 gaim_connection_error(gc, _("Authentication failed"));
1199 break;
1200
1201 case SILC_CLIENT_CONN_ERROR_RESUME:
1202 gaim_connection_error(gc,
1203 _("Resuming detached session failed."
1204 "Press Reconnect to create new connection."));
1205 unlink(silcgaim_session_file(gaim_account_get_username(sg->account)));
1206 break;
1207
1208 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
1209 gaim_connection_error(gc, _("Connection timeout"));
1210 break;
1211 }
1212
1213 /* Error */
1214 sg->conn = NULL;
1215 silc_client_close_connection(client, conn);
1216 }
1217
1218
1219 /* Called to indicate that connection was disconnected to the server.
1220 The `status' may tell the reason of the disconnection, and if the
1221 `message' is non-NULL it may include the disconnection message
1222 received from server. */
1223
1224 static void
1225 silc_disconnected(SilcClient client, SilcClientConnection conn,
1226 SilcStatus status, const char *message)
1227 {
1228 GaimConnection *gc = client->application;
1229 SilcGaim sg = gc->proto_data;
1230
1231 if (sg->resuming && !sg->detaching)
1232 unlink(silcgaim_session_file(gaim_account_get_username(sg->account)));
1233
1234 sg->conn = NULL;
1235
1236 /* Close the connection */
1237 if (!sg->detaching)
1238 gaim_connection_error(gc, _("Disconnected by server"));
1239 else
1240 gaim_connection_destroy(gc);
1241 }
1242
1243
1244 typedef struct {
1245 SilcGetAuthMeth completion;
1246 void *context;
1247 } *SilcGaimGetAuthMethod;
1248
1249 /* Callback called when we've received the authentication method information
1250 from the server after we've requested it. */
1251
1252 static void silc_get_auth_method_callback(SilcClient client,
1253 SilcClientConnection conn,
1254 SilcAuthMethod auth_meth,
1255 void *context)
1256 {
1257 SilcGaimGetAuthMethod internal = context;
1258
1259 switch (auth_meth) {
1260 case SILC_AUTH_NONE:
1261 /* No authentication required. */
1262 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1263 break;
1264
1265 case SILC_AUTH_PASSWORD:
1266 /* By returning NULL here the library will ask the passphrase from us
1267 by calling the silc_ask_passphrase. */
1268 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1269 break;
1270
1271 case SILC_AUTH_PUBLIC_KEY:
1272 /* Do not get the authentication data now, the library will generate
1273 it using our default key, if we do not provide it here. */
1274 (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1275 break;
1276 }
1277
1278 silc_free(internal);
1279 }
1280
1281 /* Find authentication method and authentication data by hostname and
1282 port. The hostname may be IP address as well. When the authentication
1283 method has been resolved the `completion' callback with the found
1284 authentication method and authentication data is called. The `conn'
1285 may be NULL. */
1286
1287 static void
1288 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1289 char *hostname, SilcUInt16 port,
1290 SilcGetAuthMeth completion, void *context)
1291 {
1292 GaimConnection *gc = client->application;
1293 SilcGaim sg = gc->proto_data;
1294 SilcGaimGetAuthMethod internal;
1295
1296 /* Progress */
1297 if (sg->resuming)
1298 gaim_connection_update_progress(gc, _("Resuming session"), 4, 5);
1299 else
1300 gaim_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
1301
1302 /* Check configuration if we have this connection configured. If we
1303 have then return that data immediately, as it's faster way. */
1304 if (gc->account->password && *gc->account->password) {
1305 completion(TRUE, SILC_AUTH_PASSWORD, gc->account->password,
1306 strlen(gc->account->password), context);
1307 return;
1308 }
1309 if (gaim_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
1310 completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
1311 return;
1312 }
1313
1314 /* Resolve the authentication method from server, as we may not know it. */
1315 internal = silc_calloc(1, sizeof(*internal));
1316 if (!internal)
1317 return;
1318 internal->completion = completion;
1319 internal->context = context;
1320 silc_client_request_authentication_method(client, conn,
1321 silc_get_auth_method_callback,
1322 internal);
1323 }
1324
1325
1326 /* Verifies received public key. The `conn_type' indicates which entity
1327 (server, client etc.) has sent the public key. If user decides to trust
1328 the application may save the key as trusted public key for later
1329 use. The `completion' must be called after the public key has been
1330 verified. */
1331
1332 static void
1333 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1334 SilcSocketType conn_type, unsigned char *pk,
1335 SilcUInt32 pk_len, SilcSKEPKType pk_type,
1336 SilcVerifyPublicKey completion, void *context)
1337 {
1338 GaimConnection *gc = client->application;
1339 SilcGaim sg = gc->proto_data;
1340
1341 if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
1342 conn_type == SILC_SOCKET_TYPE_ROUTER)) {
1343 /* Progress */
1344 if (sg->resuming)
1345 gaim_connection_update_progress(gc, _("Resuming session"), 3, 5);
1346 else
1347 gaim_connection_update_progress(gc, _("Verifying server public key"),
1348 3, 5);
1349 }
1350
1351 /* Verify public key */
1352 silcgaim_verify_public_key(client, conn, NULL, conn_type, pk,
1353 pk_len, pk_type, completion, context);
1354 }
1355
1356 typedef struct {
1357 SilcAskPassphrase completion;
1358 void *context;
1359 } *SilcGaimAskPassphrase;
1360
1361 static void
1362 silc_ask_passphrase_cb(SilcGaimAskPassphrase internal, const char *passphrase)
1363 {
1364 if (!passphrase || !(*passphrase))
1365 internal->completion(NULL, 0, internal->context);
1366 else
1367 internal->completion((unsigned char *)passphrase,
1368 strlen(passphrase), internal->context);
1369 silc_free(internal);
1370 }
1371
1372 /* Ask (interact, that is) a passphrase from user. The passphrase is
1373 returned to the library by calling the `completion' callback with
1374 the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1375 if not then the library will attempt to encode. */
1376
1377 static void
1378 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1379 SilcAskPassphrase completion, void *context)
1380 {
1381 SilcGaimAskPassphrase internal = silc_calloc(1, sizeof(*internal));
1382
1383 if (!internal)
1384 return;
1385 internal->completion = completion;
1386 internal->context = context;
1387 gaim_request_input(NULL, _("Passphrase"), NULL,
1388 _("Passphrase required"), NULL, FALSE, TRUE, NULL,
1389 _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
1390 _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
1391 internal);
1392 }
1393
1394
1395 /* Notifies application that failure packet was received. This is called
1396 if there is some protocol active in the client. The `protocol' is the
1397 protocol context. The `failure' is opaque pointer to the failure
1398 indication. Note, that the `failure' is protocol dependant and
1399 application must explicitly cast it to correct type. Usually `failure'
1400 is 32 bit failure type (see protocol specs for all protocol failure
1401 types). */
1402
1403 static void
1404 silc_failure(SilcClient client, SilcClientConnection conn,
1405 SilcProtocol protocol, void *failure)
1406 {
1407 GaimConnection *gc = client->application;
1408 char buf[128];
1409
1410 memset(buf, 0, sizeof(buf));
1411
1412 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1413 SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
1414
1415 if (status == SILC_SKE_STATUS_BAD_VERSION)
1416 g_snprintf(buf, sizeof(buf),
1417 _("Failure: Version mismatch, upgrade your client"));
1418 if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1419 g_snprintf(buf, sizeof(buf),
1420 _("Failure: Remote does not trust/support your public key"));
1421 if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1422 g_snprintf(buf, sizeof(buf),
1423 _("Failure: Remote does not support proposed KE group"));
1424 if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1425 g_snprintf(buf, sizeof(buf),
1426 _("Failure: Remote does not support proposed cipher"));
1427 if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1428 g_snprintf(buf, sizeof(buf),
1429 _("Failure: Remote does not support proposed PKCS"));
1430 if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1431 g_snprintf(buf, sizeof(buf),
1432 _("Failure: Remote does not support proposed hash function"));
1433 if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1434 g_snprintf(buf, sizeof(buf),
1435 _("Failure: Remote does not support proposed HMAC"));
1436 if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1437 g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
1438 if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1439 g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
1440
1441 /* Show the error on the progress bar. A more generic error message
1442 is going to be showed to user after this in the silc_connected. */
1443 gaim_connection_update_progress(gc, buf, 2, 5);
1444 }
1445
1446 if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1447 SilcUInt32 err = SILC_PTR_TO_32(failure);
1448
1449 if (err == SILC_AUTH_FAILED)
1450 g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
1451
1452 /* Show the error on the progress bar. A more generic error message
1453 is going to be showed to user after this in the silc_connected. */
1454 gaim_connection_update_progress(gc, buf, 4, 5);
1455 }
1456 }
1457
1458 /* Asks whether the user would like to perform the key agreement protocol.
1459 This is called after we have received an key agreement packet or an
1460 reply to our key agreement packet. This returns TRUE if the user wants
1461 the library to perform the key agreement protocol and FALSE if it is not
1462 desired (application may start it later by calling the function
1463 silc_client_perform_key_agreement). If TRUE is returned also the
1464 `completion' and `context' arguments must be set by the application. */
1465
1466 static bool
1467 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1468 SilcClientEntry client_entry, const char *hostname,
1469 SilcUInt16 port, SilcKeyAgreementCallback *completion,
1470 void **context)
1471 {
1472 silcgaim_buddy_keyagr_request(client, conn, client_entry, hostname, port);
1473 *completion = NULL;
1474 *context = NULL;
1475 return FALSE;
1476 }
1477
1478
1479 /* Notifies application that file transfer protocol session is being
1480 requested by the remote client indicated by the `client_entry' from
1481 the `hostname' and `port'. The `session_id' is the file transfer
1482 session and it can be used to either accept or reject the file
1483 transfer request, by calling the silc_client_file_receive or
1484 silc_client_file_close, respectively. */
1485
1486 static void
1487 silc_ftp(SilcClient client, SilcClientConnection conn,
1488 SilcClientEntry client_entry, SilcUInt32 session_id,
1489 const char *hostname, SilcUInt16 port)
1490 {
1491 silcgaim_ftp_request(client, conn, client_entry, session_id,
1492 hostname, port);
1493 }
1494
1495
1496 /* Delivers SILC session detachment data indicated by `detach_data' to the
1497 application. If application has issued SILC_COMMAND_DETACH command
1498 the client session in the SILC network is not quit. The client remains
1499 in the network but is detached. The detachment data may be used later
1500 to resume the session in the SILC Network. The appliation is
1501 responsible of saving the `detach_data', to for example in a file.
1502
1503 The detachment data can be given as argument to the functions
1504 silc_client_connect_to_server, or silc_client_add_connection when
1505 creating connection to remote server, inside SilcClientConnectionParams
1506 structure. If it is provided the client library will attempt to resume
1507 the session in the network. After the connection is created
1508 successfully, the application is responsible of setting the user
1509 interface for user into the same state it was before detaching (showing
1510 same channels, channel modes, etc). It can do this by fetching the
1511 information (like joined channels) from the client library. */
1512
1513 static void
1514 silc_detach(SilcClient client, SilcClientConnection conn,
1515 const unsigned char *detach_data, SilcUInt32 detach_data_len)
1516 {
1517 GaimConnection *gc = client->application;
1518 SilcGaim sg = gc->proto_data;
1519 const char *file;
1520
1521 /* Save the detachment data to file. */
1522 file = silcgaim_session_file(gaim_account_get_username(sg->account));
1523 unlink(file);
1524 silc_file_writefile(file, detach_data, detach_data_len);
1525 }
1526
1527 SilcClientOperations ops = {
1528 silc_say,
1529 silc_channel_message,
1530 silc_private_message,
1531 silc_notify,
1532 silc_command,
1533 silc_command_reply,
1534 silc_connected,
1535 silc_disconnected,
1536 silc_get_auth_method,
1537 silc_verify_public_key,
1538 silc_ask_passphrase,
1539 silc_failure,
1540 silc_key_agreement,
1541 silc_ftp,
1542 silc_detach
1543 };