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