rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / silc / util.c
blobd557cffae658b5192523e9eba8dac5c71cfa00c7
1 /*
3 silcpurple_util.c
5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2004 - 2007 Pekka Riikonen
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.
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.
20 #include "internal.h"
21 #include "glibcompat.h"
22 #include "image-store.h"
24 PURPLE_BEGIN_IGNORE_CAST_ALIGN
25 #include "silc.h"
26 PURPLE_END_IGNORE_CAST_ALIGN
27 #include "silcclient.h"
28 #include "silcpurple.h"
30 /**************************** Utility Routines *******************************/
32 static char str[256], str2[256];
34 const char *silcpurple_silcdir(void)
36 const char *hd = purple_home_dir();
37 memset(str, 0, sizeof(str));
38 g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp");
39 return (const char *)str;
42 const char *silcpurple_session_file(const char *account)
44 memset(str2, 0, sizeof(str2));
45 g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session",
46 silcpurple_silcdir(), account);
47 return (const char *)str2;
50 gboolean silcpurple_ip_is_private(const char *ip)
52 if (silc_net_is_ip4(ip)) {
53 if (!strncmp(ip, "10.", 3)) {
54 return TRUE;
55 } else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) {
56 char tmp[3];
57 int s;
58 memset(tmp, 0, sizeof(tmp));
59 strncpy(tmp, ip + 4, 2);
60 s = atoi(tmp);
61 if (s >= 16 && s <= 31)
62 return TRUE;
63 } else if (!strncmp(ip, "192.168.", 8)) {
64 return TRUE;
68 return FALSE;
71 /* This checks stats for various SILC files and directories. First it
72 checks if ~/.silc directory exist and is owned by the correct user. If
73 it doesn't exist, it will create the directory. After that it checks if
74 user's Public and Private key files exists and creates them if needed. */
76 gboolean silcpurple_check_silc_dir(PurpleConnection *gc)
78 char filename[256], file_public_key[256], file_private_key[256];
79 char servfilename[256], clientfilename[256], friendsfilename[256];
80 char pkd[256], prd[256];
81 GStatBuf st;
82 struct passwd *pw;
83 int fd;
85 pw = getpwuid(getuid());
86 if (!pw) {
87 purple_debug_error("silc", "silc: %s\n", g_strerror(errno));
88 return FALSE;
91 g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir());
92 g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys",
93 silcpurple_silcdir());
94 g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys",
95 silcpurple_silcdir());
96 g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends",
97 silcpurple_silcdir());
99 if (pw->pw_uid != geteuid()) {
100 purple_debug_error("silc", "Couldn't create directories due to wrong uid!\n");
101 return FALSE;
105 * Check ~/.silc directory
107 if (g_mkdir(filename, 0755) != 0 && errno != EEXIST) {
108 purple_debug_error("silc", "Couldn't create '%s' directory\n", filename);
109 return FALSE;
112 #ifndef _WIN32
113 if ((g_stat(filename, &st)) == -1) {
114 purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, g_strerror(errno));
115 return FALSE;
116 } else {
117 /* Check the owner of the dir */
118 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
119 purple_debug_error("silc", "You don't seem to own '%s' directory\n",
120 filename);
121 return FALSE;
124 #endif
127 * Check ~./silc/serverkeys directory
129 if (g_mkdir(servfilename, 0755) != 0 && errno != EEXIST) {
130 purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename);
131 return FALSE;
135 * Check ~./silc/clientkeys directory
137 if (g_mkdir(clientfilename, 0755) != 0 && errno != EEXIST) {
138 purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename);
139 return FALSE;
143 * Check ~./silc/friends directory
145 if (g_mkdir(friendsfilename, 0755) != 0 && errno != EEXIST) {
146 purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename);
147 return FALSE;
151 * Check Public and Private keys
153 g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir());
154 g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir());
155 g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s",
156 purple_account_get_string(purple_connection_get_account(gc), "public-key", pkd));
157 g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s",
158 purple_account_get_string(purple_connection_get_account(gc), "private-key", prd));
160 if ((g_stat(file_public_key, &st)) == -1) {
161 /* If file doesn't exist */
162 if (errno == ENOENT) {
163 purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
164 if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
165 SILCPURPLE_DEF_PKCS_LEN,
166 file_public_key,
167 file_private_key, NULL,
168 (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
169 NULL, NULL, FALSE)) {
170 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
171 _("Unable to create SILC key pair"));
172 return FALSE;
175 if ((g_stat(file_public_key, &st)) == -1) {
176 purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
177 file_public_key, g_strerror(errno));
178 return FALSE;
180 } else {
181 purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
182 file_public_key, g_strerror(errno));
183 return FALSE;
187 #ifndef _WIN32
188 /* Check the owner of the public key */
189 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
190 purple_debug_error("silc", "You don't seem to own your public key!?\n");
191 return FALSE;
193 #endif
195 if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
196 if (_purple_fstat(fd, &st) == -1) {
197 purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
198 file_private_key, g_strerror(errno));
199 close(fd);
200 return FALSE;
202 } else {
203 /* If file doesn't exist */
204 if (errno == ENOENT) {
205 purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5);
206 if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS,
207 SILCPURPLE_DEF_PKCS_LEN,
208 file_public_key,
209 file_private_key, NULL,
210 (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc),
211 NULL, NULL, FALSE)) {
212 purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
213 _("Unable to create SILC key pair"));
214 return FALSE;
217 if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) {
218 if (_purple_fstat(fd, &st) == -1) {
219 purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n",
220 file_private_key, g_strerror(errno));
221 close(fd);
222 return FALSE;
224 } else {
225 purple_debug_error("silc", "Couldn't open '%s' "
226 "private key, error: %s\n",
227 file_private_key, g_strerror(errno));
228 return FALSE;
230 } else {
231 purple_debug_error("silc", "Couldn't open '%s' private key, error: %s\n",
232 file_private_key, g_strerror(errno));
233 return FALSE;
237 #ifndef _WIN32
238 /* Check the owner of the private key */
239 if (st.st_uid != 0 && st.st_uid != pw->pw_uid) {
240 purple_debug_error("silc", "You don't seem to own your private key!?\n");
241 if (fd != -1)
242 close(fd);
243 return FALSE;
246 /* Check the permissions for the private key */
247 if ((st.st_mode & 0777) != 0600) {
248 purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n"
249 "Trying to change them ...\n", file_private_key);
250 if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) {
251 purple_debug_error("silc",
252 "Failed to change permissions for private key file!\n"
253 "Permissions for your private key file must be 0600.\n");
254 if (fd != -1)
255 close(fd);
256 return FALSE;
258 purple_debug_warning("silc", "Done.\n\n");
260 #endif
262 if (fd != -1)
263 close(fd);
265 #ifdef _WIN32
266 /* on win32, we calloc pw so pass it to free
267 * (see the getpwuid code below)
269 free(pw);
270 #endif
272 return TRUE;
275 #ifdef _WIN32
276 struct passwd *getpwuid(uid_t uid) {
277 struct passwd *pwd = calloc(1, sizeof(struct passwd));
278 return pwd;
281 uid_t getuid() {
282 return 0;
285 uid_t geteuid() {
286 return 0;
288 #endif
290 void silcpurple_show_public_key(SilcPurple sg,
291 const char *name, SilcPublicKey public_key,
292 GCallback callback, void *context)
294 SilcPublicKeyIdentifier ident;
295 SilcSILCPublicKey silc_pubkey;
296 char *fingerprint, *babbleprint;
297 unsigned char *pk;
298 SilcUInt32 pk_len, key_len = 0;
299 GString *s;
301 /* We support showing only SILC public keys for now */
302 if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC)
303 return;
305 silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
306 ident = &silc_pubkey->identifier;
307 key_len = silc_pkcs_public_key_get_len(public_key);
309 pk = silc_pkcs_public_key_encode(public_key, &pk_len);
310 if (!pk)
311 return;
312 fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
313 babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
314 if (!fingerprint || !babbleprint)
315 return;
317 s = g_string_new("");
318 if (ident->realname)
319 /* Hint for translators: Please check the tabulator width here and in
320 the next strings (short strings: 2 tabs, longer strings 1 tab,
321 sum: 3 tabs or 24 characters) */
322 g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname);
323 if (ident->username)
324 g_string_append_printf(s, _("User Name: \t%s\n"), ident->username);
325 if (ident->email)
326 g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email);
327 if (ident->host)
328 g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host);
329 if (ident->org)
330 g_string_append_printf(s, _("Organization: \t%s\n"), ident->org);
331 if (ident->country)
332 g_string_append_printf(s, _("Country: \t%s\n"), ident->country);
333 g_string_append_printf(s, _("Algorithm: \t%s\n"), silc_pubkey->pkcs->name);
334 g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len);
335 if (ident->version)
336 g_string_append_printf(s, _("Version: \t%s\n"), ident->version);
337 g_string_append_printf(s, "\n");
338 g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint);
339 g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint);
341 purple_request_action(sg->gc, _("Public Key Information"),
342 _("Public Key Information"),
343 s->str, 0, purple_request_cpar_from_connection(sg->gc),
344 context, 1, _("Close"), callback);
346 g_string_free(s, TRUE);
347 silc_free(fingerprint);
348 silc_free(babbleprint);
349 silc_free(pk);
352 SilcAttributePayload
353 silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute)
355 SilcAttributePayload attr = NULL;
357 if (!attrs)
358 return NULL;
360 silc_dlist_start(attrs);
361 while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END)
362 if (attribute == silc_attribute_get_attribute(attr))
363 break;
365 return attr;
368 void silcpurple_get_umode_string(SilcUInt32 mode, char *buf,
369 SilcUInt32 buf_size)
371 memset(buf, 0, buf_size);
372 if ((mode & SILC_UMODE_SERVER_OPERATOR) ||
373 (mode & SILC_UMODE_ROUTER_OPERATOR)) {
374 strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ?
375 "[server operator] " :
376 (mode & SILC_UMODE_ROUTER_OPERATOR) ?
377 "[SILC operator] " : "[unknown mode] ");
379 if (mode & SILC_UMODE_GONE)
380 strcat(buf, "[away] ");
381 if (mode & SILC_UMODE_INDISPOSED)
382 strcat(buf, "[indisposed] ");
383 if (mode & SILC_UMODE_BUSY)
384 strcat(buf, "[busy] ");
385 if (mode & SILC_UMODE_PAGE)
386 strcat(buf, "[wake me up] ");
387 if (mode & SILC_UMODE_HYPER)
388 strcat(buf, "[hyperactive] ");
389 if (mode & SILC_UMODE_ROBOT)
390 strcat(buf, "[robot] ");
391 if (mode & SILC_UMODE_ANONYMOUS)
392 strcat(buf, "[anonymous] ");
393 if (mode & SILC_UMODE_BLOCK_PRIVMSG)
394 strcat(buf, "[blocks private messages] ");
395 if (mode & SILC_UMODE_DETACHED)
396 strcat(buf, "[detached] ");
397 if (mode & SILC_UMODE_REJECT_WATCHING)
398 strcat(buf, "[rejects watching] ");
399 if (mode & SILC_UMODE_BLOCK_INVITE)
400 strcat(buf, "[blocks invites] ");
401 g_strchomp(buf);
404 void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf,
405 SilcUInt32 buf_size)
407 memset(buf, 0, buf_size);
408 if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
409 strcat(buf, "[permanent] ");
410 if (mode & SILC_CHANNEL_MODE_PRIVATE)
411 strcat(buf, "[private] ");
412 if (mode & SILC_CHANNEL_MODE_SECRET)
413 strcat(buf, "[secret] ");
414 if (mode & SILC_CHANNEL_MODE_PRIVKEY)
415 strcat(buf, "[private key] ");
416 if (mode & SILC_CHANNEL_MODE_INVITE)
417 strcat(buf, "[invite only] ");
418 if (mode & SILC_CHANNEL_MODE_TOPIC)
419 strcat(buf, "[topic restricted] ");
420 if (mode & SILC_CHANNEL_MODE_ULIMIT)
421 strcat(buf, "[user count limit] ");
422 if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
423 strcat(buf, "[passphrase auth] ");
424 if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
425 strcat(buf, "[public key auth] ");
426 if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
427 strcat(buf, "[users silenced] ");
428 if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
429 strcat(buf, "[operators silenced] ");
430 g_strchomp(buf);
433 void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf,
434 SilcUInt32 buf_size)
436 memset(buf, 0, buf_size);
437 if (mode & SILC_CHANNEL_UMODE_CHANFO)
438 strcat(buf, "[founder] ");
439 if (mode & SILC_CHANNEL_UMODE_CHANOP)
440 strcat(buf, "[operator] ");
441 if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
442 strcat(buf, "[blocks messages] ");
443 if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
444 strcat(buf, "[blocks user messages] ");
445 if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
446 strcat(buf, "[blocks robot messages] ");
447 if (mode & SILC_CHANNEL_UMODE_QUIET)
448 strcat(buf, "[quieted] ");
449 g_strchomp(buf);
452 void
453 silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr,
454 char **contactstr, char **langstr, char **devicestr,
455 char **tzstr, char **geostr)
457 SilcAttributePayload attr;
458 SilcAttributeMood mood = 0;
459 SilcAttributeContact contact;
460 SilcAttributeObjDevice device;
461 SilcAttributeObjGeo geo;
463 char tmp[1024];
464 GString *s;
466 *moodstr = NULL;
467 *statusstr = NULL;
468 *contactstr = NULL;
469 *langstr = NULL;
470 *devicestr = NULL;
471 *tzstr = NULL;
472 *geostr = NULL;
474 if (!attrs)
475 return;
477 s = g_string_new("");
478 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD);
479 if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) {
480 if (mood & SILC_ATTRIBUTE_MOOD_HAPPY)
481 g_string_append_printf(s, "[%s] ", _("Happy"));
482 if (mood & SILC_ATTRIBUTE_MOOD_SAD)
483 g_string_append_printf(s, "[%s] ", _("Sad"));
484 if (mood & SILC_ATTRIBUTE_MOOD_ANGRY)
485 g_string_append_printf(s, "[%s] ", _("Angry"));
486 if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS)
487 g_string_append_printf(s, "[%s] ", _("Jealous"));
488 if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED)
489 g_string_append_printf(s, "[%s] ", _("Ashamed"));
490 if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE)
491 g_string_append_printf(s, "[%s] ", _("Invincible"));
492 if (mood & SILC_ATTRIBUTE_MOOD_INLOVE)
493 g_string_append_printf(s, "[%s] ", _("In Love"));
494 if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY)
495 g_string_append_printf(s, "[%s] ", _("Sleepy"));
496 if (mood & SILC_ATTRIBUTE_MOOD_BORED)
497 g_string_append_printf(s, "[%s] ", _("Bored"));
498 if (mood & SILC_ATTRIBUTE_MOOD_EXCITED)
499 g_string_append_printf(s, "[%s] ", _("Excited"));
500 if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS)
501 g_string_append_printf(s, "[%s] ", _("Anxious"));
503 if (*s->str != '\0') {
504 *moodstr = g_string_free(s, FALSE);
505 g_strchomp(*moodstr);
506 } else
507 g_string_free(s, TRUE);
509 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT);
510 memset(tmp, 0, sizeof(tmp));
511 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
512 *statusstr = g_strdup(tmp);
514 s = g_string_new("");
515 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT);
516 if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) {
517 if (contact & SILC_ATTRIBUTE_CONTACT_CHAT)
518 g_string_append_printf(s, "[%s] ", _("Chat"));
519 if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL)
520 g_string_append_printf(s, "[%s] ", _("Email"));
521 if (contact & SILC_ATTRIBUTE_CONTACT_CALL)
522 g_string_append_printf(s, "[%s] ", _("Phone"));
523 if (contact & SILC_ATTRIBUTE_CONTACT_PAGE)
524 g_string_append_printf(s, "[%s] ", _("Paging"));
525 if (contact & SILC_ATTRIBUTE_CONTACT_SMS)
526 g_string_append_printf(s, "[%s] ", _("SMS"));
527 if (contact & SILC_ATTRIBUTE_CONTACT_MMS)
528 g_string_append_printf(s, "[%s] ", _("MMS"));
529 if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO)
530 g_string_append_printf(s, "[%s] ", _("Video Conferencing"));
532 if (*s->str != '\0') {
533 *contactstr = g_string_free(s, FALSE);
534 g_strchomp(*contactstr);
535 } else
536 g_string_free(s, TRUE);
538 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE);
539 memset(tmp, 0, sizeof(tmp));
540 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
541 *langstr = g_strdup(tmp);
543 s = g_string_new("");
544 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO);
545 memset(&device, 0, sizeof(device));
546 if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) {
547 if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER)
548 g_string_append_printf(s, "%s: ", _("Computer"));
549 if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE)
550 g_string_append_printf(s, "%s: ", _("Mobile Phone"));
551 if (device.type == SILC_ATTRIBUTE_DEVICE_PDA)
552 g_string_append_printf(s, "%s: ", _("PDA"));
553 if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL)
554 g_string_append_printf(s, "%s: ", _("Terminal"));
555 g_string_append_printf(s, "%s %s %s %s",
556 device.manufacturer ? device.manufacturer : "",
557 device.version ? device.version : "",
558 device.model ? device.model : "",
559 device.language ? device.language : "");
561 if (*s->str != '\0')
562 *devicestr = g_string_free(s, FALSE);
563 else
564 g_string_free(s, TRUE);
566 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE);
567 memset(tmp, 0, sizeof(tmp));
568 if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp)))
569 *tzstr = g_strdup(tmp);
571 attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION);
572 memset(&geo, 0, sizeof(geo));
573 if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo)))
574 *geostr = g_strdup_printf("%s %s %s (%s)",
575 geo.longitude ? geo.longitude : "",
576 geo.latitude ? geo.latitude : "",
577 geo.altitude ? geo.altitude : "",
578 geo.accuracy ? geo.accuracy : "");
581 /* Checks if message has images, and assembles MIME message if it has.
582 If only one image is present, creates simple MIME image message. If
583 there are multiple images and/or text with images multipart MIME
584 message is created. */
586 SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags)
588 SilcMime mime = NULL, p;
589 SilcDList list, parts = NULL;
590 const char *start, *end, *last;
591 GData *attribs;
592 gboolean images = FALSE;
594 last = msg;
595 while (last && *last && purple_markup_find_tag("img", last, &start,
596 &end, &attribs)) {
597 PurpleImage *image = NULL;
598 const gchar *uri;
600 /* Check if there is text before image */
601 if (start - last) {
602 char *text, *tmp;
603 p = silc_mime_alloc();
605 /* Add content type */
606 silc_mime_add_field(p, "Content-Type",
607 "text/plain; charset=utf-8");
609 tmp = g_strndup(last, start - last);
610 text = purple_unescape_html(tmp);
611 g_free(tmp);
613 /* Add text */
614 silc_mime_add_data(p, (const unsigned char *)text, strlen(text));
615 g_free(text);
617 if (!parts)
618 parts = silc_dlist_init();
619 silc_dlist_add(parts, p);
622 uri = g_datalist_get_data(&attribs, "src");
623 if (uri)
624 image = purple_image_store_get_from_uri(uri);
625 if (uri) {
626 unsigned long imglen = purple_image_get_data_size(image);
627 gconstpointer img = purple_image_get_data(image);
628 const gchar *type;
630 p = silc_mime_alloc();
632 /* Add content type */
633 type = purple_image_get_mimetype(image);
634 if (!type) {
635 g_datalist_clear(&attribs);
636 last = end + 1;
637 continue;
639 silc_mime_add_field(p, "Content-Type", type);
641 /* Add content transfer encoding */
642 silc_mime_add_field(p, "Content-Transfer-Encoding", "binary");
644 /* Add image data */
645 silc_mime_add_data(p, img, imglen);
647 if (!parts)
648 parts = silc_dlist_init();
649 silc_dlist_add(parts, p);
650 images = TRUE;
653 g_datalist_clear(&attribs);
655 /* Continue after tag */
656 last = end + 1;
659 /* Check for text after the image(s) */
660 if (images && last && *last) {
661 char *tmp = purple_unescape_html(last);
662 p = silc_mime_alloc();
664 /* Add content type */
665 silc_mime_add_field(p, "Content-Type",
666 "text/plain; charset=utf-8");
668 /* Add text */
669 silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp));
670 g_free(tmp);
672 if (!parts)
673 parts = silc_dlist_init();
674 silc_dlist_add(parts, p);
677 /* If there weren't any images, don't return anything. */
678 if (!images) {
679 if (parts)
680 silc_dlist_uninit(parts);
681 return NULL;
684 if (silc_dlist_count(parts) > 1) {
685 /* Multipart MIME message */
686 char b[32];
687 mime = silc_mime_alloc();
688 silc_mime_add_field(mime, "MIME-Version", "1.0");
689 g_snprintf(b, sizeof(b), "b%4X%4X",
690 (unsigned int)time(NULL),
691 silc_dlist_count(parts));
692 silc_mime_set_multipart(mime, "mixed", b);
693 silc_dlist_start(parts);
694 while ((p = silc_dlist_get(parts)) != SILC_LIST_END)
695 silc_mime_add_multipart(mime, p);
696 } else {
697 /* Simple MIME message */
698 silc_dlist_start(parts);
699 mime = silc_dlist_get(parts);
700 silc_mime_add_field(mime, "MIME-Version", "1.0");
703 *mflags &= ~SILC_MESSAGE_FLAG_UTF8;
704 *mflags |= SILC_MESSAGE_FLAG_DATA;
706 /* Encode message. Fragment if it is too large */
707 list = silc_mime_encode_partial(mime, 0xfc00);
709 silc_dlist_uninit(parts);
711 /* Added multiparts gets freed here */
712 silc_mime_free(mime);
714 return list;