1 /*
2 
3   silcpurple_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 "silcpurple.h"
23 #include "imgstore.h"
24 #include "wb.h"
25 
26 #include "glibcompat.h"
27 
28 static void
29 silc_channel_message(SilcClient client, SilcClientConnection conn,
30 		     SilcClientEntry sender, SilcChannelEntry channel,
31 		     SilcMessagePayload payload, SilcChannelPrivateKey key,
32 		     SilcMessageFlags flags, const unsigned char *message,
33 		     SilcUInt32 message_len);
34 static void
35 silc_private_message(SilcClient client, SilcClientConnection conn,
36 		     SilcClientEntry sender, SilcMessagePayload payload,
37 		     SilcMessageFlags flags, const unsigned char *message,
38 		     SilcUInt32 message_len);
39 
40 /* Message sent to the application by library. `conn' associates the
41    message to a specific connection.  `conn', however, may be NULL.
42    The `type' indicates the type of the message sent by the library.
43    The application can for example filter the message according the
44    type. */
45 
46 static void
silc_say(SilcClient client,SilcClientConnection conn,SilcClientMessageType type,char * msg,...)47 silc_say(SilcClient client, SilcClientConnection conn,
48 	 SilcClientMessageType type, char *msg, ...)
49 {
50 	/* Nothing */
51 }
52 
53 #ifdef HAVE_SILCMIME_H
54 /* Processes incoming MIME message.  Can be private message or channel
55    message. */
56 
57 static void
silcpurple_mime_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcChannelEntry channel,SilcMessagePayload payload,SilcChannelPrivateKey key,SilcMessageFlags flags,SilcMime mime,gboolean recursive)58 silcpurple_mime_message(SilcClient client, SilcClientConnection conn,
59 		      SilcClientEntry sender, SilcChannelEntry channel,
60 		      SilcMessagePayload payload, SilcChannelPrivateKey key,
61 		      SilcMessageFlags flags, SilcMime mime,
62 		      gboolean recursive)
63 {
64 	PurpleConnection *gc = client->application;
65 	SilcPurple sg = gc->proto_data;
66 	const char *type;
67 	const unsigned char *data;
68 	SilcUInt32 data_len;
69 	PurpleMessageFlags cflags = 0;
70 	PurpleConversation *convo = NULL;
71 
72 	if (!mime)
73 		return;
74 
75 	/* Check for fragmented MIME message */
76 	if (silc_mime_is_partial(mime)) {
77 		if (!sg->mimeass)
78 			sg->mimeass = silc_mime_assembler_alloc();
79 
80 		/* Defragment */
81 		mime = silc_mime_assemble(sg->mimeass, mime);
82 		if (!mime)
83 			/* More fragments to come */
84 			return;
85 
86 		/* Process the complete message */
87 		silcpurple_mime_message(client, conn, sender, channel,
88 				      payload, key, flags, mime, FALSE);
89 		return;
90 	}
91 
92 	/* Check for multipart message */
93 	if (silc_mime_is_multipart(mime)) {
94 		SilcMime p;
95 		const char *mtype;
96 		SilcDList parts = silc_mime_get_multiparts(mime, &mtype);
97 
98 		/* Only "mixed" type supported */
99 		if (!purple_strequal(mtype, "mixed"))
100 			goto out;
101 
102 		silc_dlist_start(parts);
103 		while ((p = silc_dlist_get(parts)) != SILC_LIST_END) {
104 			/* Recursively process parts */
105 			silcpurple_mime_message(client, conn, sender, channel,
106 					      payload, key, flags, p, TRUE);
107 		}
108 		goto out;
109 	}
110 
111 	/* Get content type and MIME data */
112 	type = silc_mime_get_field(mime, "Content-Type");
113 	if (!type)
114 		goto out;
115 	data = silc_mime_get_data(mime, &data_len);
116 	if (!data)
117 		goto out;
118 
119 	/* Process according to content type */
120 
121 	/* Plain text */
122 	if (strstr(type, "text/plain")) {
123 		/* Default is UTF-8, don't check for other charsets */
124 		if (!strstr(type, "utf-8"))
125 			goto out;
126 
127 		if (channel)
128 			silc_channel_message(client, conn, sender, channel,
129 					     payload, key,
130 					     SILC_MESSAGE_FLAG_UTF8, data,
131 					     data_len);
132 		else
133 			silc_private_message(client, conn, sender, payload,
134 					     SILC_MESSAGE_FLAG_UTF8, data,
135 					     data_len);
136 		goto out;
137 	}
138 
139 	/* Image */
140 	if (strstr(type, "image/png") ||
141 	    strstr(type, "image/jpeg") ||
142 	    strstr(type, "image/gif") ||
143 	    strstr(type, "image/tiff")) {
144 		char tmp[32];
145 		int imgid;
146 
147 		/* Get channel convo (if message is for channel) */
148 		if (key && channel) {
149 			GList *l;
150 			SilcPurplePrvgrp prv;
151 
152 			for (l = sg->grps; l; l = l->next)
153 				if (((SilcPurplePrvgrp)l->data)->key == key) {
154 					prv = l->data;
155 					convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
156 							prv->channel, sg->account);
157 					break;
158 				}
159 		}
160 		if (channel && !convo)
161 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
162 								    channel->channel_name, sg->account);
163 		if (channel && !convo)
164 			goto out;
165 
166 		imgid = purple_imgstore_add_with_id(g_memdup2(data, data_len), data_len, "");
167 		if (imgid) {
168 			cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
169 			g_snprintf(tmp, sizeof(tmp), "<IMG ID=\"%d\">", imgid);
170 
171 			if (channel)
172 				serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
173 				 		 sender->nickname ?
174 				 		  sender->nickname :
175 						 "<unknown>", cflags,
176 						 tmp, time(NULL));
177 			else
178 				serv_got_im(gc, sender->nickname ?
179 					    sender->nickname : "<unknown>",
180 					    tmp, cflags, time(NULL));
181 
182 			purple_imgstore_unref_by_id(imgid);
183 			cflags = 0;
184 		}
185 		goto out;
186 	}
187 
188 	/* Whiteboard message */
189 	if (strstr(type, "application/x-wb") &&
190 	    !purple_account_get_bool(sg->account, "block-wb", FALSE)) {
191 		if (channel)
192 			silcpurple_wb_receive_ch(client, conn, sender, channel,
193 					       payload, flags, data, data_len);
194 		else
195 			silcpurple_wb_receive(client, conn, sender, payload,
196 					    flags, data, data_len);
197 		goto out;
198 	}
199 
200  out:
201 	if (!recursive)
202 		silc_mime_free(mime);
203 }
204 #endif /* HAVE_SILCMIME_H */
205 
206 /* Message for a channel. The `sender' is the sender of the message
207    The `channel' is the channel. The `message' is the message.  Note
208    that `message' maybe NULL.  The `flags' indicates message flags
209    and it is used to determine how the message can be interpreted
210    (like it may tell the message is multimedia message). */
211 
212 static void
silc_channel_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcChannelEntry channel,SilcMessagePayload payload,SilcChannelPrivateKey key,SilcMessageFlags flags,const unsigned char * message,SilcUInt32 message_len)213 silc_channel_message(SilcClient client, SilcClientConnection conn,
214 		     SilcClientEntry sender, SilcChannelEntry channel,
215 		     SilcMessagePayload payload, SilcChannelPrivateKey key,
216 		     SilcMessageFlags flags, const unsigned char *message,
217 		     SilcUInt32 message_len)
218 {
219 	PurpleConnection *gc = client->application;
220 	SilcPurple sg = gc->proto_data;
221 	PurpleConversation *convo = NULL;
222 	char *msg, *tmp;
223 
224 	if (!message)
225 		return;
226 
227 	if (key) {
228 		GList *l;
229 		SilcPurplePrvgrp prv;
230 
231 		for (l = sg->grps; l; l = l->next)
232 			if (((SilcPurplePrvgrp)l->data)->key == key) {
233 				prv = l->data;
234 				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
235 										prv->channel, sg->account);
236 				break;
237 			}
238 	}
239 	if (!convo)
240 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
241 								channel->channel_name, sg->account);
242 	if (!convo)
243 		return;
244 
245 	if (flags & SILC_MESSAGE_FLAG_SIGNED &&
246 	    purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
247 		/* XXX */
248 	}
249 
250 	if (flags & SILC_MESSAGE_FLAG_DATA) {
251 		/* Process MIME message */
252 #ifdef HAVE_SILCMIME_H
253 		SilcMime mime;
254 		mime = silc_mime_decode(message, message_len);
255 		silcpurple_mime_message(client, conn, sender, channel, payload,
256 				      key, flags, mime, FALSE);
257 #else
258 		char type[128], enc[128];
259 		unsigned char *data;
260 		SilcUInt32 data_len;
261 
262 		memset(type, 0, sizeof(type));
263 		memset(enc, 0, sizeof(enc));
264 
265 		if (!silc_mime_parse(message, message_len, NULL, 0,
266 		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
267 		    &data_len))
268 			return;
269 
270 		if (purple_strequal(type, "application/x-wb") &&
271 		    purple_strequal(enc, "binary") &&
272 		    !purple_account_get_bool(sg->account, "block-wb", FALSE))
273 			silcpurple_wb_receive_ch(client, conn, sender, channel,
274 					       payload, flags, data, data_len);
275 #endif
276 		return;
277 	}
278 
279 	if (flags & SILC_MESSAGE_FLAG_ACTION) {
280 		msg = g_strdup_printf("/me %s",
281 				      (const char *)message);
282 		if (!msg)
283 			return;
284 
285 		tmp = g_markup_escape_text(msg, -1);
286 		/* Send to Purple */
287 		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
288 				 sender->nickname ?
289 				 sender->nickname : "<unknown>", 0,
290 				 tmp, time(NULL));
291 		g_free(tmp);
292 		g_free(msg);
293 		return;
294 	}
295 
296 	if (flags & SILC_MESSAGE_FLAG_NOTICE) {
297 		msg = g_strdup_printf("(notice) <I>%s</I> %s",
298 				      sender->nickname ?
299 				      sender->nickname : "<unknown>",
300 				      (const char *)message);
301 		if (!msg)
302 			return;
303 
304 		/* Send to Purple */
305 		purple_conversation_write(convo, NULL, (const char *)msg,
306 					PURPLE_MESSAGE_SYSTEM, time(NULL));
307 		g_free(msg);
308 		return;
309 	}
310 
311 	if (flags & SILC_MESSAGE_FLAG_UTF8) {
312 		tmp = g_markup_escape_text((const char *)message, -1);
313 		/* Send to Purple */
314 		serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)),
315 				 sender->nickname ?
316 				 sender->nickname : "<unknown>", 0,
317 				 tmp, time(NULL));
318 		g_free(tmp);
319 	}
320 }
321 
322 
323 /* Private message to the client. The `sender' is the sender of the
324    message. The message is `message'and maybe NULL.  The `flags'
325    indicates message flags  and it is used to determine how the message
326    can be interpreted (like it may tell the message is multimedia
327    message). */
328 
329 static void
silc_private_message(SilcClient client,SilcClientConnection conn,SilcClientEntry sender,SilcMessagePayload payload,SilcMessageFlags flags,const unsigned char * message,SilcUInt32 message_len)330 silc_private_message(SilcClient client, SilcClientConnection conn,
331 		     SilcClientEntry sender, SilcMessagePayload payload,
332 		     SilcMessageFlags flags, const unsigned char *message,
333 		     SilcUInt32 message_len)
334 {
335 	PurpleConnection *gc = client->application;
336 	SilcPurple sg = gc->proto_data;
337 	PurpleConversation *convo = NULL;
338 	char *msg, *tmp;
339 
340 	if (!message)
341 		return;
342 
343 	if (sender->nickname)
344 		/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
345 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
346 								sender->nickname, sg->account);
347 
348 	if (flags & SILC_MESSAGE_FLAG_SIGNED &&
349 	    purple_account_get_bool(sg->account, "sign-verify", FALSE)) {
350 		/* XXX */
351 	}
352 
353 	if (flags & SILC_MESSAGE_FLAG_DATA) {
354 #ifdef HAVE_SILCMIME_H
355 		/* Process MIME message */
356 		SilcMime mime;
357 		mime = silc_mime_decode(message, message_len);
358 		silcpurple_mime_message(client, conn, sender, NULL, payload,
359 				      NULL, flags, mime, FALSE);
360 #else
361 		char type[128], enc[128];
362 		unsigned char *data;
363 		SilcUInt32 data_len;
364 
365 		memset(type, 0, sizeof(type));
366 		memset(enc, 0, sizeof(enc));
367 
368 		if (!silc_mime_parse(message, message_len, NULL, 0,
369 		    type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data,
370 		    &data_len))
371 			return;
372 
373 		if (purple_strequal(type, "application/x-wb") &&
374 		    purple_strequal(enc, "binary") &&
375 		    !purple_account_get_bool(sg->account, "block-wb", FALSE))
376 			silcpurple_wb_receive(client, conn, sender, payload,
377 					    flags, data, data_len);
378 #endif
379 		return;
380 	}
381 
382 	if (flags & SILC_MESSAGE_FLAG_ACTION && convo) {
383 		msg = g_strdup_printf("/me %s",
384 				      (const char *)message);
385 		if (!msg)
386 			return;
387 
388 		tmp = g_markup_escape_text(msg, -1);
389 		/* Send to Purple */
390 		serv_got_im(gc, sender->nickname ?
391 			    sender->nickname : "<unknown>",
392 			    tmp, 0, time(NULL));
393 		g_free(msg);
394 		g_free(tmp);
395 		return;
396 	}
397 
398 	if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) {
399 		msg = g_strdup_printf("(notice) <I>%s</I> %s",
400 				      sender->nickname ?
401 				      sender->nickname : "<unknown>",
402 				      (const char *)message);
403 		if (!msg)
404 			return;
405 
406 		/* Send to Purple */
407 		purple_conversation_write(convo, NULL, (const char *)msg,
408 					PURPLE_MESSAGE_SYSTEM, time(NULL));
409 		g_free(msg);
410 		return;
411 	}
412 
413 	if (flags & SILC_MESSAGE_FLAG_UTF8) {
414 		tmp = g_markup_escape_text((const char *)message, -1);
415 		/* Send to Purple */
416 		serv_got_im(gc, sender->nickname ?
417 			    sender->nickname : "<unknown>",
418 			    tmp, 0, time(NULL));
419 		g_free(tmp);
420 	}
421 }
422 
423 
424 /* Notify message to the client. The notify arguments are sent in the
425    same order as servers sends them. The arguments are same as received
426    from the server except for ID's.  If ID is received application receives
427    the corresponding entry to the ID. For example, if Client ID is received
428    application receives SilcClientEntry.  Also, if the notify type is
429    for channel the channel entry is sent to application (even if server
430    does not send it because client library gets the channel entry from
431    the Channel ID in the packet's header). */
432 
433 static void
silc_notify(SilcClient client,SilcClientConnection conn,SilcNotifyType type,...)434 silc_notify(SilcClient client, SilcClientConnection conn,
435 	    SilcNotifyType type, ...)
436 {
437 	va_list va;
438 	PurpleConnection *gc = client->application;
439 	SilcPurple sg = gc->proto_data;
440 	PurpleConversation *convo;
441 	SilcClientEntry client_entry, client_entry2;
442 	SilcChannelEntry channel;
443 	SilcServerEntry server_entry;
444 	SilcIdType idtype;
445 	void *entry;
446 	SilcUInt32 mode;
447 	SilcHashTableList htl;
448 	SilcChannelUser chu;
449 	char buf[512], buf2[512], *tmp, *name;
450 	SilcNotifyType notify;
451 	PurpleBuddy *b;
452 	int i;
453 
454 	va_start(va, type);
455 	memset(buf, 0, sizeof(buf));
456 
457 	switch (type) {
458 
459 	case SILC_NOTIFY_TYPE_NONE:
460 		break;
461 
462 	case SILC_NOTIFY_TYPE_INVITE:
463 		{
464 			GHashTable *components;
465 			va_arg(va, SilcChannelEntry);
466 			name = va_arg(va, char *);
467 			client_entry = va_arg(va, SilcClientEntry);
468 
469 			components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
470 			g_hash_table_insert(components, strdup("channel"), strdup(name));
471 			serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components);
472 		}
473 		break;
474 
475 	case SILC_NOTIFY_TYPE_JOIN:
476 		client_entry = va_arg(va, SilcClientEntry);
477 		channel = va_arg(va, SilcChannelEntry);
478 
479 		/* If we joined channel, do nothing */
480 		if (client_entry == conn->local_entry)
481 			break;
482 
483 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
484 								channel->channel_name, sg->account);
485 		if (!convo)
486 			break;
487 
488 		/* Join user to channel */
489 		g_snprintf(buf, sizeof(buf), "%s@%s",
490 			   client_entry->username, client_entry->hostname);
491 		purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo),
492 					g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE);
493 
494 		break;
495 
496 	case SILC_NOTIFY_TYPE_LEAVE:
497 		client_entry = va_arg(va, SilcClientEntry);
498 		channel = va_arg(va, SilcChannelEntry);
499 
500 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
501 								channel->channel_name, sg->account);
502 		if (!convo)
503 			break;
504 
505 		/* Remove user from channel */
506 		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
507 					   client_entry->nickname, NULL);
508 
509 		break;
510 
511 	case SILC_NOTIFY_TYPE_SIGNOFF:
512 		client_entry = va_arg(va, SilcClientEntry);
513 		tmp = va_arg(va, char *);
514 
515 		if (!client_entry->nickname)
516 			break;
517 
518 		/* Remove from all channels */
519 		silc_hash_table_list(client_entry->channels, &htl);
520 		while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
521 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
522 									chu->channel->channel_name, sg->account);
523 			if (!convo)
524 				continue;
525 			purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
526 						   client_entry->nickname,
527 						   tmp);
528 		}
529 		silc_hash_table_list_reset(&htl);
530 
531 		break;
532 
533 	case SILC_NOTIFY_TYPE_TOPIC_SET:
534 		{
535 			char *esc, *tmp2;
536 			idtype = va_arg(va, int);
537 			entry = va_arg(va, void *);
538 			tmp = va_arg(va, char *);
539 			channel = va_arg(va, SilcChannelEntry);
540 
541 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
542 									channel->channel_name, sg->account);
543 			if (!convo)
544 				break;
545 
546 			if (!tmp)
547 				break;
548 
549 			esc = g_markup_escape_text(tmp, -1);
550 			tmp2 = purple_markup_linkify(esc);
551 			g_free(esc);
552 
553 			if (idtype == SILC_ID_CLIENT) {
554 				client_entry = (SilcClientEntry)entry;
555 				g_snprintf(buf, sizeof(buf),
556 						_("%s has changed the topic of <I>%s</I> to: %s"),
557 						client_entry->nickname, channel->channel_name, tmp2);
558 				purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
559 						buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
560 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
561 						client_entry->nickname, tmp);
562 			} else if (idtype == SILC_ID_SERVER) {
563 				server_entry = (SilcServerEntry)entry;
564 				g_snprintf(buf, sizeof(buf),
565 						_("%s has changed the topic of <I>%s</I> to: %s"),
566 						server_entry->server_name, channel->channel_name, tmp2);
567 				purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name,
568 						buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
569 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
570 						server_entry->server_name, tmp);
571 			} else if (idtype == SILC_ID_CHANNEL) {
572 				channel = (SilcChannelEntry)entry;
573 				g_snprintf(buf, sizeof(buf),
574 						_("%s has changed the topic of <I>%s</I> to: %s"),
575 						channel->channel_name, channel->channel_name, tmp2);
576 				purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
577 						buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
578 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo),
579 						channel->channel_name, tmp);
580 			} else {
581 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp);
582 			}
583 
584 			g_free(tmp2);
585 
586 			break;
587 
588 		}
589 	case SILC_NOTIFY_TYPE_NICK_CHANGE:
590 		client_entry = va_arg(va, SilcClientEntry);
591 		client_entry2 = va_arg(va, SilcClientEntry);
592 
593 		if (purple_strequal(client_entry->nickname, client_entry2->nickname))
594 			break;
595 
596 		/* Change nick on all channels */
597 		silc_hash_table_list(client_entry2->channels, &htl);
598 		while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
599 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
600 									chu->channel->channel_name, sg->account);
601 			if (!convo)
602 				continue;
603 			if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname))
604 				purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
605 										   client_entry->nickname,
606 										   client_entry2->nickname);
607 		}
608 		silc_hash_table_list_reset(&htl);
609 
610 		break;
611 
612 	case SILC_NOTIFY_TYPE_CMODE_CHANGE:
613 		idtype = va_arg(va, int);
614 		entry = va_arg(va, void *);
615 		mode = va_arg(va, SilcUInt32);
616 		(void)va_arg(va, char *);
617 		(void)va_arg(va, char *);
618 		(void)va_arg(va, char *);
619 		(void)va_arg(va, SilcPublicKey);
620 		(void)va_arg(va, SilcBuffer);
621 		channel = va_arg(va, SilcChannelEntry);
622 
623 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
624 								channel->channel_name, sg->account);
625 		if (!convo)
626 			break;
627 
628 		if (idtype == SILC_ID_CLIENT)
629 			name = ((SilcClientEntry)entry)->nickname;
630 		else if (idtype == SILC_ID_SERVER)
631 			name = ((SilcServerEntry)entry)->server_name;
632 		else
633 			name = ((SilcChannelEntry)entry)->channel_name;
634 		if (!name)
635 			break;
636 
637 		if (mode) {
638 			silcpurple_get_chmode_string(mode, buf2, sizeof(buf2));
639 			g_snprintf(buf, sizeof(buf),
640 				   _("<I>%s</I> set channel <I>%s</I> modes to: %s"), name,
641 				   channel->channel_name, buf2);
642 		} else {
643 			g_snprintf(buf, sizeof(buf),
644 				   _("<I>%s</I> removed all channel <I>%s</I> modes"), name,
645 				   channel->channel_name);
646 		}
647 		purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
648 				     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
649 		break;
650 
651 	case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
652 		{
653 			PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
654 			idtype = va_arg(va, int);
655 			entry = va_arg(va, void *);
656 			mode = va_arg(va, SilcUInt32);
657 			client_entry2 = va_arg(va, SilcClientEntry);
658 			channel = va_arg(va, SilcChannelEntry);
659 
660 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
661 									channel->channel_name, sg->account);
662 			if (!convo)
663 				break;
664 
665 			if (idtype == SILC_ID_CLIENT)
666 				name = ((SilcClientEntry)entry)->nickname;
667 			else if (idtype == SILC_ID_SERVER)
668 				name = ((SilcServerEntry)entry)->server_name;
669 			else
670 				name = ((SilcChannelEntry)entry)->channel_name;
671 			if (!name)
672 				break;
673 
674 			if (mode) {
675 				silcpurple_get_chumode_string(mode, buf2, sizeof(buf2));
676 				g_snprintf(buf, sizeof(buf),
677 						_("<I>%s</I> set <I>%s's</I> modes to: %s"), name,
678 						client_entry2->nickname, buf2);
679 				if (mode & SILC_CHANNEL_UMODE_CHANFO)
680 					flags |= PURPLE_CBFLAGS_FOUNDER;
681 				if (mode & SILC_CHANNEL_UMODE_CHANOP)
682 					flags |= PURPLE_CBFLAGS_OP;
683 			} else {
684 				g_snprintf(buf, sizeof(buf),
685 						_("<I>%s</I> removed all <I>%s's</I> modes"), name,
686 						client_entry2->nickname);
687 			}
688 			purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name,
689 					buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
690 			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags);
691 			break;
692 		}
693 
694 	case SILC_NOTIFY_TYPE_MOTD:
695 		tmp = va_arg(va, char *);
696 		silc_free(sg->motd);
697 		sg->motd = silc_memdup(tmp, strlen(tmp));
698 		break;
699 
700 	case SILC_NOTIFY_TYPE_KICKED:
701 		client_entry = va_arg(va, SilcClientEntry);
702 		tmp = va_arg(va, char *);
703 		client_entry2 = va_arg(va, SilcClientEntry);
704 		channel = va_arg(va, SilcChannelEntry);
705 
706 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
707 								channel->channel_name, sg->account);
708 		if (!convo)
709 			break;
710 
711 		if (client_entry == conn->local_entry) {
712 			/* Remove us from channel */
713 			g_snprintf(buf, sizeof(buf),
714 				   _("You have been kicked off <I>%s</I> by <I>%s</I> (%s)"),
715 				   channel->channel_name, client_entry2->nickname,
716 				   tmp ? tmp : "");
717 			purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
718 					     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
719 			serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
720 		} else {
721 			/* Remove user from channel */
722 			g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"),
723 				   client_entry2->nickname, tmp ? tmp : "");
724 			purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
725 						   client_entry->nickname,
726 						   buf);
727 		}
728 
729 		break;
730 
731 	case SILC_NOTIFY_TYPE_KILLED:
732 		client_entry = va_arg(va, SilcClientEntry);
733 		tmp = va_arg(va, char *);
734 		idtype = va_arg(va, int);
735 		entry = va_arg(va, SilcClientEntry);
736 
737 		if (!client_entry->nickname)
738 			break;
739 
740 		if (client_entry == conn->local_entry) {
741 			if (idtype == SILC_ID_CLIENT) {
742 				client_entry2 = (SilcClientEntry)entry;
743 				g_snprintf(buf, sizeof(buf),
744 					   _("You have been killed by %s (%s)"),
745 					   client_entry2->nickname, tmp ? tmp : "");
746 			} else if (idtype == SILC_ID_SERVER) {
747 				server_entry = (SilcServerEntry)entry;
748 				g_snprintf(buf, sizeof(buf),
749 					   _("You have been killed by %s (%s)"),
750 					   server_entry->server_name, tmp ? tmp : "");
751 			} else if (idtype == SILC_ID_CHANNEL) {
752 				channel = (SilcChannelEntry)entry;
753 				g_snprintf(buf, sizeof(buf),
754 					   _("You have been killed by %s (%s)"),
755 					   channel->channel_name, tmp ? tmp : "");
756 			}
757 
758 			/* Remove us from all channels */
759 			silc_hash_table_list(client_entry->channels, &htl);
760 			while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
761 				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
762 										chu->channel->channel_name, sg->account);
763 				if (!convo)
764 					continue;
765 				purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname,
766 						     buf, PURPLE_MESSAGE_SYSTEM, time(NULL));
767 				serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)));
768 			}
769 			silc_hash_table_list_reset(&htl);
770 
771 		} else {
772 			if (idtype == SILC_ID_CLIENT) {
773 				client_entry2 = (SilcClientEntry)entry;
774 				g_snprintf(buf, sizeof(buf),
775 					   _("Killed by %s (%s)"),
776 					   client_entry2->nickname, tmp ? tmp : "");
777 			} else if (idtype == SILC_ID_SERVER) {
778 				server_entry = (SilcServerEntry)entry;
779 				g_snprintf(buf, sizeof(buf),
780 					   _("Killed by %s (%s)"),
781 					   server_entry->server_name, tmp ? tmp : "");
782 			} else if (idtype == SILC_ID_CHANNEL) {
783 				channel = (SilcChannelEntry)entry;
784 				g_snprintf(buf, sizeof(buf),
785 					   _("Killed by %s (%s)"),
786 					   channel->channel_name, tmp ? tmp : "");
787 			}
788 
789 			/* Remove user from all channels */
790 			silc_hash_table_list(client_entry->channels, &htl);
791 			while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
792 				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
793 										chu->channel->channel_name, sg->account);
794 				if (!convo)
795 					continue;
796 				purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
797 							   client_entry->nickname, tmp);
798 			}
799 			silc_hash_table_list_reset(&htl);
800 		}
801 
802 		break;
803 
804 	case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
805 		break;
806 
807 	case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
808 		{
809 			int i;
810 			SilcClientEntry *clients;
811 			SilcUInt32 clients_count;
812 
813 			(void)va_arg(va, void *);
814 			clients = va_arg(va, SilcClientEntry *);
815 			clients_count = va_arg(va, SilcUInt32);
816 
817 			for (i = 0; i < clients_count; i++) {
818 				if (!clients[i]->nickname)
819 					break;
820 
821 				/* Remove from all channels */
822 				silc_hash_table_list(clients[i]->channels, &htl);
823 				while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
824 					convo =
825 						purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
826 											chu->channel->channel_name, sg->account);
827 					if (!convo)
828 						continue;
829 					purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo),
830 								   clients[i]->nickname,
831 								   _("Server signoff"));
832 				}
833 				silc_hash_table_list_reset(&htl);
834 			}
835 		}
836 		break;
837 
838 	case SILC_NOTIFY_TYPE_ERROR:
839 		{
840 			SilcStatus error = va_arg(va, int);
841 			purple_notify_error(gc, "Error Notify",
842 					  silc_get_status_message(error),
843 					  NULL);
844 		}
845 		break;
846 
847 	case SILC_NOTIFY_TYPE_WATCH:
848 		{
849 			SilcPublicKey public_key;
850 			unsigned char *pk;
851 			SilcUInt32 pk_len;
852 			char *fingerprint;
853 
854 			client_entry = va_arg(va, SilcClientEntry);
855 			(void)va_arg(va, char *);
856 			mode = va_arg(va, SilcUInt32);
857 			notify = va_arg(va, int);
858 			public_key = va_arg(va, SilcPublicKey);
859 
860 			b = NULL;
861 			if (public_key) {
862 				PurpleBlistNode *gnode, *cnode, *bnode;
863 				const char *f;
864 
865 				pk = silc_pkcs_public_key_encode(public_key, &pk_len);
866 				if (!pk)
867 					break;
868 				fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
869 				for (i = 0; i < strlen(fingerprint); i++)
870 					if (fingerprint[i] == ' ')
871 						fingerprint[i] = '_';
872 				g_snprintf(buf, sizeof(buf) - 1,
873 					   "%s" G_DIR_SEPARATOR_S "clientkeys"
874 					   G_DIR_SEPARATOR_S "clientkey_%s.pub",
875 					   silcpurple_silcdir(), fingerprint);
876 				silc_free(fingerprint);
877 				silc_free(pk);
878 
879 				/* Find buddy by associated public key */
880 				for (gnode = purple_get_blist()->root; gnode;
881 				     gnode = gnode->next) {
882 					if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
883 						continue;
884 					for (cnode = gnode->child; cnode; cnode = cnode->next) {
885 						if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode))
886 							continue;
887 						for (bnode = cnode->child; bnode;
888 						     bnode = bnode->next) {
889 							if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
890 								continue;
891 							b = (PurpleBuddy *)bnode;
892 							if (b->account != gc->account)
893 								continue;
894 							f = purple_blist_node_get_string(bnode, "public-key");
895 							if (f && purple_strequal(f, buf))
896 								goto cont;
897 							b = NULL;
898 						}
899 					}
900 				}
901 			}
902 		cont:
903 			if (!b) {
904 				/* Find buddy by nickname */
905 				b = purple_find_buddy(sg->account, client_entry->nickname);
906 				if (!b) {
907 					purple_debug_warning("silc", "WATCH for %s, unknown buddy\n",
908 						client_entry->nickname);
909 					break;
910 				}
911 			}
912 
913 			silc_free(b->proto_data);
914 			b->proto_data = silc_memdup(client_entry->id,
915 						    sizeof(*client_entry->id));
916 			if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) {
917 				break;
918 			} else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) {
919 				/* See if client was away and is now present */
920 				if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED |
921 					      SILC_UMODE_BUSY | SILC_UMODE_PAGE |
922 					      SILC_UMODE_DETACHED)) &&
923 				    (client_entry->mode & SILC_UMODE_GONE ||
924 				     client_entry->mode & SILC_UMODE_INDISPOSED ||
925 				     client_entry->mode & SILC_UMODE_BUSY ||
926 				     client_entry->mode & SILC_UMODE_PAGE ||
927 				     client_entry->mode & SILC_UMODE_DETACHED)) {
928 					client_entry->mode = mode;
929 					purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
930 				}
931 				else if ((mode & SILC_UMODE_GONE) ||
932 					 (mode & SILC_UMODE_INDISPOSED) ||
933 					 (mode & SILC_UMODE_BUSY) ||
934 					 (mode & SILC_UMODE_PAGE) ||
935 					 (mode & SILC_UMODE_DETACHED)) {
936 					client_entry->mode = mode;
937 					purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
938 				}
939 			} else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
940 				   notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
941 				   notify == SILC_NOTIFY_TYPE_KILLED) {
942 				client_entry->mode = mode;
943 				purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL);
944 			} else if (notify == SILC_NOTIFY_TYPE_NONE) {
945 				client_entry->mode = mode;
946 				purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL);
947 			}
948 		}
949 		break;
950 
951 	default:
952 		purple_debug_info("silc", "Unhandled notification: %d\n", type);
953 		break;
954 	}
955 
956 	va_end(va);
957 }
958 
959 
960 /* Command handler. This function is called always in the command function.
961    If error occurs it will be called as well. `conn' is the associated
962    client connection. `cmd_context' is the command context that was
963    originally sent to the command. `success' is FALSE if error occurred
964    during command. `command' is the command being processed. It must be
965    noted that this is not reply from server. This is merely called just
966    after application has called the command. Just to tell application
967    that the command really was processed. */
968 
969 static void
silc_command(SilcClient client,SilcClientConnection conn,SilcClientCommandContext cmd_context,bool success,SilcCommand command,SilcStatus status)970 silc_command(SilcClient client, SilcClientConnection conn,
971 	     SilcClientCommandContext cmd_context, bool success,
972 	     SilcCommand command, SilcStatus status)
973 {
974 	PurpleConnection *gc = client->application;
975 	SilcPurple sg = gc->proto_data;
976 
977 	switch (command) {
978 
979 	case SILC_COMMAND_CMODE:
980 		if (cmd_context->argc == 3 &&
981 		    purple_strequal((char *)cmd_context->argv[2], "+C"))
982 			sg->chpk = TRUE;
983 		else
984 			sg->chpk = FALSE;
985 		break;
986 
987 	default:
988 		break;
989 	}
990 }
991 
992 #if 0
993 static void
994 silcpurple_whois_more(SilcClientEntry client_entry, gint id)
995 {
996 	SilcAttributePayload attr;
997 	SilcAttribute attribute;
998 	char *buf;
999 	GString *s;
1000 	SilcVCardStruct vcard;
1001 	int i;
1002 
1003 	if (id != 0)
1004 		return;
1005 
1006 	memset(&vcard, 0, sizeof(vcard));
1007 
1008 	s = g_string_new("");
1009 
1010 	silc_dlist_start(client_entry->attrs);
1011 	while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) {
1012 		attribute = silc_attribute_get_attribute(attr);
1013 		switch (attribute) {
1014 
1015 		case SILC_ATTRIBUTE_USER_INFO:
1016 			if (!silc_attribute_get_object(attr, (void *)&vcard,
1017 						       sizeof(vcard)))
1018 				continue;
1019 			g_string_append_printf(s, "%s:\n\n", _("Personal Information"));
1020 			if (vcard.full_name)
1021 				g_string_append_printf(s, "%s:\t\t%s\n",
1022 						       _("Full Name"),
1023 						       vcard.full_name);
1024 			if (vcard.first_name)
1025 				g_string_append_printf(s, "%s:\t%s\n",
1026 						       _("First Name"),
1027 						       vcard.first_name);
1028 			if (vcard.middle_names)
1029 				g_string_append_printf(s, "%s:\t%s\n",
1030 						       _("Middle Name"),
1031 						       vcard.middle_names);
1032 			if (vcard.family_name)
1033 				g_string_append_printf(s, "%s:\t%s\n",
1034 						       _("Family Name"),
1035 						       vcard.family_name);
1036 			if (vcard.nickname)
1037 				g_string_append_printf(s, "%s:\t\t%s\n",
1038 						       _("Nickname"),
1039 						       vcard.nickname);
1040 			if (vcard.bday)
1041 				g_string_append_printf(s, "%s:\t\t%s\n",
1042 						       _("Birth Day"),
1043 						       vcard.bday);
1044 			if (vcard.title)
1045 				g_string_append_printf(s, "%s:\t\t%s\n",
1046 						       _("Job Title"),
1047 						       vcard.title);
1048 			if (vcard.role)
1049 				g_string_append_printf(s, "%s:\t\t%s\n",
1050 						       _("Job Role"),
1051 						       vcard.role);
1052 			if (vcard.org_name)
1053 				g_string_append_printf(s, "%s:\t%s\n",
1054 						       _("Organization"),
1055 						       vcard.org_name);
1056 			if (vcard.org_unit)
1057 				g_string_append_printf(s, "%s:\t\t%s\n",
1058 						       _("Unit"),
1059 						       vcard.org_unit);
1060 			if (vcard.url)
1061 				g_string_append_printf(s, "%s:\t%s\n",
1062 						       _("Homepage"),
1063 						       vcard.url);
1064 			if (vcard.label)
1065 				g_string_append_printf(s, "%s:\t%s\n",
1066 						       _("Address"),
1067 						       vcard.label);
1068 			for (i = 0; i < vcard.num_tels; i++) {
1069 				if (vcard.tels[i].telnum)
1070 					g_string_append_printf(s, "%s:\t\t\t%s\n",
1071 							       _("Phone"),
1072 							       vcard.tels[i].telnum);
1073 			}
1074 			for (i = 0; i < vcard.num_emails; i++) {
1075 				if (vcard.emails[i].address)
1076 					g_string_append_printf(s, "%s:\t\t%s\n",
1077 							       _("Email"),
1078 							       vcard.emails[i].address);
1079 			}
1080 			if (vcard.note)
1081 				g_string_append_printf(s, "\n%s:\t\t%s\n",
1082 						       _("Note"),
1083 						       vcard.note);
1084 			break;
1085 		}
1086 	}
1087 
1088 	buf = g_string_free(s, FALSE);
1089 	purple_notify_info(NULL, _("User Information"), _("User Information"),
1090 			 buf);
1091 	g_free(buf);
1092 }
1093 #endif
1094 
1095 /* Command reply handler. This function is called always in the command reply
1096    function. If error occurs it will be called as well. Normal scenario
1097    is that it will be called after the received command data has been parsed
1098    and processed. The function is used to pass the received command data to
1099    the application.
1100 
1101    `conn' is the associated client connection. `cmd_payload' is the command
1102    payload data received from server and it can be ignored. It is provided
1103    if the application would like to re-parse the received command data,
1104    however, it must be noted that the data is parsed already by the library
1105    thus the payload can be ignored. `success' is FALSE if error occurred.
1106    In this case arguments are not sent to the application. The `status' is
1107    the command reply status server returned. The `command' is the command
1108    reply being processed. The function has variable argument list and each
1109    command defines the number and type of arguments it passes to the
1110    application (on error they are not sent). */
1111 
1112 static void
silc_command_reply(SilcClient client,SilcClientConnection conn,SilcCommandPayload cmd_payload,bool success,SilcCommand command,SilcStatus status,...)1113 silc_command_reply(SilcClient client, SilcClientConnection conn,
1114 		   SilcCommandPayload cmd_payload, bool success,
1115 		   SilcCommand command, SilcStatus status, ...)
1116 {
1117 	PurpleConnection *gc = client->application;
1118 	SilcPurple sg = gc->proto_data;
1119 	PurpleConversation *convo;
1120 	va_list vp;
1121 
1122 	va_start(vp, status);
1123 
1124 	switch (command) {
1125 	case SILC_COMMAND_JOIN:
1126 		{
1127 			SilcChannelEntry channel_entry;
1128 
1129 			if (!success) {
1130 				purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"),
1131 						  silc_get_status_message(status));
1132 				return;
1133 			}
1134 
1135 			(void)va_arg(vp, char *);
1136 			channel_entry = va_arg(vp, SilcChannelEntry);
1137 
1138 			/* Resolve users on channel */
1139 			silc_client_get_clients_by_channel(client, conn, channel_entry,
1140 							   silcpurple_chat_join_done,
1141 							   channel_entry);
1142 		}
1143 		break;
1144 
1145 	case SILC_COMMAND_LEAVE:
1146 		break;
1147 
1148 	case SILC_COMMAND_USERS:
1149 		break;
1150 
1151 	case SILC_COMMAND_WHOIS:
1152 		{
1153 			SilcUInt32 idle, mode;
1154 			SilcBuffer channels, user_modes;
1155 			SilcClientEntry client_entry;
1156 			char tmp[1024], *tmp2;
1157 			char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr;
1158 			PurpleNotifyUserInfo *user_info;
1159 
1160 			if (!success) {
1161 				purple_notify_error(gc, _("User Information"),
1162 						_("Cannot get user information"),
1163 						silc_get_status_message(status));
1164 				break;
1165 			}
1166 
1167 			client_entry = va_arg(vp, SilcClientEntry);
1168 			if (!client_entry->nickname)
1169 				break;
1170 			(void)va_arg(vp, char *);
1171 			(void)va_arg(vp, char *);
1172 			(void)va_arg(vp, char *);
1173 			channels = va_arg(vp, SilcBuffer);
1174 			mode = va_arg(vp, SilcUInt32);
1175 			idle = va_arg(vp, SilcUInt32);
1176 			(void)va_arg(vp, unsigned char *);
1177 			user_modes = va_arg(vp, SilcBuffer);
1178 
1179 			user_info = purple_notify_user_info_new();
1180 			tmp2 = g_markup_escape_text(client_entry->nickname, -1);
1181 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2);
1182 			g_free(tmp2);
1183 			if (client_entry->realname) {
1184 				tmp2 = g_markup_escape_text(client_entry->realname, -1);
1185 				purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2);
1186 				g_free(tmp2);
1187 			}
1188 			if (client_entry->username) {
1189 				tmp2 = g_markup_escape_text(client_entry->username, -1);
1190 				if (client_entry->hostname) {
1191 					gchar *tmp3;
1192 					tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname);
1193 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1194 					g_free(tmp3);
1195 				} else
1196 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp2);
1197 				g_free(tmp2);
1198 			}
1199 
1200 			if (client_entry->mode) {
1201 				memset(tmp, 0, sizeof(tmp));
1202 				silcpurple_get_umode_string(client_entry->mode,
1203 						tmp, sizeof(tmp) - strlen(tmp));
1204 				purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp);
1205 			}
1206 
1207 			silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr);
1208 			if (moodstr) {
1209 				purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr);
1210 				g_free(moodstr);
1211 			}
1212 
1213 			if (statusstr) {
1214 				tmp2 = g_markup_escape_text(statusstr, -1);
1215 				purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2);
1216 				g_free(statusstr);
1217 				g_free(tmp2);
1218 			}
1219 
1220 			if (contactstr) {
1221 				purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr);
1222 				g_free(contactstr);
1223 			}
1224 
1225 			if (langstr) {
1226 				purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr);
1227 				g_free(langstr);
1228 			}
1229 
1230 			if (devicestr) {
1231 				purple_notify_user_info_add_pair(user_info, _("Device"), devicestr);
1232 				g_free(devicestr);
1233 			}
1234 
1235 			if (tzstr) {
1236 				purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr);
1237 				g_free(tzstr);
1238 			}
1239 
1240 			if (geostr) {
1241 				purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr);
1242 				g_free(geostr);
1243 			}
1244 
1245 			if (client_entry->server)
1246 				purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1247 
1248 			if (channels && user_modes) {
1249 				SilcUInt32 *umodes;
1250 				SilcDList list =
1251 					silc_channel_payload_parse_list(channels->data,
1252 							channels->len);
1253 				if (list && silc_get_mode_list(user_modes,
1254 							silc_dlist_count(list),
1255 							&umodes)) {
1256 					SilcChannelPayload entry;
1257 					int i = 0;
1258 
1259 					memset(tmp, 0, sizeof(tmp));
1260 					silc_dlist_start(list);
1261 					while ((entry = silc_dlist_get(list))
1262 							!= SILC_LIST_END) {
1263 						SilcUInt32 name_len;
1264 						char *m = silc_client_chumode_char(umodes[i++]);
1265 						char *name = (char *)silc_channel_get_name(entry, &name_len);
1266 						if (m)
1267 							silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m));
1268 						silc_strncat(tmp, sizeof(tmp) - 1, name, name_len);
1269 						silc_strncat(tmp, sizeof(tmp) - 1, "  ", 1);
1270 						silc_free(m);
1271 
1272 					}
1273 					tmp2 = g_markup_escape_text(tmp, -1);
1274 					purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2);
1275 					g_free(tmp2);
1276 					silc_free(umodes);
1277 				}
1278 			}
1279 
1280 			if (client_entry->public_key) {
1281 				char *fingerprint, *babbleprint;
1282 				unsigned char *pk;
1283 				SilcUInt32 pk_len;
1284 				pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1285 				fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1286 				babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1287 				purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1288 				purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1289 				silc_free(fingerprint);
1290 				silc_free(babbleprint);
1291 				silc_free(pk);
1292 			}
1293 
1294 #if 0 /* XXX for now, let's not show attrs here */
1295 			if (client_entry->attrs)
1296 				purple_request_action(gc, _("User Information"),
1297 						_("User Information"),
1298 						buf, 1, client_entry, 2,
1299 						_("OK"), G_CALLBACK(silcpurple_whois_more),
1300 						_("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL);
1301 			else
1302 #endif
1303 			purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL);
1304 			purple_notify_user_info_destroy(user_info);
1305 		}
1306 		break;
1307 
1308 	case SILC_COMMAND_WHOWAS:
1309 		{
1310 			SilcClientEntry client_entry;
1311 			char *nickname, *realname, *username, *tmp;
1312 			PurpleNotifyUserInfo *user_info;
1313 
1314 			if (!success) {
1315 				purple_notify_error(gc, _("User Information"),
1316 						  _("Cannot get user information"),
1317 						  silc_get_status_message(status));
1318 				break;
1319 			}
1320 
1321 			client_entry = va_arg(vp, SilcClientEntry);
1322 			nickname = va_arg(vp, char *);
1323 			username = va_arg(vp, char *);
1324 			realname = va_arg(vp, char *);
1325 			if (!nickname)
1326 				break;
1327 
1328 			user_info = purple_notify_user_info_new();
1329 			tmp = g_markup_escape_text(nickname, -1);
1330 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
1331 			g_free(tmp);
1332 			if (realname) {
1333 				tmp = g_markup_escape_text(realname, -1);
1334 				purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp);
1335 				g_free(tmp);
1336 			}
1337 			if (username) {
1338 				tmp = g_markup_escape_text(username, -1);
1339 				if (client_entry && client_entry->hostname) {
1340 					gchar *tmp3;
1341 					tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname);
1342 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp3);
1343 					g_free(tmp3);
1344 				} else
1345 					purple_notify_user_info_add_pair(user_info, _("Username"), tmp);
1346 				g_free(tmp);
1347 			}
1348 			if (client_entry && client_entry->server)
1349 				purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server);
1350 
1351 
1352 			if (client_entry && client_entry->public_key) {
1353 				char *fingerprint, *babbleprint;
1354 				unsigned char *pk;
1355 				SilcUInt32 pk_len;
1356 				pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len);
1357 				fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
1358 				babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
1359 				purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint);
1360 				purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint);
1361 				silc_free(fingerprint);
1362 				silc_free(babbleprint);
1363 				silc_free(pk);
1364 			}
1365 
1366 			purple_notify_userinfo(gc, nickname, user_info, NULL, NULL);
1367 			purple_notify_user_info_destroy(user_info);
1368 		}
1369 		break;
1370 
1371 	case SILC_COMMAND_DETACH:
1372 		if (!success) {
1373 			purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"),
1374 					  silc_get_status_message(status));
1375 			return;
1376 		}
1377 		break;
1378 
1379 	case SILC_COMMAND_TOPIC:
1380 		{
1381 			SilcChannelEntry channel;
1382 
1383 			if (!success) {
1384 				purple_notify_error(gc, _("Topic"), _("Cannot set topic"),
1385 						  silc_get_status_message(status));
1386 				return;
1387 			}
1388 
1389 			channel = va_arg(vp, SilcChannelEntry);
1390 
1391 			convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1392 									channel->channel_name, sg->account);
1393 			if (!convo) {
1394 				purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n",
1395 								 channel->channel_name);
1396 				break;
1397 			}
1398 
1399 			/* Set topic */
1400 			if (channel->topic)
1401 				purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic);
1402 		}
1403 		break;
1404 
1405 	case SILC_COMMAND_NICK:
1406 		{
1407 			/* I don't think we should need to do this because the server should
1408 			 * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own
1409 			 * nick, but it isn't, so we deal with it here instead. Stu. */
1410 			SilcClientEntry local_entry;
1411 			SilcHashTableList htl;
1412 			SilcChannelUser chu;
1413 			const char *oldnick;
1414 
1415 			if (!success) {
1416 				purple_notify_error(gc, _("Nick"), _("Failed to change nickname"),
1417 						silc_get_status_message(status));
1418 				return;
1419 			}
1420 
1421 			local_entry = va_arg(vp, SilcClientEntry);
1422 
1423 			/* Change nick on all channels */
1424 			silc_hash_table_list(local_entry->channels, &htl);
1425 			while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
1426 				convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
1427 									chu->channel->channel_name, sg->account);
1428 				if (!convo)
1429 					continue;
1430 				oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo));
1431 				if (!purple_strequal(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) {
1432 					purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo),
1433 							oldnick, local_entry->nickname);
1434 					purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname);
1435 				}
1436 			}
1437 			silc_hash_table_list_reset(&htl);
1438 
1439 			purple_connection_set_display_name(gc, local_entry->nickname);
1440 		}
1441 		break;
1442 
1443 	case SILC_COMMAND_LIST:
1444 		{
1445 			char *topic, *name;
1446 			int usercount;
1447 			PurpleRoomlistRoom *room;
1448 
1449 			if (sg->roomlist_cancelled)
1450 				break;
1451 
1452 			if (!success) {
1453 				purple_notify_error(gc, _("Error"), _("Error retrieving room list"),
1454 						  silc_get_status_message(status));
1455 				purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1456 				purple_roomlist_unref(sg->roomlist);
1457 				sg->roomlist = NULL;
1458 				return;
1459 			}
1460 
1461 			(void)va_arg(vp, SilcChannelEntry);
1462 			name = va_arg(vp, char *);
1463 			if (!name) {
1464 				purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"),
1465 						  silc_get_status_message(status));
1466 				purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1467 				purple_roomlist_unref(sg->roomlist);
1468 				sg->roomlist = NULL;
1469 				return;
1470 			}
1471 			topic = va_arg(vp, char *);
1472 			usercount = va_arg(vp, int);
1473 
1474 			room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL);
1475 			purple_roomlist_room_add_field(sg->roomlist, room, name);
1476 			purple_roomlist_room_add_field(sg->roomlist, room,
1477 						     SILC_32_TO_PTR(usercount));
1478 			purple_roomlist_room_add_field(sg->roomlist, room,
1479 						     topic ? topic : "");
1480 			purple_roomlist_room_add(sg->roomlist, room);
1481 
1482 			if (status == SILC_STATUS_LIST_END ||
1483 			    status == SILC_STATUS_OK) {
1484 				purple_roomlist_set_in_progress(sg->roomlist, FALSE);
1485 				purple_roomlist_unref(sg->roomlist);
1486 				sg->roomlist = NULL;
1487 			}
1488 		}
1489 		break;
1490 
1491 	case SILC_COMMAND_GETKEY:
1492 		{
1493 			SilcPublicKey public_key;
1494 
1495 			if (!success) {
1496 				purple_notify_error(gc, _("Get Public Key"),
1497 						  _("Cannot fetch the public key"),
1498 						  silc_get_status_message(status));
1499 				return;
1500 			}
1501 
1502 			(void)va_arg(vp, SilcUInt32);
1503 			(void)va_arg(vp, void *);
1504 			public_key = va_arg(vp, SilcPublicKey);
1505 
1506 			if (!public_key)
1507 				purple_notify_error(gc, _("Get Public Key"),
1508 						  _("Cannot fetch the public key"),
1509 						  _("No public key was received"));
1510 		}
1511 		break;
1512 
1513 	case SILC_COMMAND_INFO:
1514 		{
1515 
1516 			char *server_name;
1517 			char *server_info;
1518 			char tmp[256];
1519 
1520 			if (!success) {
1521 				purple_notify_error(gc, _("Server Information"),
1522 						  _("Cannot get server information"),
1523 						  silc_get_status_message(status));
1524 				return;
1525 			}
1526 
1527 			(void)va_arg(vp, SilcServerEntry);
1528 			server_name = va_arg(vp, char *);
1529 			server_info = va_arg(vp, char *);
1530 
1531 			if (server_name && server_info) {
1532 				g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s",
1533 					   server_name, server_info);
1534 				purple_notify_info(gc, NULL, _("Server Information"), tmp);
1535 			}
1536 		}
1537 		break;
1538 
1539 	case SILC_COMMAND_STATS:
1540 		{
1541 			SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops,
1542 			my_router_ops, cell_clients, cell_channels, cell_servers,
1543 			clients, channels, servers, routers, server_ops, router_ops;
1544 			SilcUInt32 buffer_length;
1545 			SilcBufferStruct buf;
1546 
1547 			unsigned char *server_stats;
1548 			char *msg;
1549 
1550 			if (!success) {
1551 				purple_notify_error(gc, _("Server Statistics"),
1552 						_("Cannot get server statistics"),
1553 						silc_get_status_message(status));
1554 				return;
1555 			}
1556 
1557 			server_stats = va_arg(vp, unsigned char *);
1558 			buffer_length = va_arg(vp, SilcUInt32);
1559 			if (!server_stats || !buffer_length) {
1560 				purple_notify_error(gc, _("Server Statistics"),
1561 						_("No server statistics available"), NULL);
1562 				break;
1563 			}
1564 			silc_buffer_set(&buf, server_stats, buffer_length);
1565 			silc_buffer_unformat(&buf,
1566 					SILC_STR_UI_INT(&starttime),
1567 					SILC_STR_UI_INT(&uptime),
1568 					SILC_STR_UI_INT(&my_clients),
1569 					SILC_STR_UI_INT(&my_channels),
1570 					SILC_STR_UI_INT(&my_server_ops),
1571 					SILC_STR_UI_INT(&my_router_ops),
1572 					SILC_STR_UI_INT(&cell_clients),
1573 					SILC_STR_UI_INT(&cell_channels),
1574 					SILC_STR_UI_INT(&cell_servers),
1575 					SILC_STR_UI_INT(&clients),
1576 					SILC_STR_UI_INT(&channels),
1577 					SILC_STR_UI_INT(&servers),
1578 					SILC_STR_UI_INT(&routers),
1579 					SILC_STR_UI_INT(&server_ops),
1580 					SILC_STR_UI_INT(&router_ops),
1581 					SILC_STR_END);
1582 
1583 			msg = g_strdup_printf(_("Local server start time: %s\n"
1584 					"Local server uptime: %s\n"
1585 					"Local server clients: %d\n"
1586 					"Local server channels: %d\n"
1587 					"Local server operators: %d\n"
1588 					"Local router operators: %d\n"
1589 					"Local cell clients: %d\n"
1590 					"Local cell channels: %d\n"
1591 					"Local cell servers: %d\n"
1592 					"Total clients: %d\n"
1593 					"Total channels: %d\n"
1594 					"Total servers: %d\n"
1595 					"Total routers: %d\n"
1596 					"Total server operators: %d\n"
1597 					"Total router operators: %d\n"),
1598 					silc_get_time(starttime),
1599 					purple_str_seconds_to_string((int)uptime),
1600 					(int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops,
1601 					(int)cell_clients, (int)cell_channels, (int)cell_servers,
1602 					(int)clients, (int)channels, (int)servers, (int)routers,
1603 					(int)server_ops, (int)router_ops);
1604 
1605 			purple_notify_info(gc, NULL,
1606 					_("Network Statistics"), msg);
1607 			g_free(msg);
1608 		}
1609 		break;
1610 
1611 	case SILC_COMMAND_PING:
1612 		{
1613 			if (!success) {
1614 				purple_notify_error(gc, _("Ping"), _("Ping failed"),
1615 								  silc_get_status_message(status));
1616 				return;
1617 			}
1618 
1619 			purple_notify_info(gc, _("Ping"), _("Ping reply received from server"),
1620 							 NULL);
1621 		}
1622 		break;
1623 
1624 	case SILC_COMMAND_KILL:
1625 		if (!success) {
1626 			purple_notify_error(gc, _("Kill User"),
1627 					  _("Could not kill user"),
1628 					  silc_get_status_message(status));
1629 			return;
1630 		}
1631 		break;
1632 
1633 	case SILC_COMMAND_CMODE:
1634 		{
1635 			SilcChannelEntry channel_entry;
1636 			SilcBuffer channel_pubkeys;
1637 
1638 			if (!success)
1639 				return;
1640 
1641 			channel_entry = va_arg(vp, SilcChannelEntry);
1642 			(void)va_arg(vp, SilcUInt32);
1643 			(void)va_arg(vp, SilcPublicKey);
1644 			channel_pubkeys = va_arg(vp, SilcBuffer);
1645 
1646 			if (sg->chpk)
1647 				silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys);
1648 		}
1649 		break;
1650 
1651 	default:
1652 		if (success)
1653 			purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command);
1654 		else
1655 			purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command,
1656 							silc_get_status_message(status));
1657 		break;
1658 	}
1659 
1660 	va_end(vp);
1661 }
1662 
1663 
1664 /* Called to indicate that connection was either successfully established
1665    or connecting failed.  This is also the first time application receives
1666    the SilcClientConnection object which it should save somewhere.
1667    If the `success' is FALSE the application must always call the function
1668    silc_client_close_connection. */
1669 
1670 static void
silc_connected(SilcClient client,SilcClientConnection conn,SilcClientConnectionStatus status)1671 silc_connected(SilcClient client, SilcClientConnection conn,
1672 	       SilcClientConnectionStatus status)
1673 {
1674 	PurpleConnection *gc = client->application;
1675 	SilcPurple sg;
1676 
1677 	if (gc == NULL) {
1678 		silc_client_close_connection(client, conn);
1679 		return;
1680 	}
1681 	sg = gc->proto_data;
1682 
1683 	switch (status) {
1684 	case SILC_CLIENT_CONN_SUCCESS:
1685 	case SILC_CLIENT_CONN_SUCCESS_RESUME:
1686 		purple_connection_set_state(gc, PURPLE_CONNECTED);
1687 
1688 		/* Send the server our buddy list */
1689 		silcpurple_send_buddylist(gc);
1690 
1691 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1692 
1693 		/* Send any UMODEs configured for account */
1694 		if (purple_account_get_bool(sg->account, "block-ims", FALSE)) {
1695 			silc_client_command_call(sg->client, sg->conn, NULL,
1696 					"UMODE", "+P", NULL);
1697 		}
1698 
1699 		return;
1700 		break;
1701 	case SILC_CLIENT_CONN_ERROR:
1702 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1703 			_("Error during connecting to SILC Server"));
1704 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1705 		break;
1706 
1707 	case SILC_CLIENT_CONN_ERROR_KE:
1708 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
1709 			_("Key Exchange failed"));
1710 		break;
1711 
1712 	case SILC_CLIENT_CONN_ERROR_AUTH:
1713 		purple_connection_error_reason(gc,
1714 			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
1715 			_("Authentication failed"));
1716 		break;
1717 
1718 	case SILC_CLIENT_CONN_ERROR_RESUME:
1719 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
1720 			_("Resuming detached session failed. "
1721 			  "Press Reconnect to create new connection."));
1722 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1723 		break;
1724 
1725 	case SILC_CLIENT_CONN_ERROR_TIMEOUT:
1726 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1727 			_("Connection timed out"));
1728 		break;
1729 	}
1730 
1731 	/* Error */
1732 	sg->conn = NULL;
1733 	silc_client_close_connection(client, conn);
1734 }
1735 
1736 
1737 /* Called to indicate that connection was disconnected to the server.
1738    The `status' may tell the reason of the disconnection, and if the
1739    `message' is non-NULL it may include the disconnection message
1740    received from server. */
1741 
1742 static void
silc_disconnected(SilcClient client,SilcClientConnection conn,SilcStatus status,const char * message)1743 silc_disconnected(SilcClient client, SilcClientConnection conn,
1744 		  SilcStatus status, const char *message)
1745 {
1746 	PurpleConnection *gc = client->application;
1747 	SilcPurple sg = gc->proto_data;
1748 
1749 	if (sg->resuming && !sg->detaching)
1750 		g_unlink(silcpurple_session_file(purple_account_get_username(sg->account)));
1751 
1752 	sg->conn = NULL;
1753 
1754 	/* Close the connection */
1755 	if (!sg->detaching)
1756 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
1757 			_("Disconnected by server"));
1758 	else
1759 		/* TODO: Does this work correctly? Maybe we need to set wants_to_die? */
1760 		purple_account_disconnect(purple_connection_get_account(gc));
1761 }
1762 
1763 
1764 typedef struct {
1765 	SilcGetAuthMeth completion;
1766 	void *context;
1767 } *SilcPurpleGetAuthMethod;
1768 
1769 /* Callback called when we've received the authentication method information
1770    from the server after we've requested it. */
1771 
silc_get_auth_method_callback(SilcClient client,SilcClientConnection conn,SilcAuthMethod auth_meth,void * context)1772 static void silc_get_auth_method_callback(SilcClient client,
1773 					  SilcClientConnection conn,
1774 					  SilcAuthMethod auth_meth,
1775 					  void *context)
1776 {
1777 	SilcPurpleGetAuthMethod internal = context;
1778 
1779 	switch (auth_meth) {
1780 	case SILC_AUTH_NONE:
1781 		/* No authentication required. */
1782 		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1783 		break;
1784 
1785 	case SILC_AUTH_PASSWORD:
1786 		/* By returning NULL here the library will ask the passphrase from us
1787 		   by calling the silc_ask_passphrase. */
1788 		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1789 		break;
1790 
1791 	case SILC_AUTH_PUBLIC_KEY:
1792 		/* Do not get the authentication data now, the library will generate
1793 		   it using our default key, if we do not provide it here. */
1794 		(*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context);
1795 		break;
1796 	}
1797 
1798 	silc_free(internal);
1799 }
1800 
1801 /* Find authentication method and authentication data by hostname and
1802    port. The hostname may be IP address as well. When the authentication
1803    method has been resolved the `completion' callback with the found
1804    authentication method and authentication data is called. The `conn'
1805    may be NULL. */
1806 
1807 static void
silc_get_auth_method(SilcClient client,SilcClientConnection conn,char * hostname,SilcUInt16 port,SilcGetAuthMeth completion,void * context)1808 silc_get_auth_method(SilcClient client, SilcClientConnection conn,
1809 		     char *hostname, SilcUInt16 port,
1810 		     SilcGetAuthMeth completion, void *context)
1811 {
1812 	PurpleConnection *gc = client->application;
1813 	SilcPurple sg = gc->proto_data;
1814 	SilcPurpleGetAuthMethod internal;
1815 	const char *password;
1816 
1817 	/* Progress */
1818 	if (sg->resuming)
1819 		purple_connection_update_progress(gc, _("Resuming session"), 4, 5);
1820 	else
1821 		purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5);
1822 
1823 	/* Check configuration if we have this connection configured.  If we
1824 	   have then return that data immediately, as it's faster way. */
1825 	if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) {
1826 		completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context);
1827 		return;
1828 	}
1829 	password = purple_connection_get_password(gc);
1830 	if (password && *password) {
1831 		completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context);
1832 		return;
1833 	}
1834 
1835 	/* Resolve the authentication method from server, as we may not know it. */
1836 	internal = silc_calloc(1, sizeof(*internal));
1837 	if (!internal)
1838 		return;
1839 	internal->completion = completion;
1840 	internal->context = context;
1841 	silc_client_request_authentication_method(client, conn,
1842 						  silc_get_auth_method_callback,
1843 						  internal);
1844 }
1845 
1846 
1847 /* Verifies received public key. The `conn_type' indicates which entity
1848    (server, client etc.) has sent the public key. If user decides to trust
1849    the application may save the key as trusted public key for later
1850    use. The `completion' must be called after the public key has been
1851    verified. */
1852 
1853 static void
silc_verify_public_key(SilcClient client,SilcClientConnection conn,SilcSocketType conn_type,unsigned char * pk,SilcUInt32 pk_len,SilcSKEPKType pk_type,SilcVerifyPublicKey completion,void * context)1854 silc_verify_public_key(SilcClient client, SilcClientConnection conn,
1855 		       SilcSocketType conn_type, unsigned char *pk,
1856 		       SilcUInt32 pk_len, SilcSKEPKType pk_type,
1857 		       SilcVerifyPublicKey completion, void *context)
1858 {
1859 	PurpleConnection *gc = client->application;
1860 	SilcPurple sg = gc->proto_data;
1861 
1862 	if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER ||
1863 			  conn_type == SILC_SOCKET_TYPE_ROUTER)) {
1864 		/* Progress */
1865 		if (sg->resuming)
1866 			purple_connection_update_progress(gc, _("Resuming session"), 3, 5);
1867 		else
1868 			purple_connection_update_progress(gc, _("Verifying server public key"),
1869 							3, 5);
1870 	}
1871 
1872 	/* Verify public key */
1873 	silcpurple_verify_public_key(client, conn, NULL, conn_type, pk,
1874 				   pk_len, pk_type, completion, context);
1875 }
1876 
1877 typedef struct {
1878 	SilcAskPassphrase completion;
1879 	void *context;
1880 } *SilcPurpleAskPassphrase;
1881 
1882 static void
silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal,const char * passphrase)1883 silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase)
1884 {
1885 	if (!passphrase || !(*passphrase))
1886 		internal->completion(NULL, 0, internal->context);
1887 	else
1888 		internal->completion((unsigned char *)passphrase,
1889 				     strlen(passphrase), internal->context);
1890 	silc_free(internal);
1891 }
1892 
1893 /* Ask (interact, that is) a passphrase from user. The passphrase is
1894    returned to the library by calling the `completion' callback with
1895    the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
1896    if not then the library will attempt to encode. */
1897 
1898 static void
silc_ask_passphrase(SilcClient client,SilcClientConnection conn,SilcAskPassphrase completion,void * context)1899 silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
1900 		    SilcAskPassphrase completion, void *context)
1901 {
1902 	PurpleConnection *gc = client->application;
1903 	SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal));
1904 
1905 	if (!internal)
1906 		return;
1907 	internal->completion = completion;
1908 	internal->context = context;
1909 	purple_request_input(gc, _("Passphrase"), NULL,
1910 			   _("Passphrase required"), NULL, FALSE, TRUE, NULL,
1911 			   _("OK"), G_CALLBACK(silc_ask_passphrase_cb),
1912 			   _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb),
1913 			   purple_connection_get_account(gc), NULL, NULL, internal);
1914 }
1915 
1916 
1917 /* Notifies application that failure packet was received.  This is called
1918    if there is some protocol active in the client.  The `protocol' is the
1919    protocol context.  The `failure' is opaque pointer to the failure
1920    indication.  Note, that the `failure' is protocol dependant and
1921    application must explicitly cast it to correct type.  Usually `failure'
1922    is 32 bit failure type (see protocol specs for all protocol failure
1923    types). */
1924 
1925 static void
silc_failure(SilcClient client,SilcClientConnection conn,SilcProtocol protocol,void * failure)1926 silc_failure(SilcClient client, SilcClientConnection conn,
1927 	     SilcProtocol protocol, void *failure)
1928 {
1929 	PurpleConnection *gc = client->application;
1930 	char buf[128];
1931 
1932 	memset(buf, 0, sizeof(buf));
1933 
1934 	if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) {
1935 		SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure);
1936 
1937 		if (status == SILC_SKE_STATUS_BAD_VERSION)
1938 			g_snprintf(buf, sizeof(buf),
1939 				   _("Failure: Version mismatch, upgrade your client"));
1940 		if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY)
1941 			g_snprintf(buf, sizeof(buf),
1942 				   _("Failure: Remote does not trust/support your public key"));
1943 		if (status == SILC_SKE_STATUS_UNKNOWN_GROUP)
1944 			g_snprintf(buf, sizeof(buf),
1945 				   _("Failure: Remote does not support proposed KE group"));
1946 		if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER)
1947 			g_snprintf(buf, sizeof(buf),
1948 				   _("Failure: Remote does not support proposed cipher"));
1949 		if (status == SILC_SKE_STATUS_UNKNOWN_PKCS)
1950 			g_snprintf(buf, sizeof(buf),
1951 				   _("Failure: Remote does not support proposed PKCS"));
1952 		if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION)
1953 			g_snprintf(buf, sizeof(buf),
1954 				   _("Failure: Remote does not support proposed hash function"));
1955 		if (status == SILC_SKE_STATUS_UNKNOWN_HMAC)
1956 			g_snprintf(buf, sizeof(buf),
1957 				   _("Failure: Remote does not support proposed HMAC"));
1958 		if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE)
1959 			g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature"));
1960 		if (status == SILC_SKE_STATUS_INVALID_COOKIE)
1961 			g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie"));
1962 
1963 		/* Show the error on the progress bar.  A more generic error message
1964 		   is going to be showed to user after this in the silc_connected. */
1965 		purple_connection_update_progress(gc, buf, 2, 5);
1966 	}
1967 
1968 	if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) {
1969 		SilcUInt32 err = SILC_PTR_TO_32(failure);
1970 
1971 		if (err == SILC_AUTH_FAILED)
1972 			g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed"));
1973 
1974 		/* Show the error on the progress bar.  A more generic error message
1975 		   is going to be showed to user after this in the silc_connected. */
1976 		purple_connection_update_progress(gc, buf, 4, 5);
1977 	}
1978 }
1979 
1980 /* Asks whether the user would like to perform the key agreement protocol.
1981    This is called after we have received an key agreement packet or an
1982    reply to our key agreement packet. This returns TRUE if the user wants
1983    the library to perform the key agreement protocol and FALSE if it is not
1984    desired (application may start it later by calling the function
1985    silc_client_perform_key_agreement). If TRUE is returned also the
1986    `completion' and `context' arguments must be set by the application. */
1987 
1988 static bool
silc_key_agreement(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,const char * hostname,SilcUInt16 port,SilcKeyAgreementCallback * completion,void ** context)1989 silc_key_agreement(SilcClient client, SilcClientConnection conn,
1990 		   SilcClientEntry client_entry, const char *hostname,
1991 		   SilcUInt16 port, SilcKeyAgreementCallback *completion,
1992 		   void **context)
1993 {
1994 	silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port);
1995 	*completion = NULL;
1996 	*context = NULL;
1997 	return FALSE;
1998 }
1999 
2000 
2001 /* Notifies application that file transfer protocol session is being
2002    requested by the remote client indicated by the `client_entry' from
2003    the `hostname' and `port'. The `session_id' is the file transfer
2004    session and it can be used to either accept or reject the file
2005    transfer request, by calling the silc_client_file_receive or
2006    silc_client_file_close, respectively. */
2007 
2008 static void
silc_ftp(SilcClient client,SilcClientConnection conn,SilcClientEntry client_entry,SilcUInt32 session_id,const char * hostname,SilcUInt16 port)2009 silc_ftp(SilcClient client, SilcClientConnection conn,
2010 	 SilcClientEntry client_entry, SilcUInt32 session_id,
2011 	 const char *hostname, SilcUInt16 port)
2012 {
2013 	silcpurple_ftp_request(client, conn, client_entry, session_id,
2014 			     hostname, port);
2015 }
2016 
2017 
2018 /* Delivers SILC session detachment data indicated by `detach_data' to the
2019    application.  If application has issued SILC_COMMAND_DETACH command
2020    the client session in the SILC network is not quit.  The client remains
2021    in the network but is detached.  The detachment data may be used later
2022    to resume the session in the SILC Network.  The appliation is
2023    responsible of saving the `detach_data', to for example in a file.
2024 
2025    The detachment data can be given as argument to the functions
2026    silc_client_connect_to_server, or silc_client_add_connection when
2027    creating connection to remote server, inside SilcClientConnectionParams
2028    structure.  If it is provided the client library will attempt to resume
2029    the session in the network.  After the connection is created
2030    successfully, the application is responsible of setting the user
2031    interface for user into the same state it was before detaching (showing
2032    same channels, channel modes, etc).  It can do this by fetching the
2033    information (like joined channels) from the client library. */
2034 
2035 static void
silc_detach(SilcClient client,SilcClientConnection conn,const unsigned char * detach_data,SilcUInt32 detach_data_len)2036 silc_detach(SilcClient client, SilcClientConnection conn,
2037 	    const unsigned char *detach_data, SilcUInt32 detach_data_len)
2038 {
2039 	PurpleConnection *gc = client->application;
2040 	SilcPurple sg = gc->proto_data;
2041 	const char *file;
2042 
2043 	/* Save the detachment data to file. */
2044 	file = silcpurple_session_file(purple_account_get_username(sg->account));
2045 	g_unlink(file);
2046 	silc_file_writefile(file, (char *)detach_data, detach_data_len);
2047 }
2048 
2049 SilcClientOperations ops = {
2050 	silc_say,
2051 	silc_channel_message,
2052 	silc_private_message,
2053 	silc_notify,
2054 	silc_command,
2055 	silc_command_reply,
2056 	silc_connected,
2057 	silc_disconnected,
2058 	silc_get_auth_method,
2059 	silc_verify_public_key,
2060 	silc_ask_passphrase,
2061 	silc_failure,
2062 	silc_key_agreement,
2063 	silc_ftp,
2064 	silc_detach
2065 };
2066