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.
21 #include "glibcompat.h"
22 #include "image-store.h"
24 PURPLE_BEGIN_IGNORE_CAST_ALIGN
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)) {
55 } else if (!strncmp(ip
, "172.", 4) && strlen(ip
) > 6) {
58 memset(tmp
, 0, sizeof(tmp
));
59 strncpy(tmp
, ip
+ 4, 2);
61 if (s
>= 16 && s
<= 31)
63 } else if (!strncmp(ip
, "192.168.", 8)) {
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];
85 pw
= getpwuid(getuid());
87 purple_debug_error("silc", "silc: %s\n", g_strerror(errno
));
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");
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
);
113 if ((g_stat(filename
, &st
)) == -1) {
114 purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename
, g_strerror(errno
));
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",
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
);
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
);
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
);
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
,
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"));
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
));
181 purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n",
182 file_public_key
, g_strerror(errno
));
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");
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
));
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
,
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"));
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
));
225 purple_debug_error("silc", "Couldn't open '%s' "
226 "private key, error: %s\n",
227 file_private_key
, g_strerror(errno
));
231 purple_debug_error("silc", "Couldn't open '%s' private key, error: %s\n",
232 file_private_key
, g_strerror(errno
));
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");
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");
258 purple_debug_warning("silc", "Done.\n\n");
266 /* on win32, we calloc pw so pass it to free
267 * (see the getpwuid code below)
276 struct passwd
*getpwuid(uid_t uid
) {
277 struct passwd
*pwd
= calloc(1, sizeof(struct passwd
));
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
;
298 SilcUInt32 pk_len
, key_len
= 0;
301 /* We support showing only SILC public keys for now */
302 if (silc_pkcs_get_type(public_key
) != SILC_PKCS_SILC
)
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
);
312 fingerprint
= silc_hash_fingerprint(NULL
, pk
, pk_len
);
313 babbleprint
= silc_hash_babbleprint(NULL
, pk
, pk_len
);
314 if (!fingerprint
|| !babbleprint
)
317 s
= g_string_new("");
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
);
324 g_string_append_printf(s
, _("User Name: \t%s\n"), ident
->username
);
326 g_string_append_printf(s
, _("Email: \t\t%s\n"), ident
->email
);
328 g_string_append_printf(s
, _("Host Name: \t%s\n"), ident
->host
);
330 g_string_append_printf(s
, _("Organization: \t%s\n"), ident
->org
);
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
);
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
);
353 silcpurple_get_attr(SilcDList attrs
, SilcAttribute attribute
)
355 SilcAttributePayload attr
= NULL
;
360 silc_dlist_start(attrs
);
361 while ((attr
= silc_dlist_get(attrs
)) != SILC_LIST_END
)
362 if (attribute
== silc_attribute_get_attribute(attr
))
368 void silcpurple_get_umode_string(SilcUInt32 mode
, char *buf
,
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] ");
404 void silcpurple_get_chmode_string(SilcUInt32 mode
, char *buf
,
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] ");
433 void silcpurple_get_chumode_string(SilcUInt32 mode
, char *buf
,
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] ");
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
;
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
);
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
);
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
: "");
562 *devicestr
= g_string_free(s
, FALSE
);
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
;
592 gboolean images
= FALSE
;
595 while (last
&& *last
&& purple_markup_find_tag("img", last
, &start
,
597 PurpleImage
*image
= NULL
;
600 /* Check if there is text before image */
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
);
614 silc_mime_add_data(p
, (const unsigned char *)text
, strlen(text
));
618 parts
= silc_dlist_init();
619 silc_dlist_add(parts
, p
);
622 uri
= g_datalist_get_data(&attribs
, "src");
624 image
= purple_image_store_get_from_uri(uri
);
626 unsigned long imglen
= purple_image_get_data_size(image
);
627 gconstpointer img
= purple_image_get_data(image
);
630 p
= silc_mime_alloc();
632 /* Add content type */
633 type
= purple_image_get_mimetype(image
);
635 g_datalist_clear(&attribs
);
639 silc_mime_add_field(p
, "Content-Type", type
);
641 /* Add content transfer encoding */
642 silc_mime_add_field(p
, "Content-Transfer-Encoding", "binary");
645 silc_mime_add_data(p
, img
, imglen
);
648 parts
= silc_dlist_init();
649 silc_dlist_add(parts
, p
);
653 g_datalist_clear(&attribs
);
655 /* Continue after tag */
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");
669 silc_mime_add_data(p
, (const unsigned char *)tmp
, strlen(tmp
));
673 parts
= silc_dlist_init();
674 silc_dlist_add(parts
, p
);
677 /* If there weren't any images, don't return anything. */
680 silc_dlist_uninit(parts
);
684 if (silc_dlist_count(parts
) > 1) {
685 /* Multipart MIME message */
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
);
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
);