1 /*
2 
3   silcpurple_chat.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2004 - 2007 Pekka Riikonen
8 
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18 */
19 
20 #include "internal.h"
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "silcpurple.h"
24 #include "wb.h"
25 
26 /***************************** Channel Routines ******************************/
27 
silcpurple_chat_info(PurpleConnection * gc)28 GList *silcpurple_chat_info(PurpleConnection *gc)
29 {
30 	GList *ci = NULL;
31 	struct proto_chat_entry *pce;
32 
33 	pce = g_new0(struct proto_chat_entry, 1);
34 	pce->label = _("_Channel:");
35 	pce->identifier = "channel";
36 	pce->required = TRUE;
37 	ci = g_list_append(ci, pce);
38 
39 	pce = g_new0(struct proto_chat_entry, 1);
40 	pce->label = _("_Passphrase:");
41 	pce->identifier = "passphrase";
42 	pce->secret = TRUE;
43 	ci = g_list_append(ci, pce);
44 
45 	return ci;
46 }
47 
silcpurple_chat_info_defaults(PurpleConnection * gc,const char * chat_name)48 GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
49 {
50 	GHashTable *defaults;
51 
52 	defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
53 
54 	if (chat_name != NULL)
55 		g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
56 
57 	return defaults;
58 }
59 
60 static void
61 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components);
62 
63 static void
silcpurple_chat_getinfo_res(SilcClient client,SilcClientConnection conn,SilcStatus status,SilcDList channels,void * context)64 silcpurple_chat_getinfo_res(SilcClient client,
65 			    SilcClientConnection conn,
66 			    SilcStatus status,
67 			    SilcDList channels,
68 			    void *context)
69 {
70 	GHashTable *components = context;
71 	PurpleConnection *gc = client->application;
72 	const char *chname;
73 	char tmp[256];
74 
75 	chname = g_hash_table_lookup(components, "channel");
76 	if (!chname)
77 		return;
78 
79 	if (!channels) {
80 		g_snprintf(tmp, sizeof(tmp),
81 			   _("Channel %s does not exist in the network"), chname);
82 		purple_notify_error(gc, _("Channel Information"),
83 				  _("Cannot get channel information"), tmp);
84 		return;
85 	}
86 
87 	silcpurple_chat_getinfo(gc, components);
88 }
89 
90 
91 static void
silcpurple_chat_getinfo(PurpleConnection * gc,GHashTable * components)92 silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components)
93 {
94 	SilcPurple sg = gc->proto_data;
95 	const char *chname;
96 	char tmp[256], *tmp2;
97 	GString *s;
98 	SilcChannelEntry channel;
99 	SilcHashTableList htl;
100 	SilcChannelUser chu;
101 
102 	if (!components)
103 		return;
104 
105 	chname = g_hash_table_lookup(components, "channel");
106 	if (!chname)
107 		return;
108 	channel = silc_client_get_channel(sg->client, sg->conn,
109 					  (char *)chname);
110 	if (!channel) {
111 		silc_client_get_channel_resolve(sg->client, sg->conn,
112 						(char *)chname,
113 						silcpurple_chat_getinfo_res,
114 						components);
115 		return;
116 	}
117 
118 	s = g_string_new("");
119 	tmp2 = g_markup_escape_text(channel->channel_name, -1);
120 	g_string_append_printf(s, _("<b>Channel Name:</b> %s"), tmp2);
121 	g_free(tmp2);
122 	if (channel->user_list && silc_hash_table_count(channel->user_list))
123 		g_string_append_printf(s, _("<br><b>User Count:</b> %d"),
124 				       (int)silc_hash_table_count(channel->user_list));
125 
126 	silc_hash_table_list(channel->user_list, &htl);
127 	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
128 		if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) {
129 			tmp2 = g_markup_escape_text(chu->client->nickname, -1);
130 			g_string_append_printf(s, _("<br><b>Channel Founder:</b> %s"),
131 					       tmp2);
132 			g_free(tmp2);
133 			break;
134 		}
135 	}
136 	silc_hash_table_list_reset(&htl);
137 
138 	if (channel->cipher)
139 		g_string_append_printf(s, _("<br><b>Channel Cipher:</b> %s"),
140 				       channel->cipher);
141 
142 	if (channel->hmac)
143 		/* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */
144 		g_string_append_printf(s, _("<br><b>Channel HMAC:</b> %s"),
145 				       channel->hmac);
146 
147 	if (channel->topic) {
148 		tmp2 = g_markup_escape_text(channel->topic, -1);
149 		g_string_append_printf(s, _("<br><b>Channel Topic:</b><br>%s"), tmp2);
150 		g_free(tmp2);
151 	}
152 
153 	if (channel->mode) {
154 		g_string_append(s, _("<br><b>Channel Modes:</b> "));
155 		silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
156 		g_string_append(s, tmp);
157 	}
158 
159 	if (channel->founder_key) {
160 		char *fingerprint, *babbleprint;
161 		unsigned char *pk;
162 		SilcUInt32 pk_len;
163 		pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len);
164 		if (pk) {
165 			fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
166 			babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
167 
168 			g_string_append_printf(s, _("<br><b>Founder Key Fingerprint:</b><br>%s"), fingerprint);
169 			g_string_append_printf(s, _("<br><b>Founder Key Babbleprint:</b><br>%s"), babbleprint);
170 
171 			silc_free(fingerprint);
172 			silc_free(babbleprint);
173 			silc_free(pk);
174 		}
175 	}
176 
177 	purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL);
178 	g_string_free(s, TRUE);
179 }
180 
181 
182 static void
silcpurple_chat_getinfo_menu(PurpleBlistNode * node,gpointer data)183 silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data)
184 {
185 	PurpleChat *chat = (PurpleChat *)node;
186 	PurpleAccount *account = purple_chat_get_account(chat);
187 	silcpurple_chat_getinfo(purple_account_get_connection(account),
188 			purple_chat_get_components(chat));
189 }
190 
191 
192 #if 0   /* XXX For now these are not implemented.  We need better
193 	   listview dialog from Purple for these. */
194 /************************** Channel Invite List ******************************/
195 
196 static void
197 silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data);
198 {
199 
200 }
201 
202 
203 /**************************** Channel Ban List *******************************/
204 
205 static void
206 silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data);
207 {
208 
209 }
210 #endif
211 
212 
213 /************************* Channel Authentication ****************************/
214 
215 typedef struct {
216 	SilcPurple sg;
217 	SilcChannelEntry channel;
218 	PurpleChat *c;
219 	SilcDList pubkeys;
220 } *SilcPurpleChauth;
221 
222 static void
silcpurple_chat_chpk_add(void * user_data,const char * name)223 silcpurple_chat_chpk_add(void *user_data, const char *name)
224 {
225 	SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
226 	SilcPurple sg = sgc->sg;
227 	SilcClient client = sg->client;
228 	SilcClientConnection conn = sg->conn;
229 	SilcPublicKey public_key;
230 	SilcBuffer chpks, pk, chidp;
231 	unsigned char mode[4];
232 	SilcUInt32 m;
233 
234 	/* Load the public key */
235 	if (!silc_pkcs_load_public_key(name, &public_key)) {
236 		silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
237 		silc_dlist_uninit(sgc->pubkeys);
238 		silc_free(sgc);
239 		purple_notify_error(client->application,
240 				    _("Add Channel Public Key"),
241 				    _("Could not load public key"), NULL);
242 		return;
243 	}
244 
245 	pk = silc_public_key_payload_encode(public_key);
246 	chpks = silc_buffer_alloc_size(2);
247 	SILC_PUT16_MSB(1, chpks->head);
248 	chpks = silc_argument_payload_encode_one(chpks, pk->data,
249 						 silc_buffer_len(pk), 0x00);
250 	silc_buffer_free(pk);
251 
252 	m = sgc->channel->mode;
253 	m |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
254 
255 	/* Send CMODE */
256 	SILC_PUT32_MSB(m, mode);
257 	chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
258 	silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
259 				 silcpurple_command_reply, NULL, 3,
260 				 1, chidp->data, silc_buffer_len(chidp),
261 				 2, mode, sizeof(mode),
262 				 9, chpks->data, silc_buffer_len(chpks));
263 	silc_buffer_free(chpks);
264 	silc_buffer_free(chidp);
265 	if (sgc->pubkeys) {
266 	  silc_dlist_start(sgc->pubkeys);
267 	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
268 		silc_pkcs_public_key_free(public_key);
269 	  silc_dlist_uninit(sgc->pubkeys);
270 	}
271 	silc_free(sgc);
272 }
273 
274 static void
silcpurple_chat_chpk_cancel(void * user_data,const char * name)275 silcpurple_chat_chpk_cancel(void *user_data, const char *name)
276 {
277 	SilcPurpleChauth sgc = (SilcPurpleChauth)user_data;
278 	SilcPublicKey public_key;
279 
280 	silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys);
281 
282 	if (sgc->pubkeys) {
283 	  silc_dlist_start(sgc->pubkeys);
284 	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
285 		silc_pkcs_public_key_free(public_key);
286 	  silc_dlist_uninit(sgc->pubkeys);
287 	}
288 	silc_free(sgc);
289 }
290 
291 static void
silcpurple_chat_chpk_cb(SilcPurpleChauth sgc,PurpleRequestFields * fields)292 silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields)
293 {
294 	SilcPurple sg = sgc->sg;
295 	SilcClient client = sg->client;
296 	SilcClientConnection conn = sg->conn;
297 	PurpleRequestField *f;
298 	GList *list;
299 	SilcPublicKey public_key;
300 	SilcBuffer chpks, pk, chidp;
301 	SilcUInt16 c = 0, ct;
302 	unsigned char mode[4];
303 	SilcUInt32 m;
304 
305 	f = purple_request_fields_get_field(fields, "list");
306 	if (!purple_request_field_list_get_selected(f)) {
307 		/* Add new public key */
308 		purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE,
309 				    G_CALLBACK(silcpurple_chat_chpk_add),
310 				    G_CALLBACK(silcpurple_chat_chpk_cancel),
311 				    purple_connection_get_account(sg->gc), NULL, NULL, sgc);
312 		return;
313 	}
314 
315 	list = purple_request_field_list_get_items(f);
316 	chpks = silc_buffer_alloc_size(2);
317 
318 	for (ct = 0; list; list = list->next, ct++) {
319 		public_key = purple_request_field_list_get_data(f, list->data);
320 		if (purple_request_field_list_is_selected(f, list->data)) {
321 			/* Delete this public key */
322 			pk = silc_public_key_payload_encode(public_key);
323 			chpks = silc_argument_payload_encode_one(chpks, pk->data,
324 								 silc_buffer_len(pk), 0x01);
325 			silc_buffer_free(pk);
326 			c++;
327 		}
328 	}
329 	if (!c) {
330 		silc_buffer_free(chpks);
331 		return;
332 	}
333 	SILC_PUT16_MSB(c, chpks->head);
334 
335 	m = sgc->channel->mode;
336 	if (ct == c)
337 		m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
338 
339 	/* Send CMODE */
340 	SILC_PUT32_MSB(m, mode);
341 	chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL);
342 	silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
343 				 silcpurple_command_reply, NULL, 3,
344 				 1, chidp->data, silc_buffer_len(chidp),
345 				 2, mode, sizeof(mode),
346 				 9, chpks->data, silc_buffer_len(chpks));
347 	silc_buffer_free(chpks);
348 	silc_buffer_free(chidp);
349 	if (sgc->pubkeys) {
350 	  silc_dlist_start(sgc->pubkeys);
351 	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
352 		silc_pkcs_public_key_free(public_key);
353 	  silc_dlist_uninit(sgc->pubkeys);
354 	}
355 	silc_free(sgc);
356 }
357 
358 static void
silcpurple_chat_chauth_ok(SilcPurpleChauth sgc,PurpleRequestFields * fields)359 silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields)
360 {
361 	SilcPurple sg = sgc->sg;
362 	PurpleRequestField *f;
363 	SilcPublicKey public_key;
364 	const char *curpass, *val;
365 	int set;
366 
367 	f = purple_request_fields_get_field(fields, "passphrase");
368 	val = purple_request_field_string_get_value(f);
369 	curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
370 
371 	if (!val && curpass)
372 		set = 0;
373 	else if (val && !curpass)
374 		set = 1;
375 	else if (val && curpass && !purple_strequal(val, curpass))
376 		set = 1;
377 	else
378 		set = -1;
379 
380 	if (set == 1) {
381 		silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
382 					 sgc->channel->channel_name, "+a", val, NULL);
383 		purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val);
384 	} else if (set == 0) {
385 		silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
386 					 sgc->channel->channel_name, "-a", NULL);
387 		purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase");
388 	}
389 
390 	if (sgc->pubkeys) {
391 	  silc_dlist_start(sgc->pubkeys);
392 	  while ((public_key = silc_dlist_get(sgc->pubkeys)))
393 		silc_pkcs_public_key_free(public_key);
394 	  silc_dlist_uninit(sgc->pubkeys);
395 	}
396 	silc_free(sgc);
397 }
398 
silcpurple_chat_chauth_show(SilcPurple sg,SilcChannelEntry channel,SilcDList channel_pubkeys)399 void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel,
400 				 SilcDList channel_pubkeys)
401 {
402 	SilcPublicKey public_key;
403 	SilcSILCPublicKey silc_pubkey;
404 	unsigned char *pk;
405 	SilcUInt32 pk_len;
406 	char *fingerprint, *babbleprint;
407 	SilcPublicKeyIdentifier ident;
408 	char tmp2[1024], t[512];
409 	PurpleRequestFields *fields;
410 	PurpleRequestFieldGroup *g;
411 	PurpleRequestField *f;
412 	SilcPurpleChauth sgc;
413 	const char *curpass = NULL;
414 
415 	sgc = silc_calloc(1, sizeof(*sgc));
416 	if (!sgc)
417 		return;
418 	sgc->sg = sg;
419 	sgc->channel = channel;
420 
421 	fields = purple_request_fields_new();
422 
423 	if (sgc->c)
424 	  curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase");
425 
426 	g = purple_request_field_group_new(NULL);
427 	f = purple_request_field_string_new("passphrase", _("Channel Passphrase"),
428 					    curpass, FALSE);
429 	purple_request_field_string_set_masked(f, TRUE);
430 	purple_request_field_group_add_field(g, f);
431 	purple_request_fields_add_group(fields, g);
432 
433 	g = purple_request_field_group_new(NULL);
434 	f = purple_request_field_label_new("l1", _("Channel Public Keys List"));
435 	purple_request_field_group_add_field(g, f);
436 	purple_request_fields_add_group(fields, g);
437 
438 	g_snprintf(t, sizeof(t),
439 		   _("Channel authentication is used to secure the channel from "
440 		     "unauthorized access. The authentication may be based on "
441 		     "passphrase and digital signatures. If passphrase is set, it "
442 		     "is required to be able to join. If channel public keys are set "
443 		     "then only users whose public keys are listed are able to join."));
444 
445 	if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) {
446 		f = purple_request_field_list_new("list", NULL);
447 		purple_request_field_group_add_field(g, f);
448 		purple_request_fields(sg->gc, _("Channel Authentication"),
449 				      _("Channel Authentication"), t, fields,
450 				      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
451 				      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
452 				      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
453 		if (channel_pubkeys)
454 		  silc_dlist_uninit(channel_pubkeys);
455 		return;
456 	}
457 	sgc->pubkeys = channel_pubkeys;
458 
459 	g = purple_request_field_group_new(NULL);
460 	f = purple_request_field_list_new("list", NULL);
461 	purple_request_field_group_add_field(g, f);
462 	purple_request_fields_add_group(fields, g);
463 
464 	silc_dlist_start(channel_pubkeys);
465 	while ((public_key = silc_dlist_get(channel_pubkeys))) {
466 		pk = silc_pkcs_public_key_encode(public_key, &pk_len);
467 		if (!pk)
468 			continue;
469 		fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4);
470 		babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4);
471 
472 		silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
473 		ident = &silc_pubkey->identifier;
474 
475 		g_snprintf(tmp2, sizeof(tmp2), "%s\n  %s\n  %s",
476 			   ident->realname ? ident->realname : ident->username ?
477 			   ident->username : "", fingerprint, babbleprint);
478 		purple_request_field_list_add_icon(f, tmp2, NULL, public_key);
479 
480 		silc_free(fingerprint);
481 		silc_free(babbleprint);
482 	}
483 
484 	purple_request_field_list_set_multi_select(f, FALSE);
485 	purple_request_fields(sg->gc, _("Channel Authentication"),
486 			      _("Channel Authentication"), t, fields,
487 			      _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb),
488 			      _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok),
489 			      purple_connection_get_account(sg->gc), NULL, NULL, sgc);
490 }
491 
492 static void
silcpurple_chat_chauth(PurpleBlistNode * node,gpointer data)493 silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data)
494 {
495 	PurpleChat *chat;
496 	PurpleConnection *gc;
497 	SilcPurple sg;
498 
499 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
500 
501 	chat = (PurpleChat *) node;
502 	gc = purple_account_get_connection(purple_chat_get_account(chat));
503 	sg = gc->proto_data;
504 
505 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
506 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
507 				 "+C", NULL);
508 }
509 
510 
511 /************************** Channel Private Groups **************************/
512 
513 /* Private groups are "virtual" channels.  They are groups inside a channel.
514    This is implemented by using channel private keys.  By knowing a channel
515    private key user becomes part of that group and is able to talk on that
516    group.  Other users, on the same channel, won't be able to see the
517    messages of that group.  It is possible to have multiple groups inside
518    a channel - and thus having multiple private keys on the channel. */
519 
520 typedef struct {
521 	SilcPurple sg;
522 	PurpleChat *c;
523 	const char *channel;
524 } *SilcPurpleCharPrv;
525 
526 static void
silcpurple_chat_prv_add(SilcPurpleCharPrv p,PurpleRequestFields * fields)527 silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields)
528 {
529 	SilcPurple sg = p->sg;
530 	char tmp[512];
531 	PurpleRequestField *f;
532 	const char *name, *passphrase, *alias;
533 	GHashTable *comp;
534 	PurpleGroup *g;
535 	PurpleChat *cn;
536 
537 	f = purple_request_fields_get_field(fields, "name");
538 	name = purple_request_field_string_get_value(f);
539 	if (!name) {
540 		silc_free(p);
541 		return;
542 	}
543 	f = purple_request_fields_get_field(fields, "passphrase");
544 	passphrase = purple_request_field_string_get_value(f);
545 	f = purple_request_fields_get_field(fields, "alias");
546 	alias = purple_request_field_string_get_value(f);
547 
548 	/* Add private group to buddy list */
549 	g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name);
550 	comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
551 	g_hash_table_replace(comp, "channel", g_strdup(tmp));
552 	g_hash_table_replace(comp, "passphrase", g_strdup(passphrase));
553 
554 	cn = purple_chat_new(sg->account, alias, comp);
555 	g = purple_chat_get_group(p->c);
556 	purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c);
557 
558 	/* Associate to a real channel */
559 	purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel);
560 
561 	/* Join the group */
562 	silcpurple_chat_join(sg->gc, comp);
563 
564 	silc_free(p);
565 }
566 
567 static void
silcpurple_chat_prv_cancel(SilcPurpleCharPrv p,PurpleRequestFields * fields)568 silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields)
569 {
570 	silc_free(p);
571 }
572 
573 static void
silcpurple_chat_prv(PurpleBlistNode * node,gpointer data)574 silcpurple_chat_prv(PurpleBlistNode *node, gpointer data)
575 {
576 	PurpleChat *chat;
577 	PurpleConnection *gc;
578 	SilcPurple sg;
579 
580 	SilcPurpleCharPrv p;
581 	PurpleRequestFields *fields;
582 	PurpleRequestFieldGroup *g;
583 	PurpleRequestField *f;
584 	char tmp[512];
585 
586 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
587 
588 	chat = (PurpleChat *) node;
589 	gc = purple_account_get_connection(purple_chat_get_account(chat));
590 	sg = gc->proto_data;
591 
592 	p = silc_calloc(1, sizeof(*p));
593 	if (!p)
594 		return;
595 	p->sg = sg;
596 
597 	p->channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
598 	p->c = purple_blist_find_chat(sg->account, p->channel);
599 
600 	fields = purple_request_fields_new();
601 
602 	g = purple_request_field_group_new(NULL);
603 	f = purple_request_field_string_new("name", _("Group Name"),
604 					  NULL, FALSE);
605 	purple_request_field_group_add_field(g, f);
606 
607 	f = purple_request_field_string_new("passphrase", _("Passphrase"),
608 					  NULL, FALSE);
609 	purple_request_field_string_set_masked(f, TRUE);
610 	purple_request_field_group_add_field(g, f);
611 
612 	f = purple_request_field_string_new("alias", _("Alias"),
613 					  NULL, FALSE);
614 	purple_request_field_group_add_field(g, f);
615 	purple_request_fields_add_group(fields, g);
616 
617 	g_snprintf(tmp, sizeof(tmp),
618 		   _("Please enter the %s channel private group name and passphrase."),
619 		   p->channel);
620 	purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields,
621 			      _("Add"), G_CALLBACK(silcpurple_chat_prv_add),
622 			      _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel),
623 			      purple_connection_get_account(gc), NULL, NULL, p);
624 }
625 
626 
627 /****************************** Channel Modes ********************************/
628 
629 static void
silcpurple_chat_permanent_reset(PurpleBlistNode * node,gpointer data)630 silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data)
631 {
632 	PurpleChat *chat;
633 	PurpleConnection *gc;
634 	SilcPurple sg;
635 
636 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
637 
638 	chat = (PurpleChat *) node;
639 	gc = purple_account_get_connection(purple_chat_get_account(chat));
640 	sg = gc->proto_data;
641 
642 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
643 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
644 				 "-f", NULL);
645 }
646 
647 static void
silcpurple_chat_permanent(PurpleBlistNode * node,gpointer data)648 silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data)
649 {
650 	PurpleChat *chat;
651 	PurpleConnection *gc;
652 	SilcPurple sg;
653 	const char *channel;
654 
655 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
656 
657 	chat = (PurpleChat *) node;
658 	gc = purple_account_get_connection(purple_chat_get_account(chat));
659 	sg = gc->proto_data;
660 
661 	if (!sg->conn)
662 		return;
663 
664 	/* XXX we should have ability to define which founder
665 	   key to use.  Now we use the user's own public key
666 	   (default key). */
667 
668 	/* Call CMODE */
669 	channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel");
670 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel,
671 				 "+f", NULL);
672 }
673 
674 typedef struct {
675 	SilcPurple sg;
676 	char *channel;
677 } *SilcPurpleChatInput;
678 
679 static void
silcpurple_chat_ulimit_cb(SilcPurpleChatInput s,const char * limit)680 silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit)
681 {
682 	SilcChannelEntry channel;
683 	guint ulimit = 0;
684 
685 	channel = silc_client_get_channel(s->sg->client, s->sg->conn,
686 					  (char *)s->channel);
687 	if (!channel)
688 		return;
689 	if (limit)
690 		ulimit = strtoul(limit, NULL, 10);
691 
692 	if (!limit || !(*limit) || *limit == '0') {
693 		if (limit && ulimit == channel->user_limit) {
694 			g_free(s->channel);
695 			silc_free(s);
696 			return;
697 		}
698 		silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
699 					 s->channel, "-l", NULL);
700 
701 		g_free(s->channel);
702 		silc_free(s);
703 		return;
704 	}
705 
706 	if (ulimit == channel->user_limit) {
707 		g_free(s->channel);
708 		silc_free(s);
709 		return;
710 	}
711 
712 	/* Call CMODE */
713 	silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE",
714 				 s->channel, "+l", limit, NULL);
715 
716 	g_free(s->channel);
717 	silc_free(s);
718 }
719 
720 static void
silcpurple_chat_ulimit(PurpleBlistNode * node,gpointer data)721 silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data)
722 {
723 	PurpleChat *chat;
724 	PurpleConnection *gc;
725 	SilcPurple sg;
726 
727 	SilcPurpleChatInput s;
728 	SilcChannelEntry channel;
729 	char *ch;
730 	char tmp[32];
731 
732 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
733 
734 	chat = (PurpleChat *) node;
735 	gc = purple_account_get_connection(purple_chat_get_account(chat));
736 	sg = gc->proto_data;
737 
738 	if (!sg->conn)
739 		return;
740 
741 	ch = g_strdup(g_hash_table_lookup(purple_chat_get_components(chat), "channel"));
742 	channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch);
743 	if (!channel)
744 		return;
745 
746 	s = silc_calloc(1, sizeof(*s));
747 	if (!s)
748 		return;
749 	s->channel = ch;
750 	s->sg = sg;
751 	g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit);
752 	purple_request_input(gc, _("User Limit"), NULL,
753 			   _("Set user limit on channel. Set to zero to reset user limit."),
754 			   tmp, FALSE, FALSE, NULL,
755 			   _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb),
756 			   _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb),
757 			   purple_connection_get_account(gc), NULL, NULL, s);
758 }
759 
760 static void
silcpurple_chat_resettopic(PurpleBlistNode * node,gpointer data)761 silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data)
762 {
763 	PurpleChat *chat;
764 	PurpleConnection *gc;
765 	SilcPurple sg;
766 
767 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
768 
769 	chat = (PurpleChat *) node;
770 	gc = purple_account_get_connection(purple_chat_get_account(chat));
771 	sg = gc->proto_data;
772 
773 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
774 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
775 				 "-t", NULL);
776 }
777 
778 static void
silcpurple_chat_settopic(PurpleBlistNode * node,gpointer data)779 silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data)
780 {
781 	PurpleChat *chat;
782 	PurpleConnection *gc;
783 	SilcPurple sg;
784 
785 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
786 
787 	chat = (PurpleChat *) node;
788 	gc = purple_account_get_connection(purple_chat_get_account(chat));
789 	sg = gc->proto_data;
790 
791 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
792 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
793 				 "+t", NULL);
794 }
795 
796 static void
silcpurple_chat_resetprivate(PurpleBlistNode * node,gpointer data)797 silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data)
798 {
799 	PurpleChat *chat;
800 	PurpleConnection *gc;
801 	SilcPurple sg;
802 
803 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
804 
805 	chat = (PurpleChat *) node;
806 	gc = purple_account_get_connection(purple_chat_get_account(chat));
807 	sg = gc->proto_data;
808 
809 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
810 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
811 				 "-p", NULL);
812 }
813 
814 static void
silcpurple_chat_setprivate(PurpleBlistNode * node,gpointer data)815 silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data)
816 {
817 	PurpleChat *chat;
818 	PurpleConnection *gc;
819 	SilcPurple sg;
820 
821 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
822 
823 	chat = (PurpleChat *) node;
824 	gc = purple_account_get_connection(purple_chat_get_account(chat));
825 	sg = gc->proto_data;
826 
827 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
828 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
829 				 "+p", NULL);
830 }
831 
832 static void
silcpurple_chat_resetsecret(PurpleBlistNode * node,gpointer data)833 silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data)
834 {
835 	PurpleChat *chat;
836 	PurpleConnection *gc;
837 	SilcPurple sg;
838 
839 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
840 
841 	chat = (PurpleChat *) node;
842 	gc = purple_account_get_connection(purple_chat_get_account(chat));
843 	sg = gc->proto_data;
844 
845 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
846 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
847 				 "-s", NULL);
848 }
849 
850 static void
silcpurple_chat_setsecret(PurpleBlistNode * node,gpointer data)851 silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data)
852 {
853 	PurpleChat *chat;
854 	PurpleConnection *gc;
855 	SilcPurple sg;
856 
857 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
858 
859 	chat = (PurpleChat *) node;
860 	gc = purple_account_get_connection(purple_chat_get_account(chat));
861 	sg = gc->proto_data;
862 
863 	silc_client_command_call(sg->client, sg->conn, NULL, "CMODE",
864 				 g_hash_table_lookup(purple_chat_get_components(chat), "channel"),
865 				 "+s", NULL);
866 }
867 
868 typedef struct {
869 	SilcPurple sg;
870 	SilcChannelEntry channel;
871 } *SilcPurpleChatWb;
872 
873 static void
silcpurple_chat_wb(PurpleBlistNode * node,gpointer data)874 silcpurple_chat_wb(PurpleBlistNode *node, gpointer data)
875 {
876 	SilcPurpleChatWb wb = data;
877 	silcpurple_wb_init_ch(wb->sg, wb->channel);
878 	silc_free(wb);
879 }
880 
silcpurple_chat_menu(PurpleChat * chat)881 GList *silcpurple_chat_menu(PurpleChat *chat)
882 {
883 	GHashTable *components = purple_chat_get_components(chat);
884 	PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat));
885 	SilcPurple sg = gc->proto_data;
886 	SilcClientConnection conn = sg->conn;
887 	const char *chname = NULL;
888 	SilcChannelEntry channel = NULL;
889 	SilcChannelUser chu = NULL;
890 	SilcUInt32 mode = 0;
891 
892 	GList *m = NULL;
893 	PurpleMenuAction *act;
894 
895 	if (components)
896 		chname = g_hash_table_lookup(components, "channel");
897 	if (!chname)
898 		return NULL;
899 	channel = silc_client_get_channel(sg->client, sg->conn,
900 					  (char *)chname);
901 	if (channel) {
902 		chu = silc_client_on_channel(channel, conn->local_entry);
903 		if (chu)
904 			mode = chu->mode;
905 	}
906 
907 	if (strstr(chname, "[Private Group]"))
908 		return NULL;
909 
910 	act = purple_menu_action_new(_("Get Info"),
911 	                           PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
912 	                           NULL, NULL);
913 	m = g_list_append(m, act);
914 
915 #if 0   /* XXX For now these are not implemented.  We need better
916 	   listview dialog from Purple for these. */
917 	if (mode & SILC_CHANNEL_UMODE_CHANOP) {
918 		act = purple_menu_action_new(_("Invite List"),
919 		                           PURPLE_CALLBACK(silcpurple_chat_invitelist),
920 		                           NULL, NULL);
921 		m = g_list_append(m, act);
922 
923 		act = purple_menu_action_new(_("Ban List"),
924 		                           PURPLE_CALLBACK(silcpurple_chat_banlist),
925 		                           NULL, NULL);
926 		m = g_list_append(m, act);
927 	}
928 #endif
929 
930 	if (chu) {
931 		act = purple_menu_action_new(_("Add Private Group"),
932 		                           PURPLE_CALLBACK(silcpurple_chat_prv),
933 		                           NULL, NULL);
934 		m = g_list_append(m, act);
935 	}
936 
937 	if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
938 		act = purple_menu_action_new(_("Channel Authentication"),
939 		                           PURPLE_CALLBACK(silcpurple_chat_chauth),
940 		                           NULL, NULL);
941 		m = g_list_append(m, act);
942 
943 		if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
944 			act = purple_menu_action_new(_("Reset Permanent"),
945 			                           PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
946 			                           NULL, NULL);
947 			m = g_list_append(m, act);
948 		} else {
949 			act = purple_menu_action_new(_("Set Permanent"),
950 			                           PURPLE_CALLBACK(silcpurple_chat_permanent),
951 			                           NULL, NULL);
952 			m = g_list_append(m, act);
953 		}
954 	}
955 
956 	if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
957 		act = purple_menu_action_new(_("Set User Limit"),
958 		                           PURPLE_CALLBACK(silcpurple_chat_ulimit),
959 		                           NULL, NULL);
960 		m = g_list_append(m, act);
961 
962 		if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
963 			act = purple_menu_action_new(_("Reset Topic Restriction"),
964 			                           PURPLE_CALLBACK(silcpurple_chat_resettopic),
965 			                           NULL, NULL);
966 			m = g_list_append(m, act);
967 		} else {
968 			act = purple_menu_action_new(_("Set Topic Restriction"),
969 			                           PURPLE_CALLBACK(silcpurple_chat_settopic),
970 			                           NULL, NULL);
971 			m = g_list_append(m, act);
972 		}
973 
974 		if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
975 			act = purple_menu_action_new(_("Reset Private Channel"),
976 			                           PURPLE_CALLBACK(silcpurple_chat_resetprivate),
977 			                           NULL, NULL);
978 			m = g_list_append(m, act);
979 		} else {
980 			act = purple_menu_action_new(_("Set Private Channel"),
981 			                           PURPLE_CALLBACK(silcpurple_chat_setprivate),
982 			                           NULL, NULL);
983 			m = g_list_append(m, act);
984 		}
985 
986 		if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
987 			act = purple_menu_action_new(_("Reset Secret Channel"),
988 			                           PURPLE_CALLBACK(silcpurple_chat_resetsecret),
989 			                           NULL, NULL);
990 			m = g_list_append(m, act);
991 		} else {
992 			act = purple_menu_action_new(_("Set Secret Channel"),
993 			                           PURPLE_CALLBACK(silcpurple_chat_setsecret),
994 			                           NULL, NULL);
995 			m = g_list_append(m, act);
996 		}
997 	}
998 
999 	if (chu && channel) {
1000 		SilcPurpleChatWb wb;
1001 		wb = silc_calloc(1, sizeof(*wb));
1002 		wb->sg = sg;
1003 		wb->channel = channel;
1004 		act = purple_menu_action_new(_("Draw On Whiteboard"),
1005 		                           PURPLE_CALLBACK(silcpurple_chat_wb),
1006 		                           (void *)wb, NULL);
1007 		m = g_list_append(m, act);
1008 	}
1009 
1010 	return m;
1011 }
1012 
1013 
1014 /******************************* Joining Etc. ********************************/
1015 
silcpurple_get_chat_name(GHashTable * data)1016 char *silcpurple_get_chat_name(GHashTable *data)
1017 {
1018 	return g_strdup(g_hash_table_lookup(data, "channel"));
1019 }
1020 
silcpurple_chat_join(PurpleConnection * gc,GHashTable * data)1021 void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data)
1022 {
1023 	SilcPurple sg = gc->proto_data;
1024 	SilcClient client = sg->client;
1025 	SilcClientConnection conn = sg->conn;
1026 	const char *channel, *passphrase, *parentch;
1027 
1028 	if (!conn)
1029 		return;
1030 
1031 	channel = g_hash_table_lookup(data, "channel");
1032 	passphrase = g_hash_table_lookup(data, "passphrase");
1033 
1034 	/* Check if we are joining a private group.  Handle it
1035 	   purely locally as it's not a real channel */
1036 	if (strstr(channel, "[Private Group]")) {
1037 		SilcChannelEntry channel_entry;
1038 		SilcChannelPrivateKey key;
1039 		PurpleChat *c;
1040 		SilcPurplePrvgrp grp;
1041 
1042 		c = purple_blist_find_chat(sg->account, channel);
1043 		parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch");
1044 		if (!parentch)
1045 			return;
1046 
1047 		channel_entry = silc_client_get_channel(sg->client, sg->conn,
1048 							(char *)parentch);
1049 		if (!channel_entry ||
1050 		    !silc_client_on_channel(channel_entry, sg->conn->local_entry)) {
1051 			char tmp[512];
1052 			g_snprintf(tmp, sizeof(tmp),
1053 				   _("You have to join the %s channel before you are "
1054 				     "able to join the private group"), parentch);
1055 			purple_notify_error(gc, _("Join Private Group"),
1056 					  _("Cannot join private group"), tmp);
1057 			return;
1058 		}
1059 
1060 		/* Add channel private key */
1061 		if (!silc_client_add_channel_private_key(client, conn,
1062 							 channel_entry, channel,
1063 							 NULL, NULL,
1064 							 (unsigned char *)passphrase,
1065 							 strlen(passphrase), &key))
1066 			return;
1067 
1068 		/* Join the group */
1069 		grp = silc_calloc(1, sizeof(*grp));
1070 		if (!grp)
1071 			return;
1072 		grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP;
1073 		grp->chid = SILC_PTR_TO_32(channel_entry->context);
1074 		grp->parentch = parentch;
1075 		grp->channel = channel;
1076 		grp->key = key;
1077 		sg->grps = g_list_append(sg->grps, grp);
1078 		serv_got_joined_chat(gc, grp->id, channel);
1079 		return;
1080 	}
1081 
1082 	/* XXX We should have other properties here as well:
1083 	   1. whether to try to authenticate to the channel
1084 	     1a. with default key,
1085 	     1b. with specific key.
1086 	   2. whether to try to authenticate to become founder.
1087 	     2a. with default key,
1088 	     2b. with specific key.
1089 
1090 	   Since now such variety is not possible in the join dialog
1091 	   we always use -founder and -auth options, which try to
1092 	   do both 1 and 2 with default keys. */
1093 
1094 	/* Call JOIN */
1095 	if ((passphrase != NULL) && (*passphrase != '\0'))
1096 		silc_client_command_call(client, conn, NULL, "JOIN",
1097 					 channel, passphrase, "-auth", "-founder", NULL);
1098 	else
1099 		silc_client_command_call(client, conn, NULL, "JOIN",
1100 					 channel, "-auth", "-founder", NULL);
1101 }
1102 
silcpurple_chat_invite(PurpleConnection * gc,int id,const char * msg,const char * name)1103 void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg,
1104 			    const char *name)
1105 {
1106 	SilcPurple sg = gc->proto_data;
1107 	SilcClient client = sg->client;
1108 	SilcClientConnection conn = sg->conn;
1109 	SilcHashTableList htl;
1110 	SilcChannelUser chu;
1111 	gboolean found = FALSE;
1112 
1113 	if (!conn)
1114 		return;
1115 
1116 	/* See if we are inviting on a private group.  Invite
1117 	   to the actual channel */
1118 	if (id > SILCPURPLE_PRVGRP) {
1119 		GList *l;
1120 		SilcPurplePrvgrp prv;
1121 
1122 		for (l = sg->grps; l; l = l->next)
1123 			if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1124 				break;
1125 		if (!l)
1126 			return;
1127 		prv = l->data;
1128 		id = prv->chid;
1129 	}
1130 
1131 	/* Find channel by id */
1132 	silc_hash_table_list(conn->local_entry->channels, &htl);
1133 	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1134 		if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1135 			found = TRUE;
1136 			break;
1137 		}
1138 	}
1139 	silc_hash_table_list_reset(&htl);
1140 	if (!found)
1141 		return;
1142 
1143 	/* Call INVITE */
1144 	silc_client_command_call(client, conn, NULL, "INVITE",
1145 				 chu->channel->channel_name,
1146 				 name, NULL);
1147 }
1148 
silcpurple_chat_leave(PurpleConnection * gc,int id)1149 void silcpurple_chat_leave(PurpleConnection *gc, int id)
1150 {
1151 	SilcPurple sg = gc->proto_data;
1152 	SilcClient client = sg->client;
1153 	SilcClientConnection conn = sg->conn;
1154 	SilcHashTableList htl;
1155 	SilcChannelUser chu;
1156 	gboolean found = FALSE;
1157 	GList *l;
1158 	SilcPurplePrvgrp prv;
1159 
1160 	if (!conn)
1161 		return;
1162 
1163 	/* See if we are leaving a private group */
1164 	if (id > SILCPURPLE_PRVGRP) {
1165 		SilcChannelEntry channel;
1166 
1167 		for (l = sg->grps; l; l = l->next)
1168 			if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1169 				break;
1170 		if (!l)
1171 			return;
1172 		prv = l->data;
1173 		channel = silc_client_get_channel(sg->client, sg->conn,
1174 						  (char *)prv->parentch);
1175 		if (!channel)
1176 			return;
1177 		silc_client_del_channel_private_key(client, conn,
1178 						    channel, prv->key);
1179 		silc_free(prv);
1180 		sg->grps = g_list_remove(sg->grps, prv);
1181 		serv_got_chat_left(gc, id);
1182 		return;
1183 	}
1184 
1185 	/* Find channel by id */
1186 	silc_hash_table_list(conn->local_entry->channels, &htl);
1187 	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1188 		if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1189 			found = TRUE;
1190 			break;
1191 		}
1192 	}
1193 	silc_hash_table_list_reset(&htl);
1194 	if (!found)
1195 		return;
1196 
1197 	/* Call LEAVE */
1198 	silc_client_command_call(client, conn, NULL, "LEAVE",
1199 				 chu->channel->channel_name, NULL);
1200 
1201 	serv_got_chat_left(gc, id);
1202 
1203 	/* Leave from private groups on this channel as well */
1204 	for (l = sg->grps; l; l = l->next)
1205 		if (((SilcPurplePrvgrp)l->data)->chid == (gulong)id) {
1206 			prv = l->data;
1207 			silc_client_del_channel_private_key(client, conn,
1208 							    chu->channel,
1209 							    prv->key);
1210 			serv_got_chat_left(gc, prv->id);
1211 			silc_free(prv);
1212 			sg->grps = g_list_remove(sg->grps, prv);
1213 			if (!sg->grps)
1214 				break;
1215 		}
1216 }
1217 
silcpurple_chat_send(PurpleConnection * gc,int id,const char * msg,PurpleMessageFlags msgflags)1218 int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg,
1219 			 PurpleMessageFlags msgflags)
1220 {
1221 	SilcPurple sg = gc->proto_data;
1222 	SilcClient client = sg->client;
1223 	SilcClientConnection conn = sg->conn;
1224 	SilcHashTableList htl;
1225 	SilcChannelUser chu;
1226 	SilcChannelEntry channel = NULL;
1227 	SilcChannelPrivateKey key = NULL;
1228 	SilcMessageFlags flags;
1229 	int ret = 0;
1230 	char *msg2, *tmp;
1231 	gboolean found = FALSE;
1232 	gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1233 	SilcDList list;
1234 
1235 	if (!msg || !conn)
1236 		return 0;
1237 
1238 	flags = SILC_MESSAGE_FLAG_UTF8;
1239 
1240 	tmp = msg2 = purple_unescape_html(msg);
1241 
1242 	if (!g_ascii_strncasecmp(msg2, "/me ", 4))
1243 	{
1244 		msg2 += 4;
1245 		if (!*msg2) {
1246 			g_free(tmp);
1247 			return 0;
1248 		}
1249 		flags |= SILC_MESSAGE_FLAG_ACTION;
1250 	} else if (strlen(msg) > 1 && msg[0] == '/') {
1251 		if (!silc_client_command_call(client, conn, msg + 1))
1252 			purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
1253 					    _("Unknown command"));
1254 		g_free(tmp);
1255 		return 0;
1256 	}
1257 
1258 
1259 	if (sign)
1260 		flags |= SILC_MESSAGE_FLAG_SIGNED;
1261 
1262 	/* Get the channel private key if we are sending on
1263 	   private group */
1264 	if (id > SILCPURPLE_PRVGRP) {
1265 		GList *l;
1266 		SilcPurplePrvgrp prv;
1267 
1268 		for (l = sg->grps; l; l = l->next)
1269 			if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1270 				break;
1271 		if (!l) {
1272 			g_free(tmp);
1273 			return 0;
1274 		}
1275 		prv = l->data;
1276 		channel = silc_client_get_channel(sg->client, sg->conn,
1277 						  (char *)prv->parentch);
1278 		if (!channel) {
1279 			g_free(tmp);
1280 			return 0;
1281 		}
1282 		key = prv->key;
1283 	}
1284 
1285 	if (!channel) {
1286 		/* Find channel by id */
1287 		silc_hash_table_list(conn->local_entry->channels, &htl);
1288 		while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1289 			if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1290 				found = TRUE;
1291 				break;
1292 			}
1293 		}
1294 		silc_hash_table_list_reset(&htl);
1295 		if (!found) {
1296 			g_free(tmp);
1297 			return 0;
1298 		}
1299 		channel = chu->channel;
1300 	}
1301 
1302 	/* Check for images */
1303 	if (msgflags & PURPLE_MESSAGE_IMAGES) {
1304 		list = silcpurple_image_message(msg, &flags);
1305 		if (list) {
1306 			/* Send one or more MIME message.  If more than one, they
1307 			   are MIME fragments due to over large message */
1308 			SilcBuffer buf;
1309 
1310 			silc_dlist_start(list);
1311 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1312 				ret =
1313 			 	silc_client_send_channel_message(client, conn,
1314 								 channel, key,
1315 								 flags, sg->sha1hash,
1316 								 buf->data,
1317 								 silc_buffer_len(buf));
1318 			silc_mime_partial_free(list);
1319 			g_free(tmp);
1320 
1321 			if (ret)
1322 				  serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL));
1323 			return ret;
1324 		}
1325 	}
1326 
1327 	/* Send channel message */
1328 	ret = silc_client_send_channel_message(client, conn, channel, key,
1329 					       flags, sg->sha1hash,
1330 					       (unsigned char *)msg2,
1331 					       strlen(msg2));
1332 	if (ret) {
1333 		serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg,
1334 				 time(NULL));
1335 	}
1336 	g_free(tmp);
1337 
1338 	return ret;
1339 }
1340 
silcpurple_chat_set_topic(PurpleConnection * gc,int id,const char * topic)1341 void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
1342 {
1343 	SilcPurple sg = gc->proto_data;
1344 	SilcClient client = sg->client;
1345 	SilcClientConnection conn = sg->conn;
1346 	SilcHashTableList htl;
1347 	SilcChannelUser chu;
1348 	gboolean found = FALSE;
1349 
1350 	if (!conn)
1351 		return;
1352 
1353 	/* See if setting topic on private group.  Set it
1354 	   on the actual channel */
1355 	if (id > SILCPURPLE_PRVGRP) {
1356 		GList *l;
1357 		SilcPurplePrvgrp prv;
1358 
1359 		for (l = sg->grps; l; l = l->next)
1360 			if (((SilcPurplePrvgrp)l->data)->id == (gulong)id)
1361 				break;
1362 		if (!l)
1363 			return;
1364 		prv = l->data;
1365 		id = prv->chid;
1366 	}
1367 
1368 	/* Find channel by id */
1369 	silc_hash_table_list(conn->local_entry->channels, &htl);
1370 	while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1371 		if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) {
1372 			found = TRUE;
1373 			break;
1374 		}
1375 	}
1376 	silc_hash_table_list_reset(&htl);
1377 	if (!found)
1378 		return;
1379 
1380 	/* Call TOPIC */
1381 	silc_client_command_call(client, conn, NULL, "TOPIC",
1382 				 chu->channel->channel_name, topic, NULL);
1383 }
1384 
silcpurple_roomlist_get_list(PurpleConnection * gc)1385 PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc)
1386 {
1387 	SilcPurple sg = gc->proto_data;
1388 	SilcClient client = sg->client;
1389 	SilcClientConnection conn = sg->conn;
1390 	GList *fields = NULL;
1391 	PurpleRoomlistField *f;
1392 
1393 	if (!conn)
1394 		return NULL;
1395 
1396 	if (sg->roomlist)
1397 		purple_roomlist_unref(sg->roomlist);
1398 
1399 	sg->roomlist_cancelled = FALSE;
1400 
1401 	sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
1402 	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
1403 	fields = g_list_append(fields, f);
1404 	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT,
1405 				    _("Users"), "users", FALSE);
1406 	fields = g_list_append(fields, f);
1407 	f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
1408 				    _("Topic"), "topic", FALSE);
1409 	fields = g_list_append(fields, f);
1410 	purple_roomlist_set_fields(sg->roomlist, fields);
1411 
1412 	/* Call LIST */
1413 	silc_client_command_call(client, conn, "LIST");
1414 
1415 	purple_roomlist_set_in_progress(sg->roomlist, TRUE);
1416 
1417 	return sg->roomlist;
1418 }
1419 
silcpurple_roomlist_cancel(PurpleRoomlist * list)1420 void silcpurple_roomlist_cancel(PurpleRoomlist *list)
1421 {
1422 	PurpleConnection *gc = purple_account_get_connection(list->account);
1423 	SilcPurple sg;
1424 
1425 	if (!gc)
1426 		return;
1427 	sg = gc->proto_data;
1428 
1429 	purple_roomlist_set_in_progress(list, FALSE);
1430 	if (sg->roomlist == list) {
1431 		purple_roomlist_unref(sg->roomlist);
1432 		sg->roomlist = NULL;
1433 		sg->roomlist_cancelled = TRUE;
1434 	}
1435 }
1436