Mercurial > pidgin.yaz
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 }; |