comparison src/protocols/silc/chat.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 c8cd436b5840
comparison
equal deleted inserted replaced
8848:56e6b9bdcdbc 8849:50d0f76639e7
1 /*
2
3 silcgaim_chat.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 /***************************** Channel Routines ******************************/
25
26 GList *silcgaim_chat_info(GaimConnection *gc)
27 {
28 GList *ci = NULL;
29 struct proto_chat_entry *pce;
30
31 pce = g_new0(struct proto_chat_entry, 1);
32 pce->label = _("_Channel:");
33 pce->identifier = "channel";
34 ci = g_list_append(ci, pce);
35
36 pce = g_new0(struct proto_chat_entry, 1);
37 pce->label = _("_Passphrase:");
38 pce->identifier = "passphrase";
39 pce->secret = TRUE;
40 ci = g_list_append(ci, pce);
41
42 return ci;
43 }
44
45 static void
46 silcgaim_chat_getinfo(GaimConnection *gc, GHashTable *components);
47
48 static void
49 silcgaim_chat_getinfo_res(SilcClient client,
50 SilcClientConnection conn,
51 SilcChannelEntry *channels,
52 SilcUInt32 channels_count,
53 void *context)
54 {
55 GHashTable *components = context;
56 GaimConnection *gc = client->application;
57 const char *chname;
58 char tmp[256];
59
60 chname = g_hash_table_lookup(components, "channel");
61 if (!chname)
62 return;
63
64 if (!channels) {
65 g_snprintf(tmp, sizeof(tmp),
66 _("Channel %s does not exist in the network"), chname);
67 gaim_notify_error(gc, _("Channel Information"),
68 _("Cannot get channel information"), tmp);
69 return;
70 }
71
72 silcgaim_chat_getinfo(gc, components);
73 }
74
75 static void
76 silcgaim_chat_getinfo(GaimConnection *gc, GHashTable *components)
77 {
78 SilcGaim sg = gc->proto_data;
79 const char *chname;
80 char *buf, tmp[256];
81 GString *s;
82 SilcChannelEntry channel;
83 SilcHashTableList htl;
84 SilcChannelUser chu;
85
86 if (!components)
87 return;
88
89 chname = g_hash_table_lookup(components, "channel");
90 if (!chname)
91 return;
92 channel = silc_client_get_channel(sg->client, sg->conn,
93 (char *)chname);
94 if (!channel) {
95 silc_client_get_channel_resolve(sg->client, sg->conn,
96 (char *)chname,
97 silcgaim_chat_getinfo_res,
98 components);
99 return;
100 }
101
102 s = g_string_new("");
103 g_string_append_printf(s, "Channel Name:\t\t%s\n", channel->channel_name);
104 if (channel->user_list && silc_hash_table_count(channel->user_list))
105 g_string_append_printf(s, "User Count:\t\t%d\n",
106 (int)silc_hash_table_count(channel->user_list));
107
108 silc_hash_table_list(channel->user_list, &htl);
109 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
110 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
111 g_string_append_printf(s, "Channel Founder:\t%s\n",
112 chu->client->nickname);
113 break;
114 }
115 }
116 silc_hash_table_list_reset(&htl);
117
118 if (channel->channel_key)
119 g_string_append_printf(s, "Channel Cipher:\t\t%s\n",
120 silc_cipher_get_name(channel->channel_key));
121 if (channel->hmac)
122 g_string_append_printf(s, "Channel HMAC:\t\t%s\n",
123 silc_hmac_get_name(channel->hmac));
124
125 if (channel->topic)
126 g_string_append_printf(s, "\nChannel Topic:\n\%s\n", channel->topic);
127
128 if (channel->mode) {
129 g_string_append_printf(s, "\nChannel Modes:\n");
130 silcgaim_get_chmode_string(channel->mode, tmp, sizeof(tmp));
131 g_string_append_printf(s, tmp);
132 g_string_append_printf(s, "\n");
133 }
134
135 if (channel->founder_key) {
136 char *fingerprint, *babbleprint;
137 unsigned char *pk;
138 SilcUInt32 pk_len;
139 pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
140 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
141 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
142
143 g_string_append_printf(s, "\nFounder Key Fingerprint:\n%s\n\n", fingerprint);
144 g_string_append_printf(s, "Founder Key Babbleprint:\n%s", babbleprint);
145
146 silc_free(fingerprint);
147 silc_free(babbleprint);
148 silc_free(pk);
149 }
150
151 buf = g_string_free(s, FALSE);
152 gaim_notify_message(NULL, GAIM_NOTIFY_MSG_INFO,
153 _("Channel Information"),
154 _("Channel Information"),
155 buf, NULL, NULL);
156 g_free(buf);
157 }
158
159
160 #if 0 /* XXX For now these are not implemented. We need better
161 listview dialog from Gaim for these. */
162 /************************** Channel Invite List ******************************/
163
164 static void
165 silcgaim_chat_invitelist(GaimConnection *gc, GHashTable *components)
166 {
167
168 }
169
170
171 /**************************** Channel Ban List *******************************/
172
173 static void
174 silcgaim_chat_banlist(GaimConnection *gc, GHashTable *components)
175 {
176
177 }
178 #endif
179
180
181 /************************* Channel Authentication ****************************/
182
183 typedef struct {
184 SilcGaim sg;
185 SilcChannelEntry channel;
186 GaimChat *c;
187 SilcBuffer pubkeys;
188 } *SilcGaimChauth;
189
190 static void
191 silcgaim_chat_chpk_add(void *user_data, const char *name)
192 {
193 SilcGaimChauth sgc = (SilcGaimChauth)user_data;
194 SilcGaim sg = sgc->sg;
195 SilcClient client = sg->client;
196 SilcClientConnection conn = sg->conn;
197 SilcPublicKey public_key;
198 SilcBuffer chpks, pk, chidp;
199 unsigned char mode[4];
200 SilcUInt32 m;
201
202 /* Load the public key */
203 if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) &&
204 !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) {
205 silcgaim_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
206 silc_buffer_free(sgc->pubkeys);
207 silc_free(sgc);
208 gaim_notify_error(client->application,
209 _("Add Channel Public Key"),
210 _("Could not load public key"), NULL);
211 return;
212 }
213
214 pk = silc_pkcs_public_key_payload_encode(public_key);
215 chpks = silc_buffer_alloc_size(2);
216 SILC_PUT16_MSB(1, chpks->head);
217 chpks = silc_argument_payload_encode_one(chpks, pk->data,
218 pk->len, 0x00);
219 silc_buffer_free(pk);
220
221 m = sgc->channel->mode;
222 m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
223
224 /* Send CMODE */
225 SILC_PUT32_MSB(m, mode);
226 chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
227 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
228 ++conn->cmd_ident, 3,
229 1, chidp->data, chidp->len,
230 2, mode, sizeof(mode),
231 9, chpks->data, chpks->len);
232 silc_buffer_free(chpks);
233 silc_buffer_free(chidp);
234 silc_buffer_free(sgc->pubkeys);
235 silc_free(sgc);
236 }
237
238 static void
239 silcgaim_chat_chpk_cancel(void *user_data, const char *name)
240 {
241 SilcGaimChauth sgc = (SilcGaimChauth)user_data;
242 silcgaim_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
243 silc_buffer_free(sgc->pubkeys);
244 silc_free(sgc);
245 }
246
247 static void
248 silcgaim_chat_chpk_cb(SilcGaimChauth sgc, GaimRequestFields *fields)
249 {
250 SilcGaim sg = sgc->sg;
251 SilcClient client = sg->client;
252 SilcClientConnection conn = sg->conn;
253 GaimRequestField *f;
254 const GList *list;
255 SilcPublicKey public_key;
256 SilcBuffer chpks, pk, chidp;
257 SilcUInt16 c = 0, ct;
258 unsigned char mode[4];
259 SilcUInt32 m;
260
261 f = gaim_request_fields_get_field(fields, "list");
262 if (!gaim_request_field_list_get_selected(f)) {
263 /* Add new public key */
264 gaim_request_file(NULL, _("Open Public Key..."), _(""),
265 G_CALLBACK(silcgaim_chat_chpk_add),
266 G_CALLBACK(silcgaim_chat_chpk_cancel), sgc);
267 return;
268 }
269
270 list = gaim_request_field_list_get_items(f);
271 chpks = silc_buffer_alloc_size(2);
272
273 for (ct = 0; list; list = list->next, ct++) {
274 public_key = gaim_request_field_list_get_data(f, list->data);
275 if (gaim_request_field_list_is_selected(f, list->data)) {
276 /* Delete this public key */
277 pk = silc_pkcs_public_key_payload_encode(public_key);
278 chpks = silc_argument_payload_encode_one(chpks, pk->data,
279 pk->len, 0x01);
280 silc_buffer_free(pk);
281 c++;
282 }
283 silc_pkcs_public_key_free(public_key);
284 }
285 if (!c) {
286 silc_buffer_free(chpks);
287 return;
288 }
289 SILC_PUT16_MSB(c, chpks->head);
290
291 m = sgc->channel->mode;
292 if (ct == c)
293 m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
294
295 /* Send CMODE */
296 SILC_PUT32_MSB(m, mode);
297 chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL);
298 silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
299 ++conn->cmd_ident, 3,
300 1, chidp->data, chidp->len,
301 2, mode, sizeof(mode),
302 9, chpks->data, chpks->len);
303 silc_buffer_free(chpks);
304 silc_buffer_free(chidp);
305 silc_buffer_free(sgc->pubkeys);
306 silc_free(sgc);
307 }
308
309 static void
310 silcgaim_chat_chauth_ok(SilcGaimChauth sgc, GaimRequestFields *fields)
311 {
312 SilcGaim sg = sgc->sg;
313 GaimRequestField *f;
314 const char *curpass, *val;
315 int set;
316
317 f = gaim_request_fields_get_field(fields, "passphrase");
318 val = gaim_request_field_string_get_value(f);
319 curpass = gaim_blist_node_get_string((GaimBlistNode *)sgc->c, "passphrase");
320
321 if (!val && curpass)
322 set = 0;
323 else if (val && !curpass)
324 set = 1;
325 else if (val && curpass && strcmp(val, curpass))
326 set = 1;
327 else
328 set = -1;
329
330 if (set == 1) {
331 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
332 sgc->channel->channel_name, "+a", val, NULL);
333 gaim_blist_node_set_string((GaimBlistNode *)sgc->c, "passphrase", val);
334 gaim_blist_save();
335 } else if (set == 0) {
336 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
337 sgc->channel->channel_name, "-a", NULL);
338 gaim_blist_node_remove_setting((GaimBlistNode *)sgc->c, "passphrase");
339 gaim_blist_save();
340 }
341
342 silc_buffer_free(sgc->pubkeys);
343 silc_free(sgc);
344 }
345
346 void silcgaim_chat_chauth_show(SilcGaim sg, SilcChannelEntry channel,
347 SilcBuffer channel_pubkeys)
348 {
349 SilcUInt16 argc;
350 SilcArgumentPayload chpks;
351 unsigned char *pk;
352 SilcUInt32 pk_len, type;
353 char *fingerprint, *babbleprint;
354 SilcPublicKey pubkey;
355 SilcPublicKeyIdentifier ident;
356 char tmp2[1024], t[512];
357 GaimRequestFields *fields;
358 GaimRequestFieldGroup *g;
359 GaimRequestField *f;
360 SilcGaimChauth sgc;
361 const char *curpass = NULL;
362
363 sgc = silc_calloc(1, sizeof(*sgc));
364 if (!sgc)
365 return;
366 sgc->sg = sg;
367 sgc->channel = channel;
368
369 fields = gaim_request_fields_new();
370
371 if (sgc->c)
372 curpass = gaim_blist_node_get_string((GaimBlistNode *)sgc->c, "passphrase");
373
374 g = gaim_request_field_group_new(NULL);
375 f = gaim_request_field_string_new("passphrase", _("Channel Passphrase"),
376 curpass, FALSE);
377 gaim_request_field_string_set_masked(f, TRUE);
378 gaim_request_field_group_add_field(g, f);
379 gaim_request_fields_add_group(fields, g);
380
381 g = gaim_request_field_group_new(NULL);
382 f = gaim_request_field_label_new("l1", _("Channel Public Keys List"));
383 gaim_request_field_group_add_field(g, f);
384 gaim_request_fields_add_group(fields, g);
385
386 g_snprintf(t, sizeof(t),
387 _("Channel authentication is used to secure the channel from "
388 "unauthorized access. The authentication may be based on "
389 "passphrase and digital signatures. If passphrase is set, it "
390 "is required to be able to join. If channel public keys are set "
391 "then only users whose public keys are listed are able to join."));
392
393 if (!channel_pubkeys) {
394 f = gaim_request_field_list_new("list", NULL);
395 gaim_request_field_group_add_field(g, f);
396 gaim_request_fields(NULL, _("Channel Authentication"),
397 _("Channel Authentication"), t, fields,
398 "Add / Remove", G_CALLBACK(silcgaim_chat_chpk_cb),
399 "OK", G_CALLBACK(silcgaim_chat_chauth_ok), sgc);
400 return;
401 }
402 sgc->pubkeys = silc_buffer_copy(channel_pubkeys);
403
404 g = gaim_request_field_group_new(NULL);
405 f = gaim_request_field_list_new("list", NULL);
406 gaim_request_field_group_add_field(g, f);
407 gaim_request_fields_add_group(fields, g);
408
409 SILC_GET16_MSB(argc, channel_pubkeys->data);
410 chpks = silc_argument_payload_parse(channel_pubkeys->data + 2,
411 channel_pubkeys->len - 2, argc);
412 if (!chpks)
413 return;
414
415 pk = silc_argument_get_first_arg(chpks, &type, &pk_len);
416 while (pk) {
417 fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
418 babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
419 silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey);
420 ident = silc_pkcs_decode_identifier(pubkey->identifier);
421
422 g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s",
423 ident->realname ? ident->realname : ident->username ?
424 ident->username : "", fingerprint, babbleprint);
425 gaim_request_field_list_add(f, tmp2, pubkey);
426
427 silc_free(fingerprint);
428 silc_free(babbleprint);
429 silc_pkcs_free_identifier(ident);
430 pk = silc_argument_get_next_arg(chpks, &type, &pk_len);
431 }
432
433 gaim_request_field_list_set_multi_select(f, FALSE);
434 gaim_request_fields(NULL, _("Channel Authentication"),
435 _("Channel Authentication"), t, fields,
436 "Add / Remove", G_CALLBACK(silcgaim_chat_chpk_cb),
437 "OK", G_CALLBACK(silcgaim_chat_chauth_ok), sgc);
438
439 silc_argument_payload_free(chpks);
440 }
441
442 static void
443 silcgaim_chat_chauth(GaimConnection *gc, GHashTable *components)
444 {
445 SilcGaim sg = gc->proto_data;
446 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
447 g_hash_table_lookup(components, "channel"),
448 "+C", NULL);
449 }
450
451
452 /************************** Channel Private Groups **************************/
453
454 /* Private groups are "virtual" channels. They are groups inside a channel.
455 This is implemented by using channel private keys. By knowing a channel
456 private key user becomes part of that group and is able to talk on that
457 group. Other users, on the same channel, won't be able to see the
458 messages of that group. It is possible to have multiple groups inside
459 a channel - and thus having multiple private keys on the channel. */
460
461 typedef struct {
462 SilcGaim sg;
463 GaimChat *c;
464 const char *channel;
465 } *SilcGaimCharPrv;
466
467 static void
468 silcgaim_chat_prv_add(SilcGaimCharPrv p, GaimRequestFields *fields)
469 {
470 SilcGaim sg = p->sg;
471 char tmp[512];
472 GaimRequestField *f;
473 const char *name, *passphrase, *alias;
474 GHashTable *comp;
475 GaimGroup *g;
476 GaimChat *cn;
477
478 f = gaim_request_fields_get_field(fields, "name");
479 name = gaim_request_field_string_get_value(f);
480 if (!name) {
481 silc_free(p);
482 return;
483 }
484 f = gaim_request_fields_get_field(fields, "passphrase");
485 passphrase = gaim_request_field_string_get_value(f);
486 f = gaim_request_fields_get_field(fields, "alias");
487 alias = gaim_request_field_string_get_value(f);
488
489 /* Add private group to buddy list */
490 g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
491 comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
492 g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp));
493 g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase));
494
495 cn = gaim_chat_new(sg->account, alias, comp);
496 g = (GaimGroup *)p->c->node.parent;
497 gaim_blist_add_chat(cn, g, (GaimBlistNode *)p->c);
498
499 /* Associate to a real channel */
500 gaim_blist_node_set_string((GaimBlistNode *)cn, "parentch", p->channel);
501 gaim_blist_save();
502
503 /* Join the group */
504 silcgaim_chat_join(sg->gc, comp);
505
506 silc_free(p);
507 }
508
509 static void
510 silcgaim_chat_prv_cancel(SilcGaimCharPrv p, GaimRequestFields *fields)
511 {
512 silc_free(p);
513 }
514
515 static void
516 silcgaim_chat_prv(GaimConnection *gc, GHashTable *components)
517 {
518 SilcGaim sg = gc->proto_data;
519 SilcGaimCharPrv p;
520 GaimRequestFields *fields;
521 GaimRequestFieldGroup *g;
522 GaimRequestField *f;
523 char tmp[512];
524
525 p = silc_calloc(1, sizeof(*p));
526 if (!p)
527 return;
528 p->sg = sg;
529
530 p->channel = g_hash_table_lookup(components, "channel");
531 p->c = gaim_blist_find_chat(sg->account, p->channel);
532
533 fields = gaim_request_fields_new();
534
535 g = gaim_request_field_group_new(NULL);
536 f = gaim_request_field_string_new("name", _("Group Name"),
537 NULL, FALSE);
538 gaim_request_field_group_add_field(g, f);
539
540 f = gaim_request_field_string_new("passphrase", _("Passphrase"),
541 NULL, FALSE);
542 gaim_request_field_string_set_masked(f, TRUE);
543 gaim_request_field_group_add_field(g, f);
544
545 f = gaim_request_field_string_new("alias", _("Alias"),
546 NULL, FALSE);
547 gaim_request_field_group_add_field(g, f);
548 gaim_request_fields_add_group(fields, g);
549
550 g_snprintf(tmp, sizeof(tmp),
551 _("Please enter the %s channel private group name and passphrase."),
552 p->channel);
553 gaim_request_fields(NULL, _("Add Channel Private Group"), NULL, tmp, fields,
554 "Add", G_CALLBACK(silcgaim_chat_prv_add),
555 "Cancel", G_CALLBACK(silcgaim_chat_prv_cancel), p);
556 }
557
558
559 /****************************** Channel Modes ********************************/
560
561 static void
562 silcgaim_chat_permanent_reset(GaimConnection *gc, GHashTable *components)
563 {
564 SilcGaim sg = gc->proto_data;
565 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
566 g_hash_table_lookup(components, "channel"),
567 "-f", NULL);
568 }
569
570 static void
571 silcgaim_chat_permanent(GaimConnection *gc, GHashTable *components)
572 {
573 SilcGaim sg = gc->proto_data;
574 const char *channel;
575
576 if (!sg->conn)
577 return;
578
579 /* XXX we should have ability to define which founder
580 key to use. Now we use the user's own public key
581 (default key). */
582
583 /* Call CMODE */
584 channel = g_hash_table_lookup(components, "channel");
585 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
586 "+f", NULL);
587 }
588
589 typedef struct {
590 SilcGaim sg;
591 const char *channel;
592 } *SilcGaimChatInput;
593
594 static void
595 silcgaim_chat_ulimit_cb(SilcGaimChatInput s, const char *limit)
596 {
597 SilcChannelEntry channel;
598 int ulimit = 0;
599
600 channel = silc_client_get_channel(s->sg->client, s->sg->conn,
601 (char *)s->channel);
602 if (!channel)
603 return;
604 if (limit)
605 ulimit = atoi(limit);
606
607 if (!limit || !(*limit) || *limit == '0') {
608 if (limit && ulimit == channel->user_limit) {
609 silc_free(s);
610 return;
611 }
612 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
613 s->channel, "-l", NULL);
614
615 silc_free(s);
616 return;
617 }
618
619 if (ulimit == channel->user_limit) {
620 silc_free(s);
621 return;
622 }
623
624 /* Call CMODE */
625 silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
626 s->channel, "+l", limit, NULL);
627
628 silc_free(s);
629 }
630
631 static void
632 silcgaim_chat_ulimit(GaimConnection *gc, GHashTable *components)
633 {
634 SilcGaim sg = gc->proto_data;
635 SilcGaimChatInput s;
636 SilcChannelEntry channel;
637 const char *ch;
638 char tmp[32];
639
640 if (!sg->conn)
641 return;
642
643 ch = g_strdup(g_hash_table_lookup(components, "channel"));
644 channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
645 if (!channel)
646 return;
647
648 s = silc_calloc(1, sizeof(*s));
649 if (!s)
650 return;
651 s->channel = ch;
652 s->sg = sg;
653 g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
654 gaim_request_input(NULL, _("User Limit"), NULL,
655 _("Set user limit on channel. Set to zero to reset user limit."),
656 tmp, FALSE, FALSE, NULL,
657 _("OK"), G_CALLBACK(silcgaim_chat_ulimit_cb),
658 _("Cancel"), G_CALLBACK(silcgaim_chat_ulimit_cb), s);
659 }
660
661 static void
662 silcgaim_chat_resettopic(GaimConnection *gc, GHashTable *components)
663 {
664 SilcGaim sg = gc->proto_data;
665 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
666 g_hash_table_lookup(components, "channel"),
667 "-t", NULL);
668 }
669
670 static void
671 silcgaim_chat_settopic(GaimConnection *gc, GHashTable *components)
672 {
673 SilcGaim sg = gc->proto_data;
674 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
675 g_hash_table_lookup(components, "channel"),
676 "+t", NULL);
677 }
678
679 static void
680 silcgaim_chat_resetprivate(GaimConnection *gc, GHashTable *components)
681 {
682 SilcGaim sg = gc->proto_data;
683 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
684 g_hash_table_lookup(components, "channel"),
685 "-p", NULL);
686 }
687
688 static void
689 silcgaim_chat_setprivate(GaimConnection *gc, GHashTable *components)
690 {
691 SilcGaim sg = gc->proto_data;
692 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
693 g_hash_table_lookup(components, "channel"),
694 "+p", NULL);
695 }
696
697 static void
698 silcgaim_chat_resetsecret(GaimConnection *gc, GHashTable *components)
699 {
700 SilcGaim sg = gc->proto_data;
701 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
702 g_hash_table_lookup(components, "channel"),
703 "-s", NULL);
704 }
705
706 static void
707 silcgaim_chat_setsecret(GaimConnection *gc, GHashTable *components)
708 {
709 SilcGaim sg = gc->proto_data;
710 silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
711 g_hash_table_lookup(components, "channel"),
712 "+s", NULL);
713 }
714
715 GList *silcgaim_chat_menu(GaimConnection *gc, GHashTable *components)
716 {
717 SilcGaim sg = gc->proto_data;
718 SilcClientConnection conn = sg->conn;
719 GList *m = NULL;
720 struct proto_chat_menu *pcm;
721 const char *chname = NULL;
722 SilcChannelEntry channel = NULL;
723 SilcChannelUser chu = NULL;
724 SilcUInt32 mode = 0;
725
726 if (components)
727 chname = g_hash_table_lookup(components, "channel");
728 if (chname)
729 channel = silc_client_get_channel(sg->client, sg->conn,
730 (char *)chname);
731 if (channel) {
732 chu = silc_client_on_channel(channel, conn->local_entry);
733 if (chu)
734 mode = chu->mode;
735 }
736
737 if (strstr(chname, "[Private Group]"))
738 return NULL;
739
740 pcm = g_new0(struct proto_chat_menu, 1);
741 pcm->label = _("Get Info");
742 pcm->callback = silcgaim_chat_getinfo;
743 pcm->gc = gc;
744 m = g_list_append(m, pcm);
745
746 #if 0 /* XXX For now these are not implemented. We need better
747 listview dialog from Gaim for these. */
748 if (mode & SILC_CHANNEL_UMODE_CHANOP) {
749 pcm = g_new0(struct proto_chat_menu, 1);
750 pcm->label = _("Invite List");
751 pcm->callback = silcgaim_chat_invitelist;
752 pcm->gc = gc;
753 m = g_list_append(m, pcm);
754
755 pcm = g_new0(struct proto_chat_menu, 1);
756 pcm->label = _("Ban List");
757 pcm->callback = silcgaim_chat_banlist;
758 pcm->gc = gc;
759 m = g_list_append(m, pcm);
760 }
761 #endif
762
763 if (chu) {
764 pcm = g_new0(struct proto_chat_menu, 1);
765 pcm->label = _("Add Private Group");
766 pcm->callback = silcgaim_chat_prv;
767 pcm->gc = gc;
768 m = g_list_append(m, pcm);
769 }
770
771 if (mode & SILC_CHANNEL_UMODE_CHANFO) {
772 pcm = g_new0(struct proto_chat_menu, 1);
773 pcm->label = _("Channel Authentication");
774 pcm->callback = silcgaim_chat_chauth;
775 pcm->gc = gc;
776 m = g_list_append(m, pcm);
777
778 if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
779 pcm = g_new0(struct proto_chat_menu, 1);
780 pcm->label = _("Reset Permanent");
781 pcm->callback = silcgaim_chat_permanent_reset;
782 pcm->gc = gc;
783 m = g_list_append(m, pcm);
784 } else {
785 pcm = g_new0(struct proto_chat_menu, 1);
786 pcm->label = _("Set Permanent");
787 pcm->callback = silcgaim_chat_permanent;
788 pcm->gc = gc;
789 m = g_list_append(m, pcm);
790 }
791 }
792
793 if (mode & SILC_CHANNEL_UMODE_CHANOP) {
794 pcm = g_new0(struct proto_chat_menu, 1);
795 pcm->label = _("Set User Limit");
796 pcm->callback = silcgaim_chat_ulimit;
797 pcm->gc = gc;
798 m = g_list_append(m, pcm);
799
800 if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
801 pcm = g_new0(struct proto_chat_menu, 1);
802 pcm->label = _("Reset Topic Restriction");
803 pcm->callback = silcgaim_chat_resettopic;
804 pcm->gc = gc;
805 m = g_list_append(m, pcm);
806 } else {
807 pcm = g_new0(struct proto_chat_menu, 1);
808 pcm->label = _("Set Topic Restriction");
809 pcm->callback = silcgaim_chat_settopic;
810 pcm->gc = gc;
811 m = g_list_append(m, pcm);
812 }
813
814 if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
815 pcm = g_new0(struct proto_chat_menu, 1);
816 pcm->label = _("Reset Private Channel");
817 pcm->callback = silcgaim_chat_resetprivate;
818 pcm->gc = gc;
819 m = g_list_append(m, pcm);
820 } else {
821 pcm = g_new0(struct proto_chat_menu, 1);
822 pcm->label = _("Set Private Channel");
823 pcm->callback = silcgaim_chat_setprivate;
824 pcm->gc = gc;
825 m = g_list_append(m, pcm);
826 }
827
828 if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
829 pcm = g_new0(struct proto_chat_menu, 1);
830 pcm->label = _("Reset Secret Channel");
831 pcm->callback = silcgaim_chat_resetsecret;
832 pcm->gc = gc;
833 m = g_list_append(m, pcm);
834 } else {
835 pcm = g_new0(struct proto_chat_menu, 1);
836 pcm->label = _("Set Secret Channel");
837 pcm->callback = silcgaim_chat_setsecret;
838 pcm->gc = gc;
839 m = g_list_append(m, pcm);
840 }
841 }
842
843 return m;
844 }
845
846
847 /******************************* Joining Etc. ********************************/
848
849 void silcgaim_chat_join_done(SilcClient client,
850 SilcClientConnection conn,
851 SilcClientEntry *clients,
852 SilcUInt32 clients_count,
853 void *context)
854 {
855 GaimConnection *gc = client->application;
856 SilcGaim sg = gc->proto_data;
857 SilcChannelEntry channel = context;
858 GaimConversation *convo;
859 SilcUInt32 retry = SILC_PTR_TO_32(channel->context);
860 SilcHashTableList htl;
861 SilcChannelUser chu;
862 GList *users = NULL;
863 char tmp[256];
864
865 if (!clients && retry < 1) {
866 /* Resolving users failed, try again. */
867 channel->context = SILC_32_TO_PTR(retry + 1);
868 silc_client_get_clients_by_channel(client, conn, channel,
869 silcgaim_chat_join_done, channel);
870 return;
871 }
872
873 /* Add channel to Gaim */
874 channel->context = SILC_32_TO_PTR(++sg->channel_ids);
875 serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name);
876 convo = gaim_find_conversation_with_account(channel->channel_name,
877 sg->account);
878 if (!convo)
879 return;
880
881 /* Add all users to channel */
882 silc_hash_table_list(channel->user_list, &htl);
883 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
884 if (!chu->client->nickname)
885 continue;
886 chu->context = SILC_32_TO_PTR(sg->channel_ids);
887
888 #if 0 /* XXX don't append mode char to nick because Gaim doesn't
889 give a way to change it afterwards when mode changes. */
890 tmp2 = silc_client_chumode_char(chu->mode);
891 if (tmp2)
892 g_snprintf(tmp, sizeof(tmp), _("%s%s"), tmp2,
893 chu->client->nickname);
894 else
895 g_snprintf(tmp, sizeof(tmp), _("%s"),
896 chu->client->nickname);
897 silc_free(tmp2);
898
899 users = g_list_append(users, g_strdup(tmp));
900 #else
901 users = g_list_append(users, g_strdup(chu->client->nickname));
902 #endif
903
904 if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
905 if (chu->client == conn->local_entry)
906 g_snprintf(tmp, sizeof(tmp),
907 _("You are channel founder on <I>%s</I>"),
908 channel->channel_name);
909 else
910 g_snprintf(tmp, sizeof(tmp),
911 _("Channel founder on <I>%s</I> is <I>%s</I>"),
912 channel->channel_name, chu->client->nickname);
913
914 gaim_conversation_write(convo, NULL, tmp,
915 GAIM_MESSAGE_SYSTEM, time(NULL));
916
917 }
918 }
919 silc_hash_table_list_reset(&htl);
920
921 gaim_conv_chat_add_users(GAIM_CONV_CHAT(convo), users);
922 g_list_free(users);
923
924 /* Set topic */
925 if (channel->topic)
926 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(convo), NULL, channel->topic);
927 }
928
929 void silcgaim_chat_join(GaimConnection *gc, GHashTable *data)
930 {
931 SilcGaim sg = gc->proto_data;
932 SilcClient client = sg->client;
933 SilcClientConnection conn = sg->conn;
934 const char *channel, *passphrase, *parentch;
935
936 if (!conn)
937 return;
938
939 channel = g_hash_table_lookup(data, "channel");
940 passphrase = g_hash_table_lookup(data, "passphrase");
941
942 /* Check if we are joining a private group. Handle it
943 purely locally as it's not a real channel */
944 if (strstr(channel, "[Private Group]")) {
945 SilcChannelEntry channel_entry;
946 SilcChannelPrivateKey key;
947 GaimChat *c;
948 SilcGaimPrvgrp grp;
949
950 c = gaim_blist_find_chat(sg->account, channel);
951 parentch = gaim_blist_node_get_string((GaimBlistNode *)c, "parentch");
952 if (!parentch)
953 return;
954
955 channel_entry = silc_client_get_channel(sg->client, sg->conn,
956 (char *)parentch);
957 if (!channel_entry ||
958 !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
959 char tmp[512];
960 g_snprintf(tmp, sizeof(tmp),
961 _("You have to join the %s channel before you are "
962 "able to join the private group"), parentch);
963 gaim_notify_error(gc, _("Join Private Group"),
964 _("Cannot join private group"), tmp);
965 return;
966 }
967
968 /* Add channel private key */
969 if (!silc_client_add_channel_private_key(client, conn,
970 channel_entry, channel,
971 NULL, NULL,
972 (unsigned char *)passphrase,
973 strlen(passphrase), &key))
974 return;
975
976 /* Join the group */
977 grp = silc_calloc(1, sizeof(*grp));
978 if (!grp)
979 return;
980 grp->id = ++sg->channel_ids + SILCGAIM_PRVGRP;
981 grp->chid = SILC_PTR_TO_32(channel_entry->context);
982 grp->parentch = parentch;
983 grp->channel = channel;
984 grp->key = key;
985 sg->grps = g_list_append(sg->grps, grp);
986 serv_got_joined_chat(gc, grp->id, channel);
987 return;
988 }
989
990 /* XXX We should have other properties here as well:
991 1. whether to try to authenticate to the channel
992 1a. with default key,
993 1b. with specific key.
994 2. whether to try to authenticate to become founder.
995 2a. with default key,
996 2b. with specific key.
997
998 Since now such variety is not possible in the join dialog
999 we always use -founder and -auth options, which try to
1000 do both 1 and 2 with default keys. */
1001
1002 /* Call JOIN */
1003 if (passphrase)
1004 silc_client_command_call(client, conn, NULL, "JOIN",
1005 channel, passphrase, "-auth", "-founder", NULL);
1006 else
1007 silc_client_command_call(client, conn, NULL, "JOIN",
1008 channel, "-auth", "-founder", NULL);
1009 }
1010
1011 void silcgaim_chat_invite(GaimConnection *gc, int id, const char *msg,
1012 const char *name)
1013 {
1014 SilcGaim sg = gc->proto_data;
1015 SilcClient client = sg->client;
1016 SilcClientConnection conn = sg->conn;
1017 SilcHashTableList htl;
1018 SilcChannelUser chu;
1019 gboolean found = FALSE;
1020
1021 if (!conn)
1022 return;
1023
1024 /* See if we are inviting on a private group. Invite
1025 to the actual channel */
1026 if (id > SILCGAIM_PRVGRP) {
1027 GList *l;
1028 SilcGaimPrvgrp prv;
1029
1030 for (l = sg->grps; l; l = l->next)
1031 if (((SilcGaimPrvgrp)l->data)->id == id)
1032 break;
1033 if (!l)
1034 return;
1035 prv = l->data;
1036 id = prv->chid;
1037 }
1038
1039 /* Find channel by id */
1040 silc_hash_table_list(conn->local_entry->channels, &htl);
1041 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1042 if (SILC_PTR_TO_32(chu->channel->context) == id ) {
1043 found = TRUE;
1044 break;
1045 }
1046 }
1047 silc_hash_table_list_reset(&htl);
1048 if (!found)
1049 return;
1050
1051 /* Call INVITE */
1052 silc_client_command_call(client, conn, NULL, "INVITE",
1053 chu->channel->channel_name,
1054 name);
1055 }
1056
1057 void silcgaim_chat_leave(GaimConnection *gc, int id)
1058 {
1059 SilcGaim sg = gc->proto_data;
1060 SilcClient client = sg->client;
1061 SilcClientConnection conn = sg->conn;
1062 SilcHashTableList htl;
1063 SilcChannelUser chu;
1064 gboolean found = FALSE;
1065 GList *l;
1066 SilcGaimPrvgrp prv;
1067
1068 if (!conn)
1069 return;
1070
1071 /* See if we are leaving a private group */
1072 if (id > SILCGAIM_PRVGRP) {
1073 SilcChannelEntry channel;
1074
1075 for (l = sg->grps; l; l = l->next)
1076 if (((SilcGaimPrvgrp)l->data)->id == id)
1077 break;
1078 if (!l)
1079 return;
1080 prv = l->data;
1081 channel = silc_client_get_channel(sg->client, sg->conn,
1082 (char *)prv->parentch);
1083 if (!channel)
1084 return;
1085 silc_client_del_channel_private_key(client, conn,
1086 channel, prv->key);
1087 silc_free(prv);
1088 sg->grps = g_list_remove(sg->grps, prv);
1089 serv_got_chat_left(gc, id);
1090 return;
1091 }
1092
1093 /* Find channel by id */
1094 silc_hash_table_list(conn->local_entry->channels, &htl);
1095 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1096 if (SILC_PTR_TO_32(chu->channel->context) == id ) {
1097 found = TRUE;
1098 break;
1099 }
1100 }
1101 silc_hash_table_list_reset(&htl);
1102 if (!found)
1103 return;
1104
1105 /* Call LEAVE */
1106 silc_client_command_call(client, conn, NULL, "LEAVE",
1107 chu->channel->channel_name, NULL);
1108
1109 serv_got_chat_left(gc, id);
1110
1111 /* Leave from private groups on this channel as well */
1112 for (l = sg->grps; l; l = l->next)
1113 if (((SilcGaimPrvgrp)l->data)->chid == id) {
1114 prv = l->data;
1115 silc_client_del_channel_private_key(client, conn,
1116 chu->channel,
1117 prv->key);
1118 serv_got_chat_left(gc, prv->id);
1119 silc_free(prv);
1120 sg->grps = g_list_remove(sg->grps, prv);
1121 if (!sg->grps)
1122 break;
1123 }
1124 }
1125
1126 int silcgaim_chat_send(GaimConnection *gc, int id, const char *msg)
1127 {
1128 SilcGaim sg = gc->proto_data;
1129 SilcClient client = sg->client;
1130 SilcClientConnection conn = sg->conn;
1131 SilcHashTableList htl;
1132 SilcChannelUser chu;
1133 SilcChannelEntry channel = NULL;
1134 SilcChannelPrivateKey key = NULL;
1135 SilcUInt32 flags;
1136 int ret;
1137 gboolean found = FALSE;
1138 gboolean sign = gaim_prefs_get_bool("/plugins/prpl/silc/sign_chat");
1139
1140 if (!msg || !conn)
1141 return 0;
1142
1143 /* See if command */
1144 if (strlen(msg) > 1 && msg[0] == '/') {
1145 if (!silc_client_command_call(client, conn, msg + 1))
1146 gaim_notify_error(gc, ("Call Command"), _("Cannot call command"),
1147 _("Unknown command"));
1148 return 0;
1149 }
1150
1151 flags = SILC_MESSAGE_FLAG_UTF8;
1152 if (sign)
1153 flags |= SILC_MESSAGE_FLAG_SIGNED;
1154
1155 /* Get the channel private key if we are sending on
1156 private group */
1157 if (id > SILCGAIM_PRVGRP) {
1158 GList *l;
1159 SilcGaimPrvgrp prv;
1160
1161 for (l = sg->grps; l; l = l->next)
1162 if (((SilcGaimPrvgrp)l->data)->id == id)
1163 break;
1164 if (!l)
1165 return 0;
1166 prv = l->data;
1167 channel = silc_client_get_channel(sg->client, sg->conn,
1168 (char *)prv->parentch);
1169 if (!channel)
1170 return 0;
1171 key = prv->key;
1172 }
1173
1174 if (!channel) {
1175 /* Find channel by id */
1176 silc_hash_table_list(conn->local_entry->channels, &htl);
1177 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1178 if (SILC_PTR_TO_32(chu->channel->context) == id ) {
1179 found = TRUE;
1180 break;
1181 }
1182 }
1183 silc_hash_table_list_reset(&htl);
1184 if (!found)
1185 return 0;
1186 channel = chu->channel;
1187 }
1188
1189 /* Send channel message */
1190 ret = silc_client_send_channel_message(client, conn, channel, key,
1191 flags, (unsigned char *)msg,
1192 strlen(msg), TRUE);
1193 if (ret)
1194 serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, msg,
1195 time(NULL));
1196
1197 return ret;
1198 }
1199
1200 void silcgaim_chat_set_topic(GaimConnection *gc, int id, const char *topic)
1201 {
1202 SilcGaim sg = gc->proto_data;
1203 SilcClient client = sg->client;
1204 SilcClientConnection conn = sg->conn;
1205 SilcHashTableList htl;
1206 SilcChannelUser chu;
1207 gboolean found = FALSE;
1208
1209 if (!topic || !conn)
1210 return;
1211
1212 /* See if setting topic on private group. Set it
1213 on the actual channel */
1214 if (id > SILCGAIM_PRVGRP) {
1215 GList *l;
1216 SilcGaimPrvgrp prv;
1217
1218 for (l = sg->grps; l; l = l->next)
1219 if (((SilcGaimPrvgrp)l->data)->id == id)
1220 break;
1221 if (!l)
1222 return;
1223 prv = l->data;
1224 id = prv->chid;
1225 }
1226
1227 /* Find channel by id */
1228 silc_hash_table_list(conn->local_entry->channels, &htl);
1229 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1230 if (SILC_PTR_TO_32(chu->channel->context) == id ) {
1231 found = TRUE;
1232 break;
1233 }
1234 }
1235 silc_hash_table_list_reset(&htl);
1236 if (!found)
1237 return;
1238
1239 /* Call TOPIC */
1240 silc_client_command_call(client, conn, NULL, "TOPIC",
1241 chu->channel->channel_name, topic, NULL);
1242 }
1243
1244 GaimRoomlist *silcgaim_roomlist_get_list(GaimConnection *gc)
1245 {
1246 SilcGaim sg = gc->proto_data;
1247 SilcClient client = sg->client;
1248 SilcClientConnection conn = sg->conn;
1249 GList *fields = NULL;
1250 GaimRoomlistField *f;
1251
1252 if (!conn)
1253 return NULL;
1254
1255 if (sg->roomlist)
1256 gaim_roomlist_unref(sg->roomlist);
1257
1258 sg->roomlist_canceled = FALSE;
1259
1260 sg->roomlist = gaim_roomlist_new(gaim_connection_get_account(gc));
1261 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
1262 fields = g_list_append(fields, f);
1263 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT,
1264 _("Users"), "users", FALSE);
1265 fields = g_list_append(fields, f);
1266 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING,
1267 _("Topic"), "topic", FALSE);
1268 fields = g_list_append(fields, f);
1269 gaim_roomlist_set_fields(sg->roomlist, fields);
1270
1271 /* Call LIST */
1272 silc_client_command_call(client, conn, "LIST");
1273
1274 gaim_roomlist_set_in_progress(sg->roomlist, TRUE);
1275
1276 return sg->roomlist;
1277 }
1278
1279 void silcgaim_roomlist_cancel(GaimRoomlist *list)
1280 {
1281 GaimConnection *gc = gaim_account_get_connection(list->account);
1282 SilcGaim sg;
1283
1284 if (!gc)
1285 return;
1286 sg = gc->proto_data;
1287
1288 gaim_roomlist_set_in_progress(list, FALSE);
1289 if (sg->roomlist == list) {
1290 gaim_roomlist_unref(sg->roomlist);
1291 sg->roomlist = NULL;
1292 sg->roomlist_canceled = TRUE;
1293 }
1294 }