1 /*
2 
3   silcpurple.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 2004 - 2005 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 "version.h"
24 #include "wb.h"
25 #include "core.h"
26 
27 extern SilcClientOperations ops;
28 static PurplePlugin *silc_plugin = NULL;
29 
30 static const char *
silcpurple_list_icon(PurpleAccount * a,PurpleBuddy * b)31 silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
32 {
33 	return (const char *)"silc";
34 }
35 
36 static GList *
silcpurple_away_states(PurpleAccount * account)37 silcpurple_away_states(PurpleAccount *account)
38 {
39 	PurpleStatusType *type;
40 	GList *types = NULL;
41 
42 	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE);
43 	types = g_list_append(types, type);
44 	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE);
45 	types = g_list_append(types, type);
46 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE);
47 	types = g_list_append(types, type);
48 	type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE);
49 	types = g_list_append(types, type);
50 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE);
51 	types = g_list_append(types, type);
52 	type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE);
53 	types = g_list_append(types, type);
54 	type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE);
55 	types = g_list_append(types, type);
56 
57 	return types;
58 }
59 
60 static void
silcpurple_set_status(PurpleAccount * account,PurpleStatus * status)61 silcpurple_set_status(PurpleAccount *account, PurpleStatus *status)
62 {
63 	PurpleConnection *gc = purple_account_get_connection(account);
64 	SilcPurple sg = NULL;
65 	SilcUInt32 mode;
66 	SilcBuffer idp;
67 	unsigned char mb[4];
68 	const char *state;
69 
70 	if (gc != NULL)
71 		sg = gc->proto_data;
72 
73 	if (status == NULL)
74 		return;
75 
76 	state = purple_status_get_id(status);
77 
78 	if (state == NULL)
79 		return;
80 
81 	if ((sg == NULL) || (sg->conn == NULL))
82 		return;
83 
84 	mode = sg->conn->local_entry->mode;
85 	mode &= ~(SILC_UMODE_GONE |
86 		  SILC_UMODE_HYPER |
87 		  SILC_UMODE_BUSY |
88 		  SILC_UMODE_INDISPOSED |
89 		  SILC_UMODE_PAGE);
90 
91 	if (purple_strequal(state, "hyper"))
92 		mode |= SILC_UMODE_HYPER;
93 	else if (purple_strequal(state, "away"))
94 		mode |= SILC_UMODE_GONE;
95 	else if (purple_strequal(state, "busy"))
96 		mode |= SILC_UMODE_BUSY;
97 	else if (purple_strequal(state, "indisposed"))
98 		mode |= SILC_UMODE_INDISPOSED;
99 	else if (purple_strequal(state, "page"))
100 		mode |= SILC_UMODE_PAGE;
101 
102 	/* Send UMODE */
103 	idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT);
104 	SILC_PUT32_MSB(mode, mb);
105 	silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE,
106 				 ++sg->conn->cmd_ident, 2,
107 				 1, idp->data, idp->len,
108 				 2, mb, sizeof(mb));
109 	silc_buffer_free(idp);
110 }
111 
112 
113 /*************************** Connection Routines *****************************/
114 
115 static void
silcpurple_keepalive(PurpleConnection * gc)116 silcpurple_keepalive(PurpleConnection *gc)
117 {
118 	SilcPurple sg = gc->proto_data;
119 	silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT,
120 				NULL, 0);
121 }
122 
123 static gboolean
silcpurple_scheduler(gpointer * context)124 silcpurple_scheduler(gpointer *context)
125 {
126 	SilcPurple sg = (SilcPurple)context;
127 	silc_client_run_one(sg->client);
128 	return TRUE;
129 }
130 
131 static void
silcpurple_nickname_parse(const char * nickname,char ** ret_nickname)132 silcpurple_nickname_parse(const char *nickname,
133 			char **ret_nickname)
134 {
135 	silc_parse_userfqdn(nickname, ret_nickname, NULL);
136 }
137 
138 static void
silcpurple_login_connected(gpointer data,gint source,const gchar * error_message)139 silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
140 {
141 	PurpleConnection *gc = data;
142 	SilcPurple sg;
143 	SilcClient client;
144 	SilcClientConnection conn;
145 	PurpleAccount *account;
146 	SilcClientConnectionParams params;
147 	SilcUInt32 mask;
148 	const char *dfile, *tmp;
149 #ifdef SILC_ATTRIBUTE_USER_ICON
150 	PurpleStoredImage *img;
151 #endif
152 #ifdef HAVE_SYS_UTSNAME_H
153 	struct utsname u;
154 #endif
155 
156 
157 	g_return_if_fail(gc != NULL);
158 
159 	sg = gc->proto_data;
160 
161 	if (source < 0) {
162 		purple_connection_error_reason(gc,
163 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
164 			_("Connection failed"));
165 		return;
166 	}
167 
168 	client = sg->client;
169 	account = sg->account;
170 
171 	/* Get session detachment data, if available */
172 	memset(&params, 0, sizeof(params));
173 	dfile = silcpurple_session_file(purple_account_get_username(sg->account));
174 	params.detach_data = (unsigned char *)silc_file_readfile(dfile, &params.detach_data_len);
175 	if (params.detach_data)
176 		params.detach_data[params.detach_data_len] = 0;
177 
178 	/* Add connection to SILC client library */
179 	conn = silc_client_add_connection(
180 			  sg->client, &params,
181 			  (char *)purple_account_get_string(account, "server",
182 							  "silc.silcnet.org"),
183 			  purple_account_get_int(account, "port", 706), sg);
184 	if (!conn) {
185 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
186 			_("Unable to initialize SILC Client connection"));
187 		gc->proto_data = NULL;
188 		return;
189 	}
190 	sg->conn = conn;
191 
192 	/* Progress */
193 	if (params.detach_data) {
194 		purple_connection_update_progress(gc, _("Resuming session"), 2, 5);
195 		sg->resuming = TRUE;
196 	} else {
197 		purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5);
198 	}
199 
200 	/* Perform SILC Key Exchange.  The "silc_connected" will be called
201 	   eventually. */
202 	silc_client_start_key_exchange(sg->client, sg->conn, source);
203 
204 	/* Set default attributes */
205 	mask = SILC_ATTRIBUTE_MOOD_NORMAL;
206 	silc_client_attribute_add(client, conn,
207 				  SILC_ATTRIBUTE_STATUS_MOOD,
208 				  SILC_32_TO_PTR(mask),
209 				  sizeof(SilcUInt32));
210 	mask = SILC_ATTRIBUTE_CONTACT_CHAT;
211 	silc_client_attribute_add(client, conn,
212 				  SILC_ATTRIBUTE_PREFERRED_CONTACT,
213 				  SILC_32_TO_PTR(mask),
214 				  sizeof(SilcUInt32));
215 #ifdef HAVE_SYS_UTSNAME_H
216 	if (!uname(&u)) {
217 		SilcAttributeObjDevice dev;
218 		memset(&dev, 0, sizeof(dev));
219 		dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
220 		dev.version = u.release;
221 		dev.model = u.sysname;
222 		silc_client_attribute_add(client, conn,
223 					  SILC_ATTRIBUTE_DEVICE_INFO,
224 					  (void *)&dev, sizeof(dev));
225 	}
226 #endif
227 #ifdef _WIN32
228 	tmp = _tzname[0];
229 #else
230 	tmp = tzname[0];
231 #endif
232 	silc_client_attribute_add(client, conn,
233 				  SILC_ATTRIBUTE_TIMEZONE,
234 				  (void *)tmp, strlen(tmp));
235 
236 #ifdef SILC_ATTRIBUTE_USER_ICON
237 	/* Set our buddy icon */
238 	img = purple_buddy_icons_find_account_icon(account);
239 	silcpurple_buddy_set_icon(gc, img);
240 	purple_imgstore_unref(img);
241 #endif
242 
243 	silc_free(params.detach_data);
244 }
245 
246 static void
silcpurple_login(PurpleAccount * account)247 silcpurple_login(PurpleAccount *account)
248 {
249 	SilcPurple sg;
250 	SilcClient client;
251 	SilcClientParams params;
252 	PurpleConnection *gc;
253 	char pkd[256], prd[256];
254 	const char *cipher, *hmac;
255 	char *realname;
256 	int i;
257 
258 	gc = account->gc;
259 	if (!gc)
260 		return;
261 	gc->proto_data = NULL;
262 
263 	memset(&params, 0, sizeof(params));
264 	strcat(params.nickname_format, "%n@%h%a");
265 	params.nickname_parse = silcpurple_nickname_parse;
266 	params.ignore_requested_attributes = FALSE;
267 
268 	/* Allocate SILC client */
269 	client = silc_client_alloc(&ops, &params, gc, NULL);
270 	if (!client) {
271 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
272 		                             _("Out of memory"));
273 		return;
274 	}
275 
276 	/* Get username, real name and local hostname for SILC library */
277 	if (purple_account_get_username(account)) {
278 		const char *u = purple_account_get_username(account);
279 		char **up = g_strsplit(u, "@", 2);
280 		client->username = strdup(up[0]);
281 		g_strfreev(up);
282 	} else {
283 		client->username = silc_get_username();
284 		purple_account_set_username(account, client->username);
285 	}
286 	realname = silc_get_real_name();
287 	if (purple_account_get_user_info(account)) {
288 		client->realname = strdup(purple_account_get_user_info(account));
289 		free(realname);
290 	} else if ((silc_get_real_name() != NULL) && (*realname != '\0')) {
291 		client->realname = realname;
292 		purple_account_set_user_info(account, client->realname);
293 	} else {
294 		free(realname);
295 		client->realname = strdup(_("John Noname"));
296 	}
297 	client->hostname = silc_net_localhost();
298 
299 	purple_connection_set_display_name(gc, client->username);
300 
301 	/* Register requested cipher and HMAC */
302 	cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER);
303 	for (i = 0; silc_default_ciphers[i].name; i++)
304 		if (purple_strequal(silc_default_ciphers[i].name, cipher)) {
305 			silc_cipher_register(&(silc_default_ciphers[i]));
306 			break;
307 		}
308 	hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC);
309 	for (i = 0; silc_default_hmacs[i].name; i++)
310 		if (purple_strequal(silc_default_hmacs[i].name, hmac)) {
311 			silc_hmac_register(&(silc_default_hmacs[i]));
312 			break;
313 		}
314 
315 	/* Init SILC client */
316 	if (!silc_client_init(client)) {
317 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
318 		                             _("Unable to initialize SILC protocol"));
319 		return;
320 	}
321 
322 	/* Check the ~/.silc dir and create it, and new key pair if necessary. */
323 	if (!silcpurple_check_silc_dir(gc)) {
324 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
325 		                             _("Error loading SILC key pair"));
326 		return;
327 	}
328 
329 	/* Progress */
330 	purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5);
331 
332 	/* Load SILC key pair */
333 	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
334 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
335 	if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd),
336 							(char *)purple_account_get_string(account, "private-key", prd),
337 				(gc->password == NULL) ? "" : gc->password, &client->pkcs,
338 				&client->public_key, &client->private_key)) {
339 		g_snprintf(pkd, sizeof(pkd), _("Unable to load SILC key pair: %s"), g_strerror(errno));
340 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
341 		                             pkd);
342 		return;
343 	}
344 
345 	sg = silc_calloc(1, sizeof(*sg));
346 	if (!sg)
347 		return;
348 	memset(sg, 0, sizeof(*sg));
349 	sg->client = client;
350 	sg->gc = gc;
351 	sg->account = account;
352 	gc->proto_data = sg;
353 
354 	/* Connect to the SILC server */
355 	if (purple_proxy_connect(gc, account,
356 			       purple_account_get_string(account, "server",
357 						       "silc.silcnet.org"),
358 			       purple_account_get_int(account, "port", 706),
359 			       silcpurple_login_connected, gc) == NULL)
360 	{
361 		purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
362 		                             _("Unable to create connection"));
363 		return;
364 	}
365 
366 	/* Schedule SILC using Glib's event loop */
367 	sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg);
368 }
369 
370 static int
silcpurple_close_final(gpointer * context)371 silcpurple_close_final(gpointer *context)
372 {
373 	SilcPurple sg = (SilcPurple)context;
374 	silc_client_stop(sg->client);
375 	silc_client_free(sg->client);
376 #ifdef HAVE_SILCMIME_H
377 	if (sg->mimeass)
378 		silc_mime_assembler_free(sg->mimeass);
379 #endif
380 	silc_free(sg);
381 	return 0;
382 }
383 
384 static void
silcpurple_close(PurpleConnection * gc)385 silcpurple_close(PurpleConnection *gc)
386 {
387 	SilcPurple sg = gc->proto_data;
388 	GHashTable *ui_info;
389 	const char *ui_name = NULL, *ui_website = NULL;
390 	char *quit_msg;
391 
392 	g_return_if_fail(sg != NULL);
393 
394 	ui_info = purple_core_get_ui_info();
395 
396 	if(ui_info) {
397 		ui_name = g_hash_table_lookup(ui_info, "name");
398 		ui_website = g_hash_table_lookup(ui_info, "website");
399 	}
400 
401 	if(!ui_name || !ui_website) {
402 		ui_name = "Pidgin";
403 		ui_website = PURPLE_WEBSITE;
404 	}
405 	quit_msg = g_strdup_printf(_("Download %s: %s"),
406 							   ui_name, ui_website);
407 
408 	/* Send QUIT */
409 	silc_client_command_call(sg->client, sg->conn, NULL,
410 				 "QUIT", quit_msg, NULL);
411 	g_free(quit_msg);
412 
413 	if (sg->conn)
414 		silc_client_close_connection(sg->client, sg->conn);
415 
416 	purple_timeout_remove(sg->scheduler);
417 	purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg);
418 }
419 
420 
421 /****************************** Protocol Actions *****************************/
422 
423 static void
silcpurple_attrs_cancel(PurpleConnection * gc,PurpleRequestFields * fields)424 silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
425 {
426 	/* Nothing */
427 }
428 
429 static void
silcpurple_attrs_cb(PurpleConnection * gc,PurpleRequestFields * fields)430 silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields)
431 {
432 	SilcPurple sg = gc->proto_data;
433 	SilcClient client = sg->client;
434 	SilcClientConnection conn = sg->conn;
435 	PurpleRequestField *f;
436 	char *tmp;
437 	SilcUInt32 tmp_len, mask;
438 	SilcAttributeObjService service;
439 	SilcAttributeObjDevice dev;
440 	SilcVCardStruct vcard;
441 	const char *val;
442 
443 	sg = gc->proto_data;
444 	if (!sg)
445 		return;
446 
447 	memset(&service, 0, sizeof(service));
448 	memset(&dev, 0, sizeof(dev));
449 	memset(&vcard, 0, sizeof(vcard));
450 
451 	silc_client_attribute_del(client, conn,
452 				  SILC_ATTRIBUTE_USER_INFO, NULL);
453 	silc_client_attribute_del(client, conn,
454 				  SILC_ATTRIBUTE_SERVICE, NULL);
455 	silc_client_attribute_del(client, conn,
456 				  SILC_ATTRIBUTE_STATUS_MOOD, NULL);
457 	silc_client_attribute_del(client, conn,
458 				  SILC_ATTRIBUTE_STATUS_FREETEXT, NULL);
459 	silc_client_attribute_del(client, conn,
460 				  SILC_ATTRIBUTE_STATUS_MESSAGE, NULL);
461 	silc_client_attribute_del(client, conn,
462 				  SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL);
463 	silc_client_attribute_del(client, conn,
464 				  SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL);
465 	silc_client_attribute_del(client, conn,
466 				  SILC_ATTRIBUTE_TIMEZONE, NULL);
467 	silc_client_attribute_del(client, conn,
468 				  SILC_ATTRIBUTE_GEOLOCATION, NULL);
469 	silc_client_attribute_del(client, conn,
470 				  SILC_ATTRIBUTE_DEVICE_INFO, NULL);
471 
472 	/* Set mood */
473 	mask = 0;
474 	f = purple_request_fields_get_field(fields, "mood_normal");
475 	if (f && purple_request_field_bool_get_value(f))
476 		mask |= SILC_ATTRIBUTE_MOOD_NORMAL;
477 	f = purple_request_fields_get_field(fields, "mood_happy");
478 	if (f && purple_request_field_bool_get_value(f))
479 		mask |= SILC_ATTRIBUTE_MOOD_HAPPY;
480 	f = purple_request_fields_get_field(fields, "mood_sad");
481 	if (f && purple_request_field_bool_get_value(f))
482 		mask |= SILC_ATTRIBUTE_MOOD_SAD;
483 	f = purple_request_fields_get_field(fields, "mood_angry");
484 	if (f && purple_request_field_bool_get_value(f))
485 		mask |= SILC_ATTRIBUTE_MOOD_ANGRY;
486 	f = purple_request_fields_get_field(fields, "mood_jealous");
487 	if (f && purple_request_field_bool_get_value(f))
488 		mask |= SILC_ATTRIBUTE_MOOD_JEALOUS;
489 	f = purple_request_fields_get_field(fields, "mood_ashamed");
490 	if (f && purple_request_field_bool_get_value(f))
491 		mask |= SILC_ATTRIBUTE_MOOD_ASHAMED;
492 	f = purple_request_fields_get_field(fields, "mood_invincible");
493 	if (f && purple_request_field_bool_get_value(f))
494 		mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE;
495 	f = purple_request_fields_get_field(fields, "mood_inlove");
496 	if (f && purple_request_field_bool_get_value(f))
497 		mask |= SILC_ATTRIBUTE_MOOD_INLOVE;
498 	f = purple_request_fields_get_field(fields, "mood_sleepy");
499 	if (f && purple_request_field_bool_get_value(f))
500 		mask |= SILC_ATTRIBUTE_MOOD_SLEEPY;
501 	f = purple_request_fields_get_field(fields, "mood_bored");
502 	if (f && purple_request_field_bool_get_value(f))
503 		mask |= SILC_ATTRIBUTE_MOOD_BORED;
504 	f = purple_request_fields_get_field(fields, "mood_excited");
505 	if (f && purple_request_field_bool_get_value(f))
506 		mask |= SILC_ATTRIBUTE_MOOD_EXCITED;
507 	f = purple_request_fields_get_field(fields, "mood_anxious");
508 	if (f && purple_request_field_bool_get_value(f))
509 		mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS;
510 	silc_client_attribute_add(client, conn,
511 				  SILC_ATTRIBUTE_STATUS_MOOD,
512 				  SILC_32_TO_PTR(mask),
513 				  sizeof(SilcUInt32));
514 
515 	/* Set preferred contact */
516 	mask = 0;
517 	f = purple_request_fields_get_field(fields, "contact_chat");
518 	if (f && purple_request_field_bool_get_value(f))
519 		mask |= SILC_ATTRIBUTE_CONTACT_CHAT;
520 	f = purple_request_fields_get_field(fields, "contact_email");
521 	if (f && purple_request_field_bool_get_value(f))
522 		mask |= SILC_ATTRIBUTE_CONTACT_EMAIL;
523 	f = purple_request_fields_get_field(fields, "contact_call");
524 	if (f && purple_request_field_bool_get_value(f))
525 		mask |= SILC_ATTRIBUTE_CONTACT_CALL;
526 	f = purple_request_fields_get_field(fields, "contact_sms");
527 	if (f && purple_request_field_bool_get_value(f))
528 		mask |= SILC_ATTRIBUTE_CONTACT_SMS;
529 	f = purple_request_fields_get_field(fields, "contact_mms");
530 	if (f && purple_request_field_bool_get_value(f))
531 		mask |= SILC_ATTRIBUTE_CONTACT_MMS;
532 	f = purple_request_fields_get_field(fields, "contact_video");
533 	if (f && purple_request_field_bool_get_value(f))
534 		mask |= SILC_ATTRIBUTE_CONTACT_VIDEO;
535 	if (mask)
536 		silc_client_attribute_add(client, conn,
537 					  SILC_ATTRIBUTE_PREFERRED_CONTACT,
538 					  SILC_32_TO_PTR(mask),
539 					  sizeof(SilcUInt32));
540 
541 	/* Set status text */
542 	val = NULL;
543 	f = purple_request_fields_get_field(fields, "status_text");
544 	if (f)
545 		val = purple_request_field_string_get_value(f);
546 	if (val && *val)
547 		silc_client_attribute_add(client, conn,
548 					  SILC_ATTRIBUTE_STATUS_FREETEXT,
549 					  (void *)val, strlen(val));
550 
551 	/* Set vcard */
552 	val = NULL;
553 	f = purple_request_fields_get_field(fields, "vcard");
554 	if (f)
555 		val = purple_request_field_string_get_value(f);
556 	if (val && *val) {
557 		purple_account_set_string(sg->account, "vcard", val);
558 		tmp = silc_file_readfile(val, &tmp_len);
559 		if (tmp) {
560 			tmp[tmp_len] = 0;
561 			if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard))
562 				silc_client_attribute_add(client, conn,
563 							  SILC_ATTRIBUTE_USER_INFO,
564 							  (void *)&vcard,
565 							  sizeof(vcard));
566 		}
567 		silc_vcard_free(&vcard);
568 		silc_free(tmp);
569 	} else {
570 		purple_account_set_string(sg->account, "vcard", "");
571 	}
572 
573 #ifdef HAVE_SYS_UTSNAME_H
574 	/* Set device info */
575 	f = purple_request_fields_get_field(fields, "device");
576 	if (f && purple_request_field_bool_get_value(f)) {
577 		struct utsname u;
578 		if (!uname(&u)) {
579 			dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER;
580 			dev.version = u.release;
581 			dev.model = u.sysname;
582 			silc_client_attribute_add(client, conn,
583 						  SILC_ATTRIBUTE_DEVICE_INFO,
584 						  (void *)&dev, sizeof(dev));
585 		}
586 	}
587 #endif
588 
589 	/* Set timezone */
590 	val = NULL;
591 	f = purple_request_fields_get_field(fields, "timezone");
592 	if (f)
593 		val = purple_request_field_string_get_value(f);
594 	if (val && *val)
595 		silc_client_attribute_add(client, conn,
596 					  SILC_ATTRIBUTE_TIMEZONE,
597 					  (void *)val, strlen(val));
598 }
599 
600 static void
silcpurple_attrs(PurplePluginAction * action)601 silcpurple_attrs(PurplePluginAction *action)
602 {
603 	PurpleConnection *gc = (PurpleConnection *) action->context;
604 	SilcPurple sg = gc->proto_data;
605 	SilcClient client = sg->client;
606 	SilcClientConnection conn = sg->conn;
607 	PurpleRequestFields *fields;
608 	PurpleRequestFieldGroup *g;
609 	PurpleRequestField *f;
610 	SilcHashTable attrs;
611 	SilcAttributePayload attr;
612 	gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE,
613 		mangry = FALSE, mjealous = FALSE, mashamed = FALSE,
614 		minvincible = FALSE, minlove = FALSE, msleepy = FALSE,
615 		mbored = FALSE, mexcited = FALSE, manxious = FALSE;
616 	gboolean cemail = FALSE, ccall = FALSE, csms = FALSE,
617 		cmms = FALSE, cchat = TRUE, cvideo = FALSE;
618 	gboolean device = TRUE;
619 	char status[1024];
620 
621 	sg = gc->proto_data;
622 	if (!sg)
623 		return;
624 
625 	memset(status, 0, sizeof(status));
626 
627 	attrs = silc_client_attributes_get(client, conn);
628 	if (attrs) {
629 		if (silc_hash_table_find(attrs,
630 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD),
631 					 NULL, (void *)&attr)) {
632 			SilcUInt32 mood = 0;
633 			silc_attribute_get_object(attr, &mood, sizeof(mood));
634 			mnormal = !mood;
635 			mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY);
636 			msad = (mood & SILC_ATTRIBUTE_MOOD_SAD);
637 			mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY);
638 			mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS);
639 			mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED);
640 			minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE);
641 			minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE);
642 			msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY);
643 			mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED);
644 			mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED);
645 			manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS);
646 		}
647 
648 		if (silc_hash_table_find(attrs,
649 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT),
650 					 NULL, (void *)&attr)) {
651 			SilcUInt32 contact = 0;
652 			silc_attribute_get_object(attr, &contact, sizeof(contact));
653 			cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL);
654 			ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL);
655 			csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS);
656 			cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS);
657 			cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT);
658 			cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO);
659 		}
660 
661 		if (silc_hash_table_find(attrs,
662 					 SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT),
663 					 NULL, (void *)&attr))
664 			silc_attribute_get_object(attr, &status, sizeof(status));
665 
666 		if (!silc_hash_table_find(attrs,
667 					  SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO),
668 					  NULL, (void *)&attr))
669 			device = FALSE;
670 	}
671 
672 	fields = purple_request_fields_new();
673 
674 	g = purple_request_field_group_new(NULL);
675 	f = purple_request_field_label_new("l3", _("Your Current Mood"));
676 	purple_request_field_group_add_field(g, f);
677 	f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal);
678 	purple_request_field_group_add_field(g, f);
679 	f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy);
680 	purple_request_field_group_add_field(g, f);
681 	f = purple_request_field_bool_new("mood_sad", _("Sad"), msad);
682 	purple_request_field_group_add_field(g, f);
683 	f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry);
684 	purple_request_field_group_add_field(g, f);
685 	f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous);
686 	purple_request_field_group_add_field(g, f);
687 	f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed);
688 	purple_request_field_group_add_field(g, f);
689 	f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible);
690 	purple_request_field_group_add_field(g, f);
691 	f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove);
692 	purple_request_field_group_add_field(g, f);
693 	f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy);
694 	purple_request_field_group_add_field(g, f);
695 	f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored);
696 	purple_request_field_group_add_field(g, f);
697 	f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited);
698 	purple_request_field_group_add_field(g, f);
699 	f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious);
700 	purple_request_field_group_add_field(g, f);
701 
702 	f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods"));
703 	purple_request_field_group_add_field(g, f);
704 	f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat);
705 	purple_request_field_group_add_field(g, f);
706 	f = purple_request_field_bool_new("contact_email", _("Email"), cemail);
707 	purple_request_field_group_add_field(g, f);
708 	f = purple_request_field_bool_new("contact_call", _("Phone"), ccall);
709 	purple_request_field_group_add_field(g, f);
710 	f = purple_request_field_bool_new("contact_sms", _("SMS"), csms);
711 	purple_request_field_group_add_field(g, f);
712 	f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms);
713 	purple_request_field_group_add_field(g, f);
714 	f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo);
715 	purple_request_field_group_add_field(g, f);
716 	purple_request_fields_add_group(fields, g);
717 
718 	g = purple_request_field_group_new(NULL);
719 	f = purple_request_field_string_new("status_text", _("Your Current Status"),
720 					  status[0] ? status : NULL, TRUE);
721 	purple_request_field_group_add_field(g, f);
722 	purple_request_fields_add_group(fields, g);
723 
724 	g = purple_request_field_group_new(NULL);
725 #if 0
726 	f = purple_request_field_label_new("l2", _("Online Services"));
727 	purple_request_field_group_add_field(g, f);
728 	f = purple_request_field_bool_new("services",
729 					_("Let others see what services you are using"),
730 					TRUE);
731 	purple_request_field_group_add_field(g, f);
732 #endif
733 #ifdef HAVE_SYS_UTSNAME_H
734 	f = purple_request_field_bool_new("device",
735 					_("Let others see what computer you are using"),
736 					device);
737 	purple_request_field_group_add_field(g, f);
738 #endif
739 	purple_request_fields_add_group(fields, g);
740 
741 	g = purple_request_field_group_new(NULL);
742 	f = purple_request_field_string_new("vcard", _("Your VCard File"),
743 					  purple_account_get_string(sg->account, "vcard", ""),
744 					  FALSE);
745 	purple_request_field_group_add_field(g, f);
746 #ifdef _WIN32
747 	f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE);
748 #else
749 	f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE);
750 #endif
751 	purple_request_field_group_add_field(g, f);
752 	purple_request_fields_add_group(fields, g);
753 
754 	purple_request_fields(gc, _("User Online Status Attributes"),
755 			    _("User Online Status Attributes"),
756 			    _("You can let other users see your online status information "
757 			      "and your personal information. Please fill the information "
758 			      "you would like other users to see about yourself."),
759 			    fields,
760 			    _("OK"), G_CALLBACK(silcpurple_attrs_cb),
761 			    _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel),
762 				gc->account, NULL, NULL, gc);
763 }
764 
765 static void
silcpurple_detach(PurplePluginAction * action)766 silcpurple_detach(PurplePluginAction *action)
767 {
768 	PurpleConnection *gc = (PurpleConnection *) action->context;
769 	SilcPurple sg;
770 
771 	if (!gc)
772 		return;
773 	sg = gc->proto_data;
774 	if (!sg)
775 		return;
776 
777 	/* Call DETACH */
778 	silc_client_command_call(sg->client, sg->conn, "DETACH");
779 	sg->detaching = TRUE;
780 }
781 
782 static void
silcpurple_view_motd(PurplePluginAction * action)783 silcpurple_view_motd(PurplePluginAction *action)
784 {
785 	PurpleConnection *gc = (PurpleConnection *) action->context;
786 	SilcPurple sg;
787 	char *tmp;
788 
789 	if (!gc)
790 		return;
791 	sg = gc->proto_data;
792 	if (!sg)
793 		return;
794 
795 	if (!sg->motd) {
796 		purple_notify_error(
797 		     gc, _("Message of the Day"), _("No Message of the Day available"),
798 		     _("There is no Message of the Day associated with this connection"));
799 		return;
800 	}
801 
802 	tmp = g_markup_escape_text(sg->motd, -1);
803 	purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
804 			      tmp, NULL, NULL);
805 	g_free(tmp);
806 }
807 
808 static void
silcpurple_create_keypair_cancel(PurpleConnection * gc,PurpleRequestFields * fields)809 silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields)
810 {
811 	/* Nothing */
812 }
813 
814 static void
silcpurple_create_keypair_cb(PurpleConnection * gc,PurpleRequestFields * fields)815 silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields)
816 {
817 	SilcPurple sg = gc->proto_data;
818 	PurpleRequestField *f;
819 	const char *val, *pkfile = NULL, *prfile = NULL;
820 	const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL;
821 	const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL;
822 	char *identifier;
823 	int keylen = SILCPURPLE_DEF_PKCS_LEN;
824 	SilcPublicKey public_key;
825 
826 	sg = gc->proto_data;
827 	if (!sg)
828 		return;
829 
830 	val = NULL;
831 	f = purple_request_fields_get_field(fields, "pass1");
832 	if (f)
833 		val = purple_request_field_string_get_value(f);
834 	if (val && *val)
835 		pass1 = val;
836 	else
837 		pass1 = "";
838 	val = NULL;
839 	f = purple_request_fields_get_field(fields, "pass2");
840 	if (f)
841 		val = purple_request_field_string_get_value(f);
842 	if (val && *val)
843 		pass2 = val;
844 	else
845 		pass2 = "";
846 
847 	if (!purple_strequal(pass1, pass2)) {
848 		purple_notify_error(
849 		     gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL);
850 		return;
851 	}
852 
853 	val = NULL;
854 	f = purple_request_fields_get_field(fields, "key");
855 	if (f)
856 		val = purple_request_field_string_get_value(f);
857 	if (val && *val)
858 		keylen = atoi(val);
859 	f = purple_request_fields_get_field(fields, "pkfile");
860 	if (f)
861 		pkfile = purple_request_field_string_get_value(f);
862 	f = purple_request_fields_get_field(fields, "prfile");
863 	if (f)
864 		prfile = purple_request_field_string_get_value(f);
865 
866 	f = purple_request_fields_get_field(fields, "un");
867 	if (f)
868 		un = purple_request_field_string_get_value(f);
869 	f = purple_request_fields_get_field(fields, "hn");
870 	if (f)
871 		hn = purple_request_field_string_get_value(f);
872 	f = purple_request_fields_get_field(fields, "rn");
873 	if (f)
874 		rn = purple_request_field_string_get_value(f);
875 	f = purple_request_fields_get_field(fields, "e");
876 	if (f)
877 		e = purple_request_field_string_get_value(f);
878 	f = purple_request_fields_get_field(fields, "o");
879 	if (f)
880 		o = purple_request_field_string_get_value(f);
881 	f = purple_request_fields_get_field(fields, "c");
882 	if (f)
883 		c = purple_request_field_string_get_value(f);
884 
885 	identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn,
886 						 (char *)rn, (char *)e, (char *)o, (char *)c);
887 
888 	/* Create the key pair */
889 	if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile,
890 				  identifier, pass1, NULL, &public_key, NULL,
891 				  FALSE)) {
892 		purple_notify_error(
893 		     gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL);
894 		return;
895 	}
896 
897 	silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL);
898 
899 	silc_pkcs_public_key_free(public_key);
900 	silc_free(identifier);
901 }
902 
903 static void
silcpurple_create_keypair(PurplePluginAction * action)904 silcpurple_create_keypair(PurplePluginAction *action)
905 {
906 	PurpleConnection *gc = (PurpleConnection *) action->context;
907 	SilcPurple sg = gc->proto_data;
908 	PurpleRequestFields *fields;
909 	PurpleRequestFieldGroup *g;
910 	PurpleRequestField *f;
911 	const char *username, *realname;
912 	char *hostname, **u;
913 	char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256];
914 
915 	username = purple_account_get_username(sg->account);
916 	u = g_strsplit(username, "@", 2);
917 	username = u[0];
918 	realname = purple_account_get_user_info(sg->account);
919 	hostname = silc_net_localhost();
920 	g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname);
921 
922 	g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir());
923 	g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir());
924 	g_snprintf(pkd, sizeof(pkd) - 1, "%s",
925 		   purple_account_get_string(gc->account, "public-key", pkd2));
926 	g_snprintf(prd, sizeof(prd) - 1, "%s",
927 		   purple_account_get_string(gc->account, "private-key", prd2));
928 
929 	fields = purple_request_fields_new();
930 
931 	g = purple_request_field_group_new(NULL);
932 	f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE);
933 	purple_request_field_group_add_field(g, f);
934 	f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE);
935 	purple_request_field_group_add_field(g, f);
936 	f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE);
937 	purple_request_field_group_add_field(g, f);
938 	purple_request_fields_add_group(fields, g);
939 
940 	g = purple_request_field_group_new(NULL);
941 	f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE);
942 	purple_request_field_group_add_field(g, f);
943 	f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE);
944 	purple_request_field_group_add_field(g, f);
945 	f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE);
946 	purple_request_field_group_add_field(g, f);
947 	f = purple_request_field_string_new("e", _("Email"), tmp, FALSE);
948 	purple_request_field_group_add_field(g, f);
949 	f = purple_request_field_string_new("o", _("Organization"), "", FALSE);
950 	purple_request_field_group_add_field(g, f);
951 	f = purple_request_field_string_new("c", _("Country"), "", FALSE);
952 	purple_request_field_group_add_field(g, f);
953 	purple_request_fields_add_group(fields, g);
954 
955 	g = purple_request_field_group_new(NULL);
956 	f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE);
957 	purple_request_field_string_set_masked(f, TRUE);
958 	purple_request_field_group_add_field(g, f);
959 	f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE);
960 	purple_request_field_string_set_masked(f, TRUE);
961 	purple_request_field_group_add_field(g, f);
962 	purple_request_fields_add_group(fields, g);
963 
964 	purple_request_fields(gc, _("Create New SILC Key Pair"),
965 			    _("Create New SILC Key Pair"), NULL, fields,
966 			    _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb),
967 			    _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel),
968 				gc->account, NULL, NULL, gc);
969 
970 	g_strfreev(u);
971 	silc_free(hostname);
972 }
973 
974 static void
silcpurple_change_pass(PurplePluginAction * action)975 silcpurple_change_pass(PurplePluginAction *action)
976 {
977 	PurpleConnection *gc = (PurpleConnection *) action->context;
978 	purple_account_request_change_password(purple_connection_get_account(gc));
979 }
980 
981 static void
silcpurple_change_passwd(PurpleConnection * gc,const char * old,const char * new)982 silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new)
983 {
984 	char prd[256];
985 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir());
986 	silc_change_private_key_passphrase(purple_account_get_string(gc->account,
987 								   "private-key",
988 								   prd), old ? old : "", new ? new : "");
989 }
990 
991 static void
silcpurple_show_set_info(PurplePluginAction * action)992 silcpurple_show_set_info(PurplePluginAction *action)
993 {
994 	PurpleConnection *gc = (PurpleConnection *) action->context;
995 	purple_account_request_change_user_info(purple_connection_get_account(gc));
996 }
997 
998 static void
silcpurple_set_info(PurpleConnection * gc,const char * text)999 silcpurple_set_info(PurpleConnection *gc, const char *text)
1000 {
1001 }
1002 
1003 static GList *
silcpurple_actions(PurplePlugin * plugin,gpointer context)1004 silcpurple_actions(PurplePlugin *plugin, gpointer context)
1005 {
1006 	GList *list = NULL;
1007 	PurplePluginAction *act;
1008 
1009 	act = purple_plugin_action_new(_("Online Status"),
1010 			silcpurple_attrs);
1011 	list = g_list_append(list, act);
1012 
1013 	act = purple_plugin_action_new(_("Detach From Server"),
1014 			silcpurple_detach);
1015 	list = g_list_append(list, act);
1016 
1017 	act = purple_plugin_action_new(_("View Message of the Day"),
1018 			silcpurple_view_motd);
1019 	list = g_list_append(list, act);
1020 
1021 	act = purple_plugin_action_new(_("Create SILC Key Pair..."),
1022 			silcpurple_create_keypair);
1023 	list = g_list_append(list, act);
1024 
1025 	act = purple_plugin_action_new(_("Change Password..."),
1026 			silcpurple_change_pass);
1027 	list = g_list_append(list, act);
1028 
1029 	act = purple_plugin_action_new(_("Set User Info..."),
1030 			silcpurple_show_set_info);
1031 	list = g_list_append(list, act);
1032 
1033 	return list;
1034 }
1035 
1036 
1037 /******************************* IM Routines *********************************/
1038 
1039 typedef struct {
1040 	char *nick;
1041 	char *message;
1042 	SilcUInt32 message_len;
1043 	SilcMessageFlags flags;
1044 	PurpleMessageFlags gflags;
1045 } *SilcPurpleIM;
1046 
1047 static void
silcpurple_send_im_resolved(SilcClient client,SilcClientConnection conn,SilcClientEntry * clients,SilcUInt32 clients_count,void * context)1048 silcpurple_send_im_resolved(SilcClient client,
1049 			  SilcClientConnection conn,
1050 			  SilcClientEntry *clients,
1051 			  SilcUInt32 clients_count,
1052 			  void *context)
1053 {
1054 	PurpleConnection *gc = client->application;
1055 	SilcPurple sg = gc->proto_data;
1056 	SilcPurpleIM im = context;
1057 	PurpleConversation *convo;
1058 	char tmp[256], *nickname = NULL;
1059 	SilcClientEntry client_entry;
1060 #ifdef HAVE_SILCMIME_H
1061 	SilcDList list;
1062 #endif
1063 
1064 	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick,
1065 							sg->account);
1066 	if (!convo)
1067 		return;
1068 
1069 	if (!clients)
1070 		goto err;
1071 
1072 	if (clients_count > 1) {
1073 		silc_parse_userfqdn(im->nick, &nickname, NULL);
1074 
1075 		/* Find the correct one. The im->nick might be a formatted nick
1076 		   so this will find the correct one. */
1077 		clients = silc_client_get_clients_local(client, conn,
1078 							nickname, im->nick,
1079 							&clients_count);
1080 		if (!clients)
1081 			goto err;
1082 		client_entry = clients[0];
1083 		silc_free(clients);
1084 	} else {
1085 		client_entry = clients[0];
1086 	}
1087 
1088 #ifdef HAVE_SILCMIME_H
1089 	/* Check for images */
1090 	if (im->gflags & PURPLE_MESSAGE_IMAGES) {
1091 		list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags);
1092 		if (list) {
1093 			/* Send one or more MIME message.  If more than one, they
1094 			   are MIME fragments due to over large message */
1095 			SilcBuffer buf;
1096 
1097 			silc_dlist_start(list);
1098 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1099 				silc_client_send_private_message(client, conn,
1100 								 client_entry, im->flags,
1101 								 buf->data, buf->len,
1102 								 TRUE);
1103 			silc_mime_partial_free(list);
1104 			purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
1105 				   im->message, 0, time(NULL));
1106 			goto out;
1107 		}
1108 	}
1109 #endif
1110 
1111 	/* Send the message */
1112 	silc_client_send_private_message(client, conn, client_entry, im->flags,
1113 					 (unsigned char *)im->message, im->message_len, TRUE);
1114 	purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname,
1115 			   im->message, 0, time(NULL));
1116 	goto out;
1117 
1118  err:
1119 	g_snprintf(tmp, sizeof(tmp),
1120 		   _("User <I>%s</I> is not present in the network"), im->nick);
1121 	purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
1122 
1123  out:
1124 	g_free(im->nick);
1125 	g_free(im->message);
1126 	silc_free(im);
1127 	silc_free(nickname);
1128 }
1129 
1130 static int
silcpurple_send_im(PurpleConnection * gc,const char * who,const char * message,PurpleMessageFlags flags)1131 silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message,
1132 		 PurpleMessageFlags flags)
1133 {
1134 	SilcPurple sg = gc->proto_data;
1135 	SilcClient client = sg->client;
1136 	SilcClientConnection conn = sg->conn;
1137 	SilcClientEntry *clients;
1138 	SilcUInt32 clients_count, mflags;
1139 	char *nickname, *msg, *tmp;
1140 	int ret = 0;
1141 	gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE);
1142 #ifdef HAVE_SILCMIME_H
1143 	SilcDList list;
1144 #endif
1145 
1146 	if (!who || !message)
1147 		return 0;
1148 
1149 	mflags = SILC_MESSAGE_FLAG_UTF8;
1150 
1151 	tmp = msg = purple_unescape_html(message);
1152 
1153 	if (!g_ascii_strncasecmp(msg, "/me ", 4)) {
1154 		msg += 4;
1155 		if (!*msg) {
1156 			g_free(tmp);
1157 			return 0;
1158 		}
1159 		mflags |= SILC_MESSAGE_FLAG_ACTION;
1160 	} else if (strlen(msg) > 1 && msg[0] == '/') {
1161 		if (!silc_client_command_call(client, conn, msg + 1))
1162 			purple_notify_error(gc, _("Call Command"), _("Cannot call command"),
1163 					_("Unknown command"));
1164 		g_free(tmp);
1165 		return 0;
1166 	}
1167 
1168 
1169 	if (!silc_parse_userfqdn(who, &nickname, NULL)) {
1170 		g_free(tmp);
1171 		return 0;
1172 	}
1173 
1174 	if (sign)
1175 		mflags |= SILC_MESSAGE_FLAG_SIGNED;
1176 
1177 	/* Find client entry */
1178 	clients = silc_client_get_clients_local(client, conn, nickname, who,
1179 						&clients_count);
1180 	if (!clients) {
1181 		/* Resolve unknown user */
1182 		SilcPurpleIM im = silc_calloc(1, sizeof(*im));
1183 		if (!im) {
1184 			g_free(tmp);
1185 			return 0;
1186 		}
1187 		im->nick = g_strdup(who);
1188 		im->message = g_strdup(message);
1189 		im->message_len = strlen(im->message);
1190 		im->flags = mflags;
1191 		im->gflags = flags;
1192 		silc_client_get_clients(client, conn, nickname, NULL,
1193 					silcpurple_send_im_resolved, im);
1194 		silc_free(nickname);
1195 		g_free(tmp);
1196 		return 0;
1197 	}
1198 
1199 #ifdef HAVE_SILCMIME_H
1200 	/* Check for images */
1201 	if (flags & PURPLE_MESSAGE_IMAGES) {
1202 		list = silcpurple_image_message(message, &mflags);
1203 		if (list) {
1204 			/* Send one or more MIME message.  If more than one, they
1205 			   are MIME fragments due to over large message */
1206 			SilcBuffer buf;
1207 
1208 			silc_dlist_start(list);
1209 			while ((buf = silc_dlist_get(list)) != SILC_LIST_END)
1210 				ret =
1211 			 	silc_client_send_private_message(client, conn,
1212 								 clients[0], mflags,
1213 								 buf->data, buf->len,
1214 								 TRUE);
1215 			silc_mime_partial_free(list);
1216 			g_free(tmp);
1217 			silc_free(nickname);
1218 			silc_free(clients);
1219 			return ret;
1220 		}
1221 	}
1222 #endif
1223 
1224 	/* Send private message directly */
1225 	ret = silc_client_send_private_message(client, conn, clients[0],
1226 					       mflags,
1227 					       (unsigned char *)msg,
1228 					       strlen(msg), TRUE);
1229 
1230 	g_free(tmp);
1231 	silc_free(nickname);
1232 	silc_free(clients);
1233 	return ret;
1234 }
1235 
1236 
silcpurple_blist_node_menu(PurpleBlistNode * node)1237 static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) {
1238 	/* split this single menu building function back into the two
1239 	   original: one for buddies and one for chats */
1240 
1241 	if(PURPLE_BLIST_NODE_IS_CHAT(node)) {
1242 		return silcpurple_chat_menu((PurpleChat *) node);
1243 	} else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
1244 		return silcpurple_buddy_menu((PurpleBuddy *) node);
1245 	} else {
1246 		g_return_val_if_reached(NULL);
1247 	}
1248 }
1249 
1250 /********************************* Commands **********************************/
1251 
silcpurple_cmd_chat_part(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1252 static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv,
1253 		const char *cmd, char **args, char **error, void *data)
1254 {
1255 	PurpleConnection *gc;
1256 	PurpleConversation *convo = conv;
1257 	int id = 0;
1258 
1259 	gc = purple_conversation_get_gc(conv);
1260 
1261 	if (gc == NULL)
1262 		return PURPLE_CMD_RET_FAILED;
1263 
1264 	if(args && args[0])
1265 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0],
1266 									gc->account);
1267 
1268 	if (convo != NULL)
1269 		id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo));
1270 
1271 	if (id == 0)
1272 		return PURPLE_CMD_RET_FAILED;
1273 
1274 	silcpurple_chat_leave(gc, id);
1275 
1276 	return PURPLE_CMD_RET_OK;
1277 
1278 }
1279 
silcpurple_cmd_chat_topic(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1280 static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv,
1281 		const char *cmd, char **args, char **error, void *data)
1282 {
1283 	PurpleConnection *gc;
1284 	int id = 0;
1285 	char *buf, *tmp, *tmp2;
1286 	const char *topic;
1287 
1288 	gc = purple_conversation_get_gc(conv);
1289 	id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv));
1290 
1291 	if (gc == NULL || id == 0)
1292 		return PURPLE_CMD_RET_FAILED;
1293 
1294 	if (!args || !args[0]) {
1295 		topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv));
1296 		if (topic) {
1297 			tmp = g_markup_escape_text(topic, -1);
1298 			tmp2 = purple_markup_linkify(tmp);
1299 			buf = g_strdup_printf(_("current topic is: %s"), tmp2);
1300 			g_free(tmp);
1301 			g_free(tmp2);
1302 		} else
1303 			buf = g_strdup(_("No topic is set"));
1304 		purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf,
1305 							 PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
1306 		g_free(buf);
1307 
1308 	}
1309 
1310 	if (args && args[0] && (strlen(args[0]) > 255)) {
1311 		*error = g_strdup(_("Topic too long"));
1312 		return PURPLE_CMD_RET_FAILED;
1313 	}
1314 
1315 	silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL);
1316 
1317 	return PURPLE_CMD_RET_OK;
1318 }
1319 
silcpurple_cmd_chat_join(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1320 static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv,
1321         const char *cmd, char **args, char **error, void *data)
1322 {
1323 	GHashTable *comp;
1324 
1325 	if(!args || !args[0])
1326 		return PURPLE_CMD_RET_FAILED;
1327 
1328 	comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
1329 
1330 	g_hash_table_replace(comp, "channel", args[0]);
1331 	if(args[1])
1332 		g_hash_table_replace(comp, "passphrase", args[1]);
1333 
1334 	silcpurple_chat_join(purple_conversation_get_gc(conv), comp);
1335 
1336 	g_hash_table_destroy(comp);
1337 	return PURPLE_CMD_RET_OK;
1338 }
1339 
silcpurple_cmd_chat_list(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1340 static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv,
1341         const char *cmd, char **args, char **error, void *data)
1342 {
1343 	PurpleConnection *gc;
1344 	gc = purple_conversation_get_gc(conv);
1345 	purple_roomlist_show_with_account(purple_connection_get_account(gc));
1346 	return PURPLE_CMD_RET_OK;
1347 }
1348 
silcpurple_cmd_whois(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1349 static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv,
1350 		const char *cmd, char **args, char **error, void *data)
1351 {
1352 	PurpleConnection *gc;
1353 
1354 	gc = purple_conversation_get_gc(conv);
1355 
1356 	if (gc == NULL)
1357 		return PURPLE_CMD_RET_FAILED;
1358 
1359 	silcpurple_get_info(gc, args[0]);
1360 
1361 	return PURPLE_CMD_RET_OK;
1362 }
1363 
silcpurple_cmd_msg(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1364 static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv,
1365 		const char *cmd, char **args, char **error, void *data)
1366 {
1367 	int ret;
1368 	PurpleConnection *gc;
1369 
1370 	gc = purple_conversation_get_gc(conv);
1371 
1372 	if (gc == NULL)
1373 		return PURPLE_CMD_RET_FAILED;
1374 
1375 	ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
1376 
1377 	if (ret)
1378 		return PURPLE_CMD_RET_OK;
1379 	else
1380 		return PURPLE_CMD_RET_FAILED;
1381 }
1382 
silcpurple_cmd_query(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1383 static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv,
1384 		const char *cmd, char **args, char **error, void *data)
1385 {
1386 	int ret = 1;
1387 	PurpleConversation *convo;
1388 	PurpleConnection *gc;
1389 	PurpleAccount *account;
1390 
1391 	if (!args || !args[0]) {
1392 		*error = g_strdup(_("You must specify a nick"));
1393 		return PURPLE_CMD_RET_FAILED;
1394 	}
1395 
1396 	gc = purple_conversation_get_gc(conv);
1397 
1398 	if (gc == NULL)
1399 		return PURPLE_CMD_RET_FAILED;
1400 
1401 	account = purple_connection_get_account(gc);
1402 
1403 	convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]);
1404 
1405 	if (args[1]) {
1406 		ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND);
1407 		purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc),
1408 				args[1], PURPLE_MESSAGE_SEND, time(NULL));
1409 	}
1410 
1411 	if (ret)
1412 		return PURPLE_CMD_RET_OK;
1413 	else
1414 		return PURPLE_CMD_RET_FAILED;
1415 }
1416 
silcpurple_cmd_motd(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1417 static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv,
1418 		const char *cmd, char **args, char **error, void *data)
1419 {
1420 	PurpleConnection *gc;
1421 	SilcPurple sg;
1422 	char *tmp;
1423 
1424 	gc = purple_conversation_get_gc(conv);
1425 
1426 	if (gc == NULL)
1427 		return PURPLE_CMD_RET_FAILED;
1428 
1429 	sg = gc->proto_data;
1430 
1431 	if (sg == NULL)
1432 		return PURPLE_CMD_RET_FAILED;
1433 
1434 	if (!sg->motd) {
1435 		*error = g_strdup(_("There is no Message of the Day associated with this connection"));
1436 		return PURPLE_CMD_RET_FAILED;
1437 	}
1438 
1439 	tmp = g_markup_escape_text(sg->motd, -1);
1440 	purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL,
1441 			tmp, NULL, NULL);
1442 	g_free(tmp);
1443 
1444 	return PURPLE_CMD_RET_OK;
1445 }
1446 
silcpurple_cmd_detach(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1447 static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv,
1448 		const char *cmd, char **args, char **error, void *data)
1449 {
1450 	PurpleConnection *gc;
1451 	SilcPurple sg;
1452 
1453 	gc = purple_conversation_get_gc(conv);
1454 
1455 	if (gc == NULL)
1456 		return PURPLE_CMD_RET_FAILED;
1457 
1458 	sg = gc->proto_data;
1459 
1460 	if (sg == NULL)
1461 		return PURPLE_CMD_RET_FAILED;
1462 
1463 	silc_client_command_call(sg->client, sg->conn, "DETACH");
1464 	sg->detaching = TRUE;
1465 
1466 	return PURPLE_CMD_RET_OK;
1467 }
1468 
silcpurple_cmd_cmode(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1469 static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv,
1470 		const char *cmd, char **args, char **error, void *data)
1471 {
1472 	PurpleConnection *gc;
1473 	SilcPurple sg;
1474 	SilcChannelEntry channel;
1475 	char *silccmd, *silcargs, *msg, tmp[256];
1476 	const char *chname;
1477 
1478 	gc = purple_conversation_get_gc(conv);
1479 
1480 	if (gc == NULL || !args || gc->proto_data == NULL)
1481 		return PURPLE_CMD_RET_FAILED;
1482 
1483 	sg = gc->proto_data;
1484 
1485 	if (args[0])
1486 		chname = args[0];
1487 	else
1488 		chname = purple_conversation_get_name(conv);
1489 
1490 	if (!args[1]) {
1491 		channel = silc_client_get_channel(sg->client, sg->conn,
1492 										  (char *)chname);
1493 		if (!channel) {
1494 			*error = g_strdup_printf(_("channel %s not found"), chname);
1495 			return PURPLE_CMD_RET_FAILED;
1496 		}
1497 		if (channel->mode) {
1498 			silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp));
1499 			msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp);
1500 		} else {
1501 			msg = g_strdup_printf(_("no channel modes are set on %s"), chname);
1502 		}
1503 		purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "",
1504 							 msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL));
1505 		g_free(msg);
1506 		return PURPLE_CMD_RET_OK;
1507 	}
1508 
1509 	silcargs = g_strjoinv(" ", args);
1510 	silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
1511 	g_free(silcargs);
1512 	if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1513 		g_free(silccmd);
1514 		*error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]);
1515 		return PURPLE_CMD_RET_FAILED;
1516 	}
1517 	g_free(silccmd);
1518 
1519 	return PURPLE_CMD_RET_OK;
1520 }
1521 
silcpurple_cmd_generic(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1522 static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv,
1523 		const char *cmd, char **args, char **error, void *data)
1524 {
1525 	PurpleConnection *gc;
1526 	SilcPurple sg;
1527 	char *silccmd, *silcargs;
1528 
1529 	gc = purple_conversation_get_gc(conv);
1530 
1531 	if (gc == NULL)
1532 		return PURPLE_CMD_RET_FAILED;
1533 
1534 	sg = gc->proto_data;
1535 
1536 	if (sg == NULL)
1537 		return PURPLE_CMD_RET_FAILED;
1538 
1539 	silcargs = g_strjoinv(" ", args);
1540 	silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL);
1541 	g_free(silcargs);
1542 	if (!silc_client_command_call(sg->client, sg->conn, silccmd)) {
1543 		g_free(silccmd);
1544 		*error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd);
1545 		return PURPLE_CMD_RET_FAILED;
1546 	}
1547 	g_free(silccmd);
1548 
1549 	return PURPLE_CMD_RET_OK;
1550 }
1551 
silcpurple_cmd_quit(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1552 static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
1553 		const char *cmd, char **args, char **error, void *data)
1554 {
1555 	PurpleConnection *gc;
1556 	SilcPurple sg;
1557 	GHashTable *ui_info;
1558 	const char *ui_name = NULL, *ui_website = NULL;
1559 	char *quit_msg;
1560 
1561 	gc = purple_conversation_get_gc(conv);
1562 
1563 	if (gc == NULL)
1564 		return PURPLE_CMD_RET_FAILED;
1565 
1566 	sg = gc->proto_data;
1567 
1568 	if (sg == NULL)
1569 		return PURPLE_CMD_RET_FAILED;
1570 
1571 	ui_info = purple_core_get_ui_info();
1572 
1573 	if(ui_info) {
1574 		ui_name = g_hash_table_lookup(ui_info, "name");
1575 		ui_website = g_hash_table_lookup(ui_info, "website");
1576 	}
1577 
1578 	if(!ui_name || !ui_website) {
1579 		ui_name = "Pidgin";
1580 		ui_website = PURPLE_WEBSITE;
1581 	}
1582 	quit_msg = g_strdup_printf(_("Download %s: %s"),
1583 							   ui_name, ui_website);
1584 
1585 	silc_client_command_call(sg->client, sg->conn, NULL,
1586 				 "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL);
1587 	g_free(quit_msg);
1588 
1589 	return PURPLE_CMD_RET_OK;
1590 }
1591 
silcpurple_cmd_call(PurpleConversation * conv,const char * cmd,char ** args,char ** error,void * data)1592 static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv,
1593 		const char *cmd, char **args, char **error, void *data)
1594 {
1595 	PurpleConnection *gc;
1596 	SilcPurple sg;
1597 
1598 	gc = purple_conversation_get_gc(conv);
1599 
1600 	if (gc == NULL)
1601 		return PURPLE_CMD_RET_FAILED;
1602 
1603 	sg = gc->proto_data;
1604 
1605 	if (sg == NULL)
1606 		return PURPLE_CMD_RET_FAILED;
1607 
1608 	if (!silc_client_command_call(sg->client, sg->conn, args[0])) {
1609 		*error = g_strdup_printf(_("Unknown command: %s"), args[0]);
1610 		return PURPLE_CMD_RET_FAILED;
1611 	}
1612 
1613 	return PURPLE_CMD_RET_OK;
1614 }
1615 
1616 
1617 /************************** Plugin Initialization ****************************/
1618 
1619 static void
silcpurple_register_commands(void)1620 silcpurple_register_commands(void)
1621 {
1622 	purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL,
1623 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1624 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1625 			"prpl-silc", silcpurple_cmd_chat_part, _("part [channel]:  Leave the chat"), NULL);
1626 	purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL,
1627 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1628 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1629 			"prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]:  Leave the chat"), NULL);
1630 	purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
1631 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1632 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1633 			silcpurple_cmd_chat_topic, _("topic [&lt;new topic&gt;]:  View or change the topic"), NULL);
1634 	purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
1635 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT |
1636 			PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
1637 			"prpl-silc", silcpurple_cmd_chat_join,
1638 			_("join &lt;channel&gt; [&lt;password&gt;]:  Join a chat on this network"), NULL);
1639 	purple_cmd_register("list", "", PURPLE_CMD_P_PRPL,
1640 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1641 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc",
1642 			silcpurple_cmd_chat_list, _("list:  List channels on this network"), NULL);
1643 	purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL,
1644 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1645 			"prpl-silc",
1646 			silcpurple_cmd_whois, _("whois &lt;nick&gt;:  View nick's information"), NULL);
1647 	purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
1648 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1649 			"prpl-silc", silcpurple_cmd_msg,
1650 			_("msg &lt;nick&gt; &lt;message&gt;:  Send a private message to a user"), NULL);
1651 	purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL,
1652 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1653 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query,
1654 			_("query &lt;nick&gt; [&lt;message&gt;]:  Send a private message to a user"), NULL);
1655 	purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL,
1656 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1657 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd,
1658 			_("motd:  View the server's Message Of The Day"), NULL);
1659 	purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL,
1660 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1661 			"prpl-silc", silcpurple_cmd_detach,
1662 			_("detach:  Detach this session"), NULL);
1663 	purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL,
1664 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1665 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit,
1666 			_("quit [message]:  Disconnect from the server, with an optional message"), NULL);
1667 	purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL,
1668 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1669 			"prpl-silc", silcpurple_cmd_call,
1670 			_("call &lt;command&gt;:  Call any silc client command"), NULL);
1671 	/* These below just get passed through for the silc client library to deal
1672 	 * with */
1673 	purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL,
1674 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1675 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1676 			_("kill &lt;nick&gt; [-pubkey|&lt;reason&gt;]:  Kill nick"), NULL);
1677 	purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL,
1678 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1679 			"prpl-silc", silcpurple_cmd_generic,
1680 			_("nick &lt;newnick&gt;:  Change your nickname"), NULL);
1681 	purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL,
1682 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1683 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1684 			_("whowas &lt;nick&gt;:  View nick's information"), NULL);
1685 	purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL,
1686 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1687 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode,
1688 			_("cmode &lt;channel&gt; [+|-&lt;modes&gt;] [arguments]:  Change or display channel modes"), NULL);
1689 	purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL,
1690 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1691 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1692 			_("cumode &lt;channel&gt; +|-&lt;modes&gt; &lt;nick&gt;:  Change nick's modes on channel"), NULL);
1693 	purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL,
1694 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1695 			"prpl-silc", silcpurple_cmd_generic,
1696 			_("umode &lt;usermodes&gt;:  Set your modes in the network"), NULL);
1697 	purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL,
1698 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1699 			"prpl-silc", silcpurple_cmd_generic,
1700 			_("oper &lt;nick&gt; [-pubkey]:  Get server operator privileges"), NULL);
1701 	purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
1702 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1703 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1704 			_("invite &lt;channel&gt; [-|+]&lt;nick&gt;:  invite nick or add/remove from channel invite list"), NULL);
1705 	purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL,
1706 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1707 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1708 			_("kick &lt;channel&gt; &lt;nick&gt; [comment]:  Kick client from channel"), NULL);
1709 	purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL,
1710 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1711 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1712 			_("info [server]:  View server administrative details"), NULL);
1713 	purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL,
1714 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1715 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic,
1716 			_("ban [&lt;channel&gt; +|-&lt;nick&gt;]:  Ban client from channel"), NULL);
1717 	purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL,
1718 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1719 			"prpl-silc", silcpurple_cmd_generic,
1720 			_("getkey &lt;nick|server&gt;:  Retrieve client's or server's public key"), NULL);
1721 	purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL,
1722 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1723 			"prpl-silc", silcpurple_cmd_generic,
1724 			_("stats:  View server and network statistics"), NULL);
1725 	purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL,
1726 			PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1727 			"prpl-silc", silcpurple_cmd_generic,
1728 			_("ping:  Send PING to the connected server"), NULL);
1729 #if 0 /* Purple doesn't handle these yet */
1730 	purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL,
1731 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
1732 			"prpl-silc", silcpurple_cmd_users,
1733 			_("users &lt;channel&gt;:  List users in channel"));
1734 	purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL,
1735 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
1736 			PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names,
1737 			_("names [-count|-ops|-halfops|-voices|-normal] &lt;channel(s)&gt;:  List specific users in channel(s)"));
1738 #endif
1739 }
1740 
1741 static PurpleWhiteboardPrplOps silcpurple_wb_ops =
1742 {
1743 	silcpurple_wb_start,
1744 	silcpurple_wb_end,
1745 	silcpurple_wb_get_dimensions,
1746 	silcpurple_wb_set_dimensions,
1747 	silcpurple_wb_get_brush,
1748 	silcpurple_wb_set_brush,
1749 	silcpurple_wb_send,
1750 	silcpurple_wb_clear,
1751 
1752 	/* padding */
1753 	NULL,
1754 	NULL,
1755 	NULL,
1756 	NULL
1757 };
1758 
1759 static PurplePluginProtocolInfo prpl_info =
1760 {
1761 #ifdef HAVE_SILCMIME_H
1762 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
1763 	OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE |
1764 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
1765 #else
1766 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
1767 	OPT_PROTO_PASSWORD_OPTIONAL |
1768 	OPT_PROTO_SLASH_COMMANDS_NATIVE,
1769 #endif
1770 	NULL,						/* user_splits */
1771 	NULL,						/* protocol_options */
1772 #ifdef SILC_ATTRIBUTE_USER_ICON
1773 	{"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
1774 #else
1775 	NO_BUDDY_ICONS,
1776 #endif
1777 	silcpurple_list_icon,			/* list_icon */
1778 	NULL,				/* list_emblems */
1779 	silcpurple_status_text,		/* status_text */
1780 	silcpurple_tooltip_text,		/* tooltip_text */
1781 	silcpurple_away_states,		/* away_states */
1782 	silcpurple_blist_node_menu,	/* blist_node_menu */
1783 	silcpurple_chat_info,			/* chat_info */
1784 	silcpurple_chat_info_defaults,/* chat_info_defaults */
1785 	silcpurple_login,				/* login */
1786 	silcpurple_close,				/* close */
1787 	silcpurple_send_im,			/* send_im */
1788 	silcpurple_set_info,			/* set_info */
1789 	NULL,						/* send_typing */
1790 	silcpurple_get_info,			/* get_info */
1791 	silcpurple_set_status,		/* set_status */
1792 	silcpurple_idle_set,			/* set_idle */
1793 	silcpurple_change_passwd,		/* change_passwd */
1794 	silcpurple_add_buddy,			/* add_buddy */
1795 	NULL,						/* add_buddies */
1796 	silcpurple_remove_buddy,		/* remove_buddy */
1797 	NULL,						/* remove_buddies */
1798 	NULL,						/* add_permit */
1799 	NULL,						/* add_deny */
1800 	NULL,						/* rem_permit */
1801 	NULL,						/* rem_deny */
1802 	NULL,						/* set_permit_deny */
1803 	silcpurple_chat_join,			/* join_chat */
1804 	NULL,						/* reject_chat */
1805 	silcpurple_get_chat_name,		/* get_chat_name */
1806 	silcpurple_chat_invite,		/* chat_invite */
1807 	silcpurple_chat_leave,		/* chat_leave */
1808 	NULL,						/* chat_whisper */
1809 	silcpurple_chat_send,			/* chat_send */
1810 	silcpurple_keepalive,			/* keepalive */
1811 	NULL,						/* register_user */
1812 	NULL,						/* get_cb_info */
1813 	NULL,						/* get_cb_away */
1814 	NULL,						/* alias_buddy */
1815 	NULL,						/* group_buddy */
1816 	NULL,						/* rename_group */
1817 	NULL,						/* buddy_free */
1818 	NULL,						/* convo_closed */
1819 	NULL,						/* normalize */
1820 #ifdef SILC_ATTRIBUTE_USER_ICON
1821 	silcpurple_buddy_set_icon,			/* set_buddy_icon */
1822 #else
1823 	NULL,
1824 #endif
1825 	NULL,						/* remove_group */
1826 	NULL,						/* get_cb_real_name */
1827 	silcpurple_chat_set_topic,	/* set_chat_topic */
1828 	NULL,						/* find_blist_chat */
1829 	silcpurple_roomlist_get_list,	/* roomlist_get_list */
1830 	silcpurple_roomlist_cancel,	/* roomlist_cancel */
1831 	NULL,						/* roomlist_expand_category */
1832 	NULL,						/* can_receive_file */
1833 	silcpurple_ftp_send_file,		/* send_file */
1834 	silcpurple_ftp_new_xfer,		/* new_xfer */
1835 	NULL,						/* offline_message */
1836 	&silcpurple_wb_ops,			/* whiteboard_prpl_ops */
1837 	NULL,                       /* send_raw */
1838 	NULL,                       /* roomlist_room_serialize */
1839 	NULL,                       /* unregister_user */
1840 	NULL,                       /* send_attention */
1841 	NULL,                       /* get_attention_types */
1842 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
1843 	NULL,                       /* get_account_text_table */
1844 	NULL,                       /* initiate_media */
1845 	NULL,                       /* get_media_caps */
1846 	NULL,                       /* get_moods */
1847 	NULL,                       /* set_public_alias */
1848 	NULL,                       /* get_public_alias */
1849 	NULL,                       /* add_buddy_with_invite */
1850 	NULL,                       /* add_buddies_with_invite */
1851 	NULL,                       /* get_cb_alias */
1852 	NULL,                       /* chat_can_receive_file */
1853 	NULL,                       /* chat_send_file */
1854 };
1855 
1856 static PurplePluginInfo info =
1857 {
1858 	PURPLE_PLUGIN_MAGIC,
1859 	PURPLE_MAJOR_VERSION,
1860 	PURPLE_MINOR_VERSION,
1861 	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
1862 	NULL,                                             /**< ui_requirement */
1863 	0,                                                /**< flags          */
1864 	NULL,                                             /**< dependencies   */
1865 	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
1866 
1867 	"prpl-silc",                                      /**< id             */
1868 	"SILC",                                           /**< name           */
1869 	"1.0",                                            /**< version        */
1870 	/**  summary        */
1871 	N_("SILC Protocol Plugin"),
1872 	/**  description    */
1873 	N_("Secure Internet Live Conferencing (SILC) Protocol"),
1874 	"Pekka Riikonen",                                 /**< author         */
1875 	"http://silcnet.org/",                            /**< homepage       */
1876 
1877 	NULL,                                             /**< load           */
1878 	NULL,                                             /**< unload         */
1879 	NULL,                                             /**< destroy        */
1880 
1881 	NULL,                                             /**< ui_info        */
1882 	&prpl_info,                                       /**< extra_info     */
1883 	NULL,                                             /**< prefs_info     */
1884 	silcpurple_actions,
1885 
1886 	/* padding */
1887 	NULL,
1888 	NULL,
1889 	NULL,
1890 	NULL
1891 };
1892 
1893 static void
init_plugin(PurplePlugin * plugin)1894 init_plugin(PurplePlugin *plugin)
1895 {
1896 	PurpleAccountOption *option;
1897 	PurpleAccountUserSplit *split;
1898 	char tmp[256];
1899 	int i;
1900 	PurpleKeyValuePair *kvp;
1901 	GList *list = NULL;
1902 
1903 	silc_plugin = plugin;
1904 
1905 	split = purple_account_user_split_new(_("Network"), "silcnet.org", '@');
1906 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
1907 
1908 	/* Account options */
1909 	option = purple_account_option_string_new(_("Connect server"),
1910 						"server",
1911 						"silc.silcnet.org");
1912 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1913 	option = purple_account_option_int_new(_("Port"), "port", 706);
1914 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1915 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
1916 	option = purple_account_option_string_new(_("Public Key file"),
1917 						"public-key", tmp);
1918 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1919 	g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
1920 	option = purple_account_option_string_new(_("Private Key file"),
1921 						"private-key", tmp);
1922 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1923 
1924 	for (i = 0; silc_default_ciphers[i].name; i++) {
1925 		kvp = g_new0(PurpleKeyValuePair, 1);
1926 		kvp->key = g_strdup(silc_default_ciphers[i].name);
1927 		kvp->value = g_strdup(silc_default_ciphers[i].name);
1928 		list = g_list_append(list, kvp);
1929 	}
1930 	option = purple_account_option_list_new(_("Cipher"), "cipher", list);
1931 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1932 
1933 	list = NULL;
1934 	for (i = 0; silc_default_hmacs[i].name; i++) {
1935 		kvp = g_new0(PurpleKeyValuePair, 1);
1936 		kvp->key = g_strdup(silc_default_hmacs[i].name);
1937 		kvp->value = g_strdup(silc_default_hmacs[i].name);
1938 		list = g_list_append(list, kvp);
1939 	}
1940 	option = purple_account_option_list_new(_("HMAC"), "hmac", list);
1941 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1942 
1943 	option = purple_account_option_bool_new(_("Public key authentication"),
1944 					      "pubkey-auth", FALSE);
1945 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1946 	option = purple_account_option_bool_new(_("Block IMs without Key Exchange"),
1947 					      "block-ims", FALSE);
1948 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1949 	option = purple_account_option_bool_new(_("Block messages to whiteboard"),
1950 					      "block-wb", FALSE);
1951 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1952 	option = purple_account_option_bool_new(_("Automatically open whiteboard"),
1953 					      "open-wb", FALSE);
1954 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1955 	option = purple_account_option_bool_new(_("Digitally sign and verify all messages"),
1956 					      "sign-verify", FALSE);
1957 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1958 
1959 	purple_prefs_remove("/plugins/prpl/silc");
1960 
1961 	silcpurple_register_commands();
1962 
1963 #ifdef _WIN32
1964 	silc_net_win32_init();
1965 #endif
1966 }
1967 
1968 PURPLE_INIT_PLUGIN(silc10, init_plugin, info);
1969