1 /*
2 
3   silcpurple_util.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 "imgstore.h"
24 
25 /**************************** Utility Routines *******************************/
26 
27 static char str[256], str2[256];
28 
silcpurple_silcdir(void)29 const char *silcpurple_silcdir(void)
30 {
31 	const char *hd = purple_home_dir();
32 	memset(str, 0, sizeof(str));
33 	g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp");
34 	return (const char *)str;
35 }
36 
silcpurple_session_file(const char * account)37 const char *silcpurple_session_file(const char *account)
38 {
39 	memset(str2, 0, sizeof(str2));
40 	g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session",
41 		   silcpurple_silcdir(), account);
42 	return (const char *)str2;
43 }
44 
silcpurple_ip_is_private(const char * ip)45 gboolean silcpurple_ip_is_private(const char *ip)
46 {
47 	if (silc_net_is_ip4(ip)) {
48 		if (!strncmp(ip, "10.", 3)) {
49 			return TRUE;
50 		} else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) {
51 			char tmp[3];
52 			int s;
53 			memset(tmp, 0, sizeof(tmp));
54 			strncpy(tmp, ip + 4, 2);
55 			s = atoi(tmp);
56 			if (s >= 16 && s <= 31)
57 				return TRUE;
58 		} else if (!strncmp(ip, "192.168.", 8)) {
59 			return TRUE;
60 		}
61 	}
62 
63 	return FALSE;
64 }
65 
66 /* This checks stats for various SILC files and directories. First it
67    checks if ~/.silc directory exist and is owned by the correct user. If
68    it doesn't exist, it will create the directory. After that it checks if
69    user's Public and Private key files exists and creates them if needed. */
70 
silcpurple_check_silc_dir(PurpleConnection * gc)71 gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
72 {
73 	char filename[256], file_public_key[256], file_private_key[256];
74 	char servfilename[256], clientfilename[256], friendsfilename[256];
75 	char pkd[256], prd[256];
76 	struct stat st;
77 	struct passwd *pw;
78 	int fd;
79 
80 	pw = getpwuid(getuid());
81 	if (!pw) {
82 		purple_debug_error("silc", "silc: %s\n", g_strerror(errno));
83 		return FALSE;
84 	}
85 
86 	g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir());
87 	g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys",
88 		   silcpurple_silcdir());
89 	g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys",
90 		   silcpurple_silcdir());
91 	g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends",
92 		   silcpurple_silcdir());
93 
94 	/*
95 	 * Check ~/.silc directory
96 	 */
97 	if ((g_stat(filename, &st)) == -1) {
98 		/* If dir doesn't exist */
99 		if (errno == ENOENT) {
100 			if (pw->pw_uid == geteuid()) {
101 				if ((g_mkdir(filename, 0755)) == -1) {
102 					purple_debug_error("silc", "Couldn't create '%s' directory\n", filename);
103 					return FALSE;
104 				}
105 			} else {
106 				purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
107 					filename);
108 				return FALSE;
109 			}
110 		} else {
111 			purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, g_strerror(errno));
112 			return FALSE;
113 		}
114 	} else {
115 #ifndef _WIN32
116 		/* Check the owner of the dir */
117 		if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
118 			purple_debug_error("silc", "You don't seem to own '%s' directory\n",
119 				filename);
120 			return FALSE;
121 		}
122 #endif
123 	}
124 
125 	/*
126 	 * Check ~./silc/serverkeys directory
127 	 */
128 	if ((g_stat(servfilename, &st)) == -1) {
129 		/* If dir doesn't exist */
130 		if (errno == ENOENT) {
131 			if (pw->pw_uid == geteuid()) {
132 				if ((g_mkdir(servfilename, 0755)) == -1) {
133 					purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename);
134 					return FALSE;
135 				}
136 			} else {
137 				purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
138 					servfilename);
139 				return FALSE;
140 			}
141 		} else {
142 			purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
143 							 servfilename, g_strerror(errno));
144 			return FALSE;
145 		}
146 	}
147 
148 	/*
149 	 * Check ~./silc/clientkeys directory
150 	 */
151 	if ((g_stat(clientfilename, &st)) == -1) {
152 		/* If dir doesn't exist */
153 		if (errno == ENOENT) {
154 			if (pw->pw_uid == geteuid()) {
155 				if ((g_mkdir(clientfilename, 0755)) == -1) {
156 					purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename);
157 					return FALSE;
158 				}
159 			} else {
160 				purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
161 					clientfilename);
162 				return FALSE;
163 			}
164 		} else {
165 			purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
166 							 clientfilename, g_strerror(errno));
167 			return FALSE;
168 		}
169 	}
170 
171 	/*
172 	 * Check ~./silc/friends directory
173 	 */
174 	if ((g_stat(friendsfilename, &st)) == -1) {
175 		/* If dir doesn't exist */
176 		if (errno == ENOENT) {
177 			if (pw->pw_uid == geteuid()) {
178 				if ((g_mkdir(friendsfilename, 0755)) == -1) {
179 					purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename);
180 					return FALSE;
181 				}
182 			} else {
183 				purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n",
184 					friendsfilename);
185 				return FALSE;
186 			}
187 		} else {
188 			purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n",
189 							 friendsfilename, g_strerror(errno));
190 			return FALSE;
191 		}
192 	}
193 
194 	/*
195 	 * Check Public and Private keys
196 	 */
197 	g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
198 	g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
199 	g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s",
200 		   purple_account_get_string(gc->account, "public-key", pkd));
201 	g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s",
202 		   purple_account_get_string(gc->account, "private-key", prd));
203 
204 	if ((g_stat(file_public_key, &st)) == -1) {
205 		/* If file doesn't exist */
206 		if (errno == ENOENT) {
207 			purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
208 			if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
209 					     SILCPURPLE_DEF_PKCS_LEN,
210 					     file_public_key, file_private_key, NULL,
211 					     (gc->password == NULL) ? "" : gc->password,
212 						 NULL, NULL, NULL, FALSE)) {
213 				purple_debug_error("silc", "Couldn't create key pair\n");
214 				return FALSE;
215 			}
216 
217 			if ((g_stat(file_public_key, &st)) == -1) {
218 				purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
219 					file_public_key, g_strerror(errno));
220 				return FALSE;
221 			}
222 		} else {
223 			purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
224 							 file_public_key, g_strerror(errno));
225 			return FALSE;
226 		}
227 	}
228 
229 #ifndef _WIN32
230 	/* Check the owner of the public key */
231 	if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
232 		purple_debug_error("silc", "You don't seem to own your public key!?\n");
233 		return FALSE;
234 	}
235 #endif
236 
237 	if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
238 		if ((fstat(fd, &st)) == -1) {
239 			purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
240 							 file_private_key, g_strerror(errno));
241 			close(fd);
242 			return FALSE;
243 		}
244 	} else if ((g_stat(file_private_key, &st)) == -1) {
245 		/* If file doesn't exist */
246 		if (errno == ENOENT) {
247 			purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
248 			if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
249 					     SILCPURPLE_DEF_PKCS_LEN,
250 					     file_public_key, file_private_key, NULL,
251 					     (gc->password == NULL) ? "" : gc->password,
252 						 NULL, NULL, NULL, FALSE)) {
253 				purple_debug_error("silc", "Couldn't create key pair\n");
254 				return FALSE;
255 			}
256 
257 			if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
258 				if ((fstat(fd, &st)) == -1) {
259 					purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
260 							 file_private_key, g_strerror(errno));
261 					close(fd);
262 					return FALSE;
263 				}
264 			}
265 			/* This shouldn't really happen because silc_create_key_pair()
266 			 * will set the permissions */
267 			else if ((g_stat(file_private_key, &st)) == -1) {
268 				purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
269 					file_private_key, g_strerror(errno));
270 				return FALSE;
271 			}
272 		} else {
273 			purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
274 							 file_private_key, g_strerror(errno));
275 			return FALSE;
276 		}
277 	}
278 
279 #ifndef _WIN32
280 	/* Check the owner of the private key */
281 	if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
282 		purple_debug_error("silc", "You don't seem to own your private key!?\n");
283 		if (fd != -1)
284 			close(fd);
285 		return FALSE;
286 	}
287 
288 	/* Check the permissions for the private key */
289 	if ((st.st_mode & 0777) != 0600) {
290 		purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n"
291 			"Trying to change them ...\n", file_private_key);
292 		if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) {
293 			purple_debug_error("silc",
294 				"Failed to change permissions for private key file!\n"
295 				"Permissions for your private key file must be 0600.\n");
296 			if (fd != -1)
297 				close(fd);
298 			return FALSE;
299 		}
300 		purple_debug_warning("silc", "Done.\n\n");
301 	}
302 #endif
303 
304 	if (fd != -1)
305 		close(fd);
306 
307 	return TRUE;
308 }
309 
310 #ifdef _WIN32
getpwuid(uid_t uid)311 struct passwd *getpwuid(uid_t uid) {
312 	struct passwd *pwd = calloc(1, sizeof(struct passwd));
313 	return pwd;
314 }
315 
getuid()316 uid_t getuid() {
317 	return 0;
318 }
319 
geteuid()320 uid_t geteuid() {
321 	return 0;
322 }
323 #endif
324 
silcpurple_show_public_key(SilcPurple sg,const char * name,SilcPublicKey public_key,GCallback callback,void * context)325 void silcpurple_show_public_key(SilcPurple sg,
326 			      const char *name, SilcPublicKey public_key,
327 			      GCallback callback, void *context)
328 {
329 	SilcPublicKeyIdentifier ident;
330 	SilcPKCS pkcs;
331 	char *fingerprint, *babbleprint;
332 	unsigned char *pk;
333 	SilcUInt32 pk_len, key_len = 0;
334 	GString *s;
335 	char *buf;
336 
337 	ident = silc_pkcs_decode_identifier(public_key->identifier);
338 	if (!ident)
339 		return;
340 
341 	pk = silc_pkcs_public_key_encode(public_key, &pk_len);
342 	fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
343 	babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
344 
345 	if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) {
346 		key_len = silc_pkcs_public_key_set(pkcs, public_key);
347 		silc_pkcs_free(pkcs);
348 	}
349 
350 	s = g_string_new("");
351 	if (ident->realname)
352 		/* Hint for translators: Please check the tabulator width here and in
353 		   the next strings (short strings: 2 tabs, longer strings 1 tab,
354 		   sum: 3 tabs or 24 characters) */
355 		g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname);
356 	if (ident->username)
357 		g_string_append_printf(s, _("User Name: \t%s\n"), ident->username);
358 	if (ident->email)
359 		g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email);
360 	if (ident->host)
361 		g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host);
362 	if (ident->org)
363 		g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
364 	if (ident->country)
365 		g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
366 	g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name);
367 	g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
368 	g_string_append_printf(s, "\n");
369 	g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
370 	g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
371 
372 	buf = g_string_free(s, FALSE);
373 
374 	purple_request_action(sg->gc, _("Public Key Information"),
375 			    _("Public Key Information"),
376 			    buf, 0, purple_connection_get_account(sg->gc),
377 				NULL, NULL, context, 1, _("Close"), callback);
378 
379 	g_free(buf);
380 	silc_free(fingerprint);
381 	silc_free(babbleprint);
382 	silc_free(pk);
383 	silc_pkcs_free_identifier(ident);
384 }
385 
386 SilcAttributePayload
silcpurple_get_attr(SilcDList attrs,SilcAttribute attribute)387 silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute)
388 {
389 	SilcAttributePayload attr = NULL;
390 
391 	if (!attrs)
392 		return NULL;
393 
394 	silc_dlist_start(attrs);
395 	while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END)
396 		if (attribute == silc_attribute_get_attribute(attr))
397 			break;
398 
399 	return attr;
400 }
401 
silcpurple_get_umode_string(SilcUInt32 mode,char * buf,SilcUInt32 buf_size)402 void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
403 			       SilcUInt32 buf_size)
404 {
405 	memset(buf, 0, buf_size);
406 	if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
407 	    (mode & SILC_UMODE_ROUTER_OPERATOR)) {
408 		strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
409 		       "[server operator] " :
410 		       (mode & SILC_UMODE_ROUTER_OPERATOR) ?
411 		       "[SILC operator] " : "[unknown mode] ");
412 	}
413 	if (mode & SILC_UMODE_GONE)
414 		strcat(buf, "[away] ");
415 	if (mode & SILC_UMODE_INDISPOSED)
416 		strcat(buf, "[indisposed] ");
417 	if (mode & SILC_UMODE_BUSY)
418 		strcat(buf, "[busy] ");
419 	if (mode & SILC_UMODE_PAGE)
420 		strcat(buf, "[wake me up] ");
421 	if (mode & SILC_UMODE_HYPER)
422 		strcat(buf, "[hyperactive] ");
423 	if (mode & SILC_UMODE_ROBOT)
424 		strcat(buf, "[robot] ");
425 	if (mode & SILC_UMODE_ANONYMOUS)
426 		strcat(buf, "[anonymous] ");
427 	if (mode & SILC_UMODE_BLOCK_PRIVMSG)
428 		strcat(buf, "[blocks private messages] ");
429 	if (mode & SILC_UMODE_DETACHED)
430 		strcat(buf, "[detached] ");
431 	if (mode & SILC_UMODE_REJECT_WATCHING)
432 		strcat(buf, "[rejects watching] ");
433 	if (mode & SILC_UMODE_BLOCK_INVITE)
434 		strcat(buf, "[blocks invites] ");
435 	g_strchomp(buf);
436 }
437 
silcpurple_get_chmode_string(SilcUInt32 mode,char * buf,SilcUInt32 buf_size)438 void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
439 				SilcUInt32 buf_size)
440 {
441 	memset(buf, 0, buf_size);
442 	if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
443 		strcat(buf, "[permanent] ");
444 	if (mode & SILC_CHANNEL_MODE_PRIVATE)
445 		strcat(buf, "[private] ");
446 	if (mode & SILC_CHANNEL_MODE_SECRET)
447 		strcat(buf, "[secret] ");
448 	if (mode & SILC_CHANNEL_MODE_PRIVKEY)
449 		strcat(buf, "[private key] ");
450 	if (mode & SILC_CHANNEL_MODE_INVITE)
451 		strcat(buf, "[invite only] ");
452 	if (mode & SILC_CHANNEL_MODE_TOPIC)
453 		strcat(buf, "[topic restricted] ");
454 	if (mode & SILC_CHANNEL_MODE_ULIMIT)
455 		strcat(buf, "[user count limit] ");
456 	if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
457 		strcat(buf, "[passphrase auth] ");
458 	if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
459 		strcat(buf, "[public key auth] ");
460 	if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
461 		strcat(buf, "[users silenced] ");
462 	if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
463 		strcat(buf, "[operators silenced] ");
464 	g_strchomp(buf);
465 }
466 
silcpurple_get_chumode_string(SilcUInt32 mode,char * buf,SilcUInt32 buf_size)467 void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
468 				 SilcUInt32 buf_size)
469 {
470 	memset(buf, 0, buf_size);
471 	if (mode & SILC_CHANNEL_UMODE_CHANFO)
472 		strcat(buf, "[founder] ");
473 	if (mode & SILC_CHANNEL_UMODE_CHANOP)
474 		strcat(buf, "[operator] ");
475 	if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
476 		strcat(buf, "[blocks messages] ");
477 	if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
478 		strcat(buf, "[blocks user messages] ");
479 	if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
480 		strcat(buf, "[blocks robot messages] ");
481 	if (mode & SILC_CHANNEL_UMODE_QUIET)
482 		strcat(buf, "[quieted] ");
483 	g_strchomp(buf);
484 }
485 
486 void
silcpurple_parse_attrs(SilcDList attrs,char ** moodstr,char ** statusstr,char ** contactstr,char ** langstr,char ** devicestr,char ** tzstr,char ** geostr)487 silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
488 					 char **contactstr, char **langstr, char **devicestr,
489 					 char **tzstr, char **geostr)
490 {
491 	SilcAttributePayload attr;
492 	SilcAttributeMood mood = 0;
493 	SilcAttributeContact contact;
494 	SilcAttributeObjDevice device;
495 	SilcAttributeObjGeo geo;
496 
497 	char tmp[1024];
498 	GString *s;
499 
500 	*moodstr = NULL;
501 	*statusstr = NULL;
502 	*contactstr = NULL;
503 	*langstr = NULL;
504 	*devicestr = NULL;
505 	*tzstr = NULL;
506 	*geostr = NULL;
507 
508 	if (!attrs)
509 		return;
510 
511 	s = g_string_new("");
512 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD);
513 	if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
514 		if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
515 			g_string_append_printf(s, "[%s] ", _("Happy"));
516 		if (mood & SILC_ATTRIBUTE_MOOD_SAD)
517 			g_string_append_printf(s, "[%s] ", _("Sad"));
518 		if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
519 			g_string_append_printf(s, "[%s] ", _("Angry"));
520 		if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
521 			g_string_append_printf(s, "[%s] ", _("Jealous"));
522 		if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
523 			g_string_append_printf(s, "[%s] ", _("Ashamed"));
524 		if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
525 			g_string_append_printf(s, "[%s] ", _("Invincible"));
526 		if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
527 			g_string_append_printf(s, "[%s] ", _("In Love"));
528 		if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
529 			g_string_append_printf(s, "[%s] ", _("Sleepy"));
530 		if (mood & SILC_ATTRIBUTE_MOOD_BORED)
531 			g_string_append_printf(s, "[%s] ", _("Bored"));
532 		if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
533 			g_string_append_printf(s, "[%s] ", _("Excited"));
534 		if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
535 			g_string_append_printf(s, "[%s] ", _("Anxious"));
536 	}
537 	if (strlen(s->str)) {
538 		*moodstr = s->str;
539 		g_string_free(s, FALSE);
540 		g_strchomp(*moodstr);
541 	} else
542 		g_string_free(s, TRUE);
543 
544 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT);
545 	memset(tmp, 0, sizeof(tmp));
546 	if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
547 		*statusstr = g_strdup(tmp);
548 
549 	s = g_string_new("");
550 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT);
551 	if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) {
552 		if (contact & SILC_ATTRIBUTE_CONTACT_CHAT)
553 			g_string_append_printf(s, "[%s] ", _("Chat"));
554 		if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL)
555 			g_string_append_printf(s, "[%s] ", _("Email"));
556 		if (contact & SILC_ATTRIBUTE_CONTACT_CALL)
557 			g_string_append_printf(s, "[%s] ", _("Phone"));
558 		if (contact & SILC_ATTRIBUTE_CONTACT_PAGE)
559 			g_string_append_printf(s, "[%s] ", _("Paging"));
560 		if (contact & SILC_ATTRIBUTE_CONTACT_SMS)
561 			g_string_append_printf(s, "[%s] ", _("SMS"));
562 		if (contact & SILC_ATTRIBUTE_CONTACT_MMS)
563 			g_string_append_printf(s, "[%s] ", _("MMS"));
564 		if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO)
565 			g_string_append_printf(s, "[%s] ", _("Video Conferencing"));
566 	}
567 	if (strlen(s->str)) {
568 		*contactstr = s->str;
569 		g_string_free(s, FALSE);
570 		g_strchomp(*contactstr);
571 	} else
572 		g_string_free(s, TRUE);
573 
574 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE);
575 	memset(tmp, 0, sizeof(tmp));
576 	if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
577 		*langstr = g_strdup(tmp);
578 
579 	s = g_string_new("");
580 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO);
581 	memset(&device, 0, sizeof(device));
582 	if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) {
583 		if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER)
584 			g_string_append_printf(s, "%s: ", _("Computer"));
585 		if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE)
586 			g_string_append_printf(s, "%s: ", _("Mobile Phone"));
587 		if (device.type == SILC_ATTRIBUTE_DEVICE_PDA)
588 			g_string_append_printf(s, "%s: ", _("PDA"));
589 		if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL)
590 			g_string_append_printf(s, "%s: ", _("Terminal"));
591 		g_string_append_printf(s, "%s %s %s %s",
592 				device.manufacturer ? device.manufacturer : "",
593 				device.version ? device.version : "",
594 				device.model ? device.model : "",
595 				device.language ? device.language : "");
596 	}
597 	if (strlen(s->str)) {
598 		*devicestr = s->str;
599 		g_string_free(s, FALSE);
600 	} else
601 		g_string_free(s, TRUE);
602 
603 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE);
604 	memset(tmp, 0, sizeof(tmp));
605 	if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
606 		*tzstr = g_strdup(tmp);
607 
608 	attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION);
609 	memset(&geo, 0, sizeof(geo));
610 	if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo)))
611 		*geostr = g_strdup_printf("%s %s %s (%s)",
612 				geo.longitude ? geo.longitude : "",
613 				geo.latitude ? geo.latitude : "",
614 				geo.altitude ? geo.altitude : "",
615 				geo.accuracy ? geo.accuracy : "");
616 }
617 
618 #ifdef HAVE_SILCMIME_H
619 /* Returns MIME type of filetype */
620 
silcpurple_file2mime(const char * filename)621 char *silcpurple_file2mime(const char *filename)
622 {
623 	const char *ct;
624 
625 	ct = strrchr(filename, '.');
626 	if (!ct)
627 		return NULL;
628 	else if (!g_ascii_strcasecmp(".png", ct))
629 		return strdup("image/png");
630 	else if (!g_ascii_strcasecmp(".jpg", ct))
631 		return strdup("image/jpeg");
632 	else if (!g_ascii_strcasecmp(".jpeg", ct))
633 		return strdup("image/jpeg");
634 	else if (!g_ascii_strcasecmp(".gif", ct))
635 		return strdup("image/gif");
636 	else if (!g_ascii_strcasecmp(".tiff", ct))
637 		return strdup("image/tiff");
638 
639 	return NULL;
640 }
641 
642 /* Checks if message has images, and assembles MIME message if it has.
643    If only one image is present, creates simple MIME image message.  If
644    there are multiple images and/or text with images multipart MIME
645    message is created. */
646 
silcpurple_image_message(const char * msg,SilcUInt32 * mflags)647 SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags)
648 {
649 	SilcMime mime = NULL, p;
650 	SilcDList list, parts = NULL;
651 	const char *start, *end, *last;
652 	GData *attribs;
653 	char *type;
654 	gboolean images = FALSE;
655 
656 	last = msg;
657 	while (last && *last && purple_markup_find_tag("img", last, &start,
658 						     &end, &attribs)) {
659 		PurpleStoredImage *image = NULL;
660 		const char *id;
661 
662 		/* Check if there is text before image */
663 		if (start - last) {
664 			char *text, *tmp;
665 			p = silc_mime_alloc();
666 
667 			/* Add content type */
668 			silc_mime_add_field(p, "Content-Type",
669 					    "text/plain; charset=utf-8");
670 
671 			tmp = g_strndup(last, start - last);
672 			text = purple_unescape_html(tmp);
673 			g_free(tmp);
674 			/* Add text */
675 			silc_mime_add_data(p, (unsigned char *)text, strlen(text));
676 			g_free(text);
677 
678 			if (!parts)
679 				parts = silc_dlist_init();
680 			silc_dlist_add(parts, p);
681 		}
682 
683 		id = g_datalist_get_data(&attribs, "id");
684 		if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
685 			unsigned long imglen = purple_imgstore_get_size(image);
686 			gconstpointer img = purple_imgstore_get_data(image);
687 
688 			p = silc_mime_alloc();
689 
690 			/* Add content type */
691 			type = silcpurple_file2mime(purple_imgstore_get_filename(image));
692 			if (!type) {
693 				g_datalist_clear(&attribs);
694 				last = end + 1;
695 				continue;
696 			}
697 			silc_mime_add_field(p, "Content-Type", type);
698 			silc_free(type);
699 
700 			/* Add content transfer encoding */
701 			silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
702 
703 			/* Add image data */
704 			silc_mime_add_data(p, img, imglen);
705 
706 			if (!parts)
707 				parts = silc_dlist_init();
708 			silc_dlist_add(parts, p);
709 			images = TRUE;
710 		}
711 
712 		g_datalist_clear(&attribs);
713 
714 		/* Continue after tag */
715 		last = end + 1;
716 	}
717 
718 	/* Check for text after the image(s) */
719 	if (images && last && *last) {
720 		char *tmp = purple_unescape_html(last);
721 		p = silc_mime_alloc();
722 
723 		/* Add content type */
724 		silc_mime_add_field(p, "Content-Type",
725 				    "text/plain; charset=utf-8");
726 
727 		/* Add text */
728 		silc_mime_add_data(p, (unsigned char *)tmp, strlen(tmp));
729 		g_free(tmp);
730 
731 		if (!parts)
732 			parts = silc_dlist_init();
733 		silc_dlist_add(parts, p);
734 	}
735 
736 	/* If there weren't any images, don't return anything. */
737 	if (!images) {
738 		if (parts)
739 			silc_dlist_uninit(parts);
740 		return NULL;
741 	}
742 
743 	if (silc_dlist_count(parts) > 1) {
744 		/* Multipart MIME message */
745 		char b[32];
746 		mime = silc_mime_alloc();
747 		silc_mime_add_field(mime, "MIME-Version", "1.0");
748 		g_snprintf(b, sizeof(b), "b%4X%4X",
749 			   (unsigned int)time(NULL),
750 			   silc_dlist_count(parts));
751 		silc_mime_set_multipart(mime, "mixed", b);
752 		silc_dlist_start(parts);
753 		while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
754 			silc_mime_add_multipart(mime, p);
755 	} else {
756 		/* Simple MIME message */
757 		silc_dlist_start(parts);
758 		mime = silc_dlist_get(parts);
759 		silc_mime_add_field(mime, "MIME-Version", "1.0");
760 	}
761 
762 	*mflags &= ~SILC_MESSAGE_FLAG_UTF8;
763 	*mflags |= SILC_MESSAGE_FLAG_DATA;
764 
765 	/* Encode message. Fragment if it is too large */
766 	list = silc_mime_encode_partial(mime, 0xfc00);
767 
768 	silc_dlist_uninit(parts);
769 
770 	/* Added multiparts gets freed here */
771 	silc_mime_free(mime);
772 
773 	return list;
774 }
775 
776 #endif /* HAVE_SILCMIME_H */
777