menu: added new Keywords tag to .desktop files
[barry.git] / src / vformat.c
blob8726df9a75d9e5cf120397b9fd94e7f4577aaf59
1 /*
2 * Copyright (C) 2003 Ximian, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Author: Chris Toshok (toshok@ximian.com)
19 * Author: Armin Bauer (armin.bauer@opensync.org)
23 #include "i18n.h"
24 #include "vformat.h"
25 #include "clog.h"
27 //#ifdef HAVE_CONFIG_H
28 //#include "config.h"
29 //#endif
31 #include <string.h>
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <iconv.h>
36 //#include <opensync/opensync.h>
38 #define TRACE_INTERNAL 1
39 #define TRACE_ENTRY 1
40 #define TRACE_EXIT 1
41 #define TRACE_ERROR 0
43 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save);
44 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save);
45 static size_t base64_decode_simple (char *data, size_t len);
46 static char *base64_encode_simple (const char *data, size_t len);
48 static size_t quoted_decode_simple (char *data, size_t len);
49 static char *quoted_encode_simple (const unsigned char *string, int len);
52 /**
53 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
54 * @param check_string string that should be compared with "b" or "base64"
55 * @return 0 if check_string is not base64 and 1 if it is
57 static int _helper_is_base64(const char *check_string)
59 if(!g_ascii_strcasecmp ((char *) check_string, "BASE64") ||
60 !g_ascii_strcasecmp ((char *) check_string, "b") )
61 return (1);
62 return (0);
65 time_t b_vformat_time_to_unix(const char *inptime)
67 char *date = NULL;
68 char *time = NULL;
69 char *ftime = NULL;
70 if ((ftime = g_strrstr(inptime, "T"))) {
72 date = g_strndup(inptime, ftime - inptime);
73 if (ftime[3] == ':')
74 time = g_strndup(ftime + 1, 8);
75 else
76 time = g_strndup(ftime + 1, 6);
77 } else {
78 date = g_strdup(inptime);
81 struct tm btime;
82 memset(&btime, 0, sizeof(struct tm));
83 btime.tm_isdst = -1;
85 if (strlen(date) == 10) {
86 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111 - 1900;
87 btime.tm_mon = date[5] * 10 + date[6] - '0' * 11 - 1;
88 btime.tm_mday = date[8] * 10 + date[9] - '0' * 11;
89 } else {
90 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111- 1900;
91 btime.tm_mon = date[4] * 10 + date[5] - '0' * 11 - 1;
92 btime.tm_mday = date[6] * 10 + date[7] - '0' * 11;
95 if (time && strlen(time) == 8) {
96 //Time
97 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
98 btime.tm_min = time[3] * 10 + time[4] - '0' * 11;
99 btime.tm_sec = time[6] * 10 + time[7] - '0' * 11;
100 } else if (time && strlen(time) == 6) {
101 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
102 btime.tm_min = time[2] * 10 + time[3] - '0' * 11;
103 btime.tm_sec = time[4] * 10 + time[5] - '0' * 11;
106 time_t utime = mktime(&btime);
107 return utime;
110 static char *_unfold_lines (char *buf)
112 GString *str = g_string_new ("");
113 GString *line = g_string_new ("");
114 char *p = buf;
115 char *next, *next2, *q;
116 gboolean newline = TRUE;
117 gboolean quotedprintable = FALSE;
120 * We're pretty liberal with line folding here. We handle
121 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
122 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
125 while (*p) {
127 /* search new lines for quoted printable encoding */
128 if (newline) {
129 for (q=p; *q != '\n' && *q != '\0'; q++)
130 line = g_string_append_unichar (line, g_utf8_get_char (q));
132 if (strstr(line->str, "ENCODING=QUOTED-PRINTABLE"))
133 quotedprintable = TRUE;
135 g_string_free(line, TRUE);
136 line = g_string_new ("");
138 newline = FALSE;
142 if ((quotedprintable && *p == '=') || *p == '\r' || *p == '\n') {
143 next = g_utf8_next_char (p);
144 if (*next == '\n' || *next == '\r') {
145 next2 = g_utf8_next_char (next);
146 if (*next2 == '\n' || *next2 == '\r' || *next2 == ' ' || *next2 == '\t') {
147 p = g_utf8_next_char (next2);
149 else if(quotedprintable && (*p == '=')) {
150 p = g_utf8_next_char (next);
152 else {
153 str = g_string_append (str, CRLF);
154 p = g_utf8_next_char (next);
155 newline = TRUE;
156 quotedprintable = FALSE;
159 else if (*p == '=') {
160 str = g_string_append_unichar (str, g_utf8_get_char (p));
161 p = g_utf8_next_char (p);
163 else if (*next == ' ' || *next == '\t') {
164 p = g_utf8_next_char (next);
166 else {
167 str = g_string_append (str, CRLF);
168 p = g_utf8_next_char (p);
169 newline = TRUE;
170 quotedprintable = FALSE;
173 else {
174 str = g_string_append_unichar (str, g_utf8_get_char (p));
175 p = g_utf8_next_char (p);
179 g_free (buf);
180 g_string_free(line, TRUE);
182 return g_string_free (str, FALSE);
185 /* skip forward until we hit the CRLF, or \0 */
186 static void _skip_to_next_line (char **p)
188 char *lp;
189 lp = *p;
191 while (*lp != '\r' && *lp != '\0')
192 lp = g_utf8_next_char (lp);
194 if (*lp == '\r') {
195 lp = g_utf8_next_char (lp); /* \n */
196 lp = g_utf8_next_char (lp); /* start of the next line */
199 *p = lp;
202 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
203 pointing at the character that causes us to stop */
204 static void _skip_until (char **p, char *s)
206 char *lp;
208 lp = *p;
210 while (*lp != '\r' && *lp != '\0') {
211 gboolean s_matches = FALSE;
212 char *ls;
213 for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
214 if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
215 s_matches = TRUE;
216 break;
220 if (s_matches)
221 break;
222 lp++;
225 *p = lp;
228 static void _read_attribute_value_add (b_VFormatAttribute *attr, GString *str, GString *charset)
230 /* don't convert empty strings */
231 if (str->len == 0) {
232 b_vformat_attribute_add_value(attr, str->str);
233 return;
236 char *inbuf, *outbuf, *p;
237 size_t inbytesleft, outbytesleft;
239 inbuf = str->str;
240 p = outbuf = malloc(str->len*2);
241 inbytesleft = str->len;
242 outbytesleft = str->len*2;
244 iconv_t cd;
246 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
247 if (charset) {
249 cd = iconv_open("UTF-8", charset->str);
250 #ifdef SOLARIS
251 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
252 #else
253 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
254 #endif
255 *p = 0;
256 b_vformat_attribute_add_value(attr, outbuf);
258 } else {
260 /* hmm, should not happen */
261 b_vformat_attribute_add_value(attr, str->str);
265 iconv_close(cd);
267 } else {
269 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
270 if (g_utf8_validate (inbuf, -1, NULL)) {
272 b_vformat_attribute_add_value (attr, str->str);
274 } else {
276 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
277 cd = iconv_open("UTF-8", "ISO-8859-1");
278 #ifdef SOLARIS
279 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
280 #else
281 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
282 #endif
283 *p = 0;
284 b_vformat_attribute_add_value (attr, outbuf);
286 } else {
288 b_vformat_attribute_add_value (attr, str->str);
292 iconv_close(cd);
298 free(outbuf);
302 static void _read_attribute_value (b_VFormatAttribute *attr, char **p, int format_encoding, GString *charset)
304 char *lp = *p;
305 GString *str;
307 /* read in the value */
308 str = g_string_new ("");
309 while (*lp != '\r' && *lp != '\0') {
310 if (*lp == '=' && format_encoding == VF_ENCODING_QP) {
311 char a, b, x1=0, x2=0;
313 if ((a = *(++lp)) == '\0') break;
314 if ((b = *(++lp)) == '\0') break;
316 if (isalnum(a)) {
317 if (isalnum(b)) {
318 /* e.g. ...N=C3=BCrnberg\r\n
319 * ^^^
321 x1=a;
322 x2=b;
324 else if (b == '=') {
325 /* e.g. ...N=C=\r\n
326 * ^^^
327 * 3=BCrnberg...
330 char *tmplp = lp;
331 if (*(++tmplp) == '\r' && *(++tmplp) == '\n' && isalnum(*(++tmplp))) {
332 x1 = a;
333 x2 = *tmplp;
334 lp = tmplp;
337 else {
338 /* append malformed input, and
339 continue parsing */
340 str = g_string_append_c(str, a);
341 str = g_string_append_c(str, b);
344 else if (a == '=') {
345 char *tmplp = lp;
346 char c, d, e;
347 c = *(++tmplp);
348 d = *(++tmplp);
349 e = *(++tmplp);
350 if (b == '\r' && c == '\n' && isalnum(d) && isalnum(e)) {
351 x1 = d;
352 x2 = e;
353 lp = tmplp;
355 else {
356 /* append malformed input, and
357 continue parsing */
358 str = g_string_append_c(str, a);
359 str = g_string_append_c(str, b);
362 else {
363 /* append malformed input, and
364 continue parsing */
365 str = g_string_append_c(str, a);
366 str = g_string_append_c(str, b);
368 if (x1 && x2) {
369 char c;
371 a = tolower (x1);
372 b = tolower (x2);
374 c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
375 | ((b>='a'?b-'a'+10:b-'0')&0x0f);
377 str = g_string_append_c (str, c);
379 lp++;
380 x1 = x2 = 0;
382 else if (format_encoding == VF_ENCODING_BASE64) {
383 if((*lp != ' ') && (*lp != '\t') )
384 str = g_string_append_unichar (str, g_utf8_get_char (lp));
385 lp = g_utf8_next_char(lp);
387 else if (*lp == '\\') {
388 /* convert back to the non-escaped version of
389 the characters */
390 lp = g_utf8_next_char(lp);
391 if (*lp == '\0') {
392 str = g_string_append_c (str, '\\');
393 break;
395 switch (*lp) {
396 case 'n': str = g_string_append_c (str, '\n'); break;
397 case 'r': str = g_string_append_c (str, '\r'); break;
398 case ';': str = g_string_append_c (str, ';'); break;
399 case ',':
400 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) {
401 //We need to handle categories here to work
402 //aroung a bug in evo2
403 _read_attribute_value_add (attr, str, charset);
404 g_string_assign (str, "");
405 } else
406 str = g_string_append_c (str, ',');
407 break;
408 case '\\': str = g_string_append_c (str, '\\'); break;
409 case '"': str = g_string_append_c (str, '"'); break;
410 /* \t is (incorrectly) used by kOrganizer, so handle it here */
411 case 't': str = g_string_append_c (str, '\t'); break;
412 default:
413 BarryLogf(TRACE_INTERNAL, _("invalid escape, passing it through. escaped char was %u"), (unsigned int)*lp);
414 str = g_string_append_c (str, '\\');
415 str = g_string_append_unichar (str, g_utf8_get_char(lp));
416 break;
418 lp = g_utf8_next_char(lp);
420 else if ((*lp == ';') ||
421 (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) {
422 _read_attribute_value_add (attr, str, charset);
423 g_string_assign (str, "");
424 lp = g_utf8_next_char(lp);
426 else {
427 str = g_string_append_unichar (str, g_utf8_get_char (lp));
428 lp = g_utf8_next_char(lp);
431 if (str) {
432 _read_attribute_value_add (attr, str, charset);
433 g_string_free (str, TRUE);
436 if (*lp == '\r') {
437 lp = g_utf8_next_char (lp); /* \n */
438 lp = g_utf8_next_char (lp); /* start of the next line */
441 *p = lp;
444 static void _read_attribute_params(b_VFormatAttribute *attr, char **p, int *format_encoding, GString **charset)
446 char *lp = *p;
447 GString *str;
448 b_VFormatParam *param = NULL;
449 gboolean in_quote = FALSE;
450 str = g_string_new ("");
452 while (*lp != '\0') {
453 if (*lp == '"') {
454 in_quote = !in_quote;
455 lp = g_utf8_next_char (lp);
457 else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/' || *lp == '.' || *lp == ' ') {
458 str = g_string_append_unichar (str, g_utf8_get_char (lp));
459 lp = g_utf8_next_char (lp);
461 /* accumulate until we hit the '=' or ';'. If we hit
462 * a '=' the string contains the parameter name. if
463 * we hit a ';' the string contains the parameter
464 * value and the name is either ENCODING (if value ==
465 * QUOTED-PRINTABLE) or TYPE (in any other case.)
467 else if (*lp == '=') {
468 if (str->len > 0) {
469 param = b_vformat_attribute_param_new (str->str);
470 g_string_assign (str, "");
471 lp = g_utf8_next_char (lp);
473 else {
474 _skip_until (&lp, ":;");
475 if (*lp == '\r') {
476 lp = g_utf8_next_char (lp); /* \n */
477 lp = g_utf8_next_char (lp); /* start of the next line */
478 break;
480 else if (*lp == ';')
481 lp = g_utf8_next_char (lp);
484 else if (*lp == ';' || *lp == ':' || *lp == ',') {
485 gboolean colon = (*lp == ':');
486 gboolean comma = (*lp == ',');
488 if (param) {
489 if (str->len > 0) {
490 b_vformat_attribute_param_add_value (param, str->str);
491 g_string_assign (str, "");
492 if (!colon)
493 lp = g_utf8_next_char (lp);
495 else {
496 /* we've got a parameter of the form:
497 * PARAM=(.*,)?[:;]
498 * so what we do depends on if there are already values
499 * for the parameter. If there are, we just finish
500 * this parameter and skip past the offending character
501 * (unless it's the ':'). If there aren't values, we free
502 * the parameter then skip past the character.
504 if (!param->values) {
505 b_vformat_attribute_param_free (param);
506 param = NULL;
507 if (!colon)
508 lp = g_utf8_next_char (lp);
512 if (param
513 && !g_ascii_strcasecmp (param->name, "encoding")) {
514 if (!g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
515 *format_encoding = VF_ENCODING_QP;
516 b_vformat_attribute_param_free (param);
517 param = NULL;
518 } else if ( _helper_is_base64(param->values->data)) {
519 *format_encoding = VF_ENCODING_BASE64;
520 // b_vformat_attribute_param_free (param);
521 // param = NULL;
523 } else if (param && !g_ascii_strcasecmp(param->name, "charset")) {
524 *charset = g_string_new(param->values->data);
525 b_vformat_attribute_param_free (param);
526 param = NULL;
529 else {
530 if (str->len > 0) {
531 char *param_name;
532 if (!g_ascii_strcasecmp (str->str,
533 "quoted-printable")) {
534 param_name = "ENCODING";
535 *format_encoding = VF_ENCODING_QP;
537 /* apple's broken addressbook app outputs naked BASE64
538 parameters, which aren't even vcard 3.0 compliant. */
539 else if (!g_ascii_strcasecmp (str->str,
540 "base64")) {
541 param_name = "ENCODING";
542 g_string_assign (str, "b");
543 *format_encoding = VF_ENCODING_BASE64;
545 else {
546 param_name = "TYPE";
549 if (param_name) {
550 param = b_vformat_attribute_param_new (param_name);
551 b_vformat_attribute_param_add_value (param, str->str);
553 g_string_assign (str, "");
554 if (!colon)
555 lp = g_utf8_next_char (lp);
557 else {
558 /* we've got an attribute with a truly empty
559 attribute parameter. So it's of the form:
561 ATTR;[PARAM=value;]*;[PARAM=value;]*:
563 (note the extra ';')
565 the only thing to do here is, well.. nothing.
566 we skip over the character if it's not a colon,
567 and the rest is handled for us: We'll either
568 continue through the loop again if we hit a ';',
569 or we'll break out correct below if it was a ':' */
570 if (!colon)
571 lp = g_utf8_next_char (lp);
574 if (param && !comma) {
575 b_vformat_attribute_add_param (attr, param);
576 param = NULL;
578 if (colon)
579 break;
581 else {
582 BarryLogf(TRACE_INTERNAL, _("invalid character found in parameter spec: \"%i\" String so far: %s"), lp[0], str->str);
583 g_string_assign (str, "");
584 _skip_until (&lp, ":;");
588 if (str)
589 g_string_free (str, TRUE);
591 *p = lp;
594 /* reads an entire attribute from the input buffer, leaving p pointing
595 at the start of the next line (past the \r\n) */
596 static b_VFormatAttribute *_read_attribute (char **p)
598 char *attr_group = NULL;
599 char *attr_name = NULL;
600 b_VFormatAttribute *attr = NULL;
601 GString *str, *charset = NULL;
602 char *lp = *p;
604 gboolean is_qp = FALSE;
606 /* first read in the group/name */
607 str = g_string_new ("");
608 while (*lp != '\r' && *lp != '\0') {
609 if (*lp == ':' || *lp == ';') {
610 if (str->len != 0) {
611 /* we've got a name, break out to the value/attribute parsing */
612 attr_name = g_string_free (str, FALSE);
613 break;
615 else {
616 /* a line of the form:
617 * (group.)?[:;]
619 * since we don't have an attribute
620 * name, skip to the end of the line
621 * and try again.
623 g_string_free (str, TRUE);
624 *p = lp;
625 _skip_to_next_line(p);
626 goto lose;
629 else if (*lp == '.') {
630 if (attr_group) {
631 BarryLogf(TRACE_INTERNAL, _("extra `.' in attribute specification. ignoring extra group `%s'"), str->str);
632 g_string_free (str, TRUE);
633 str = g_string_new ("");
635 if (str->len != 0) {
636 attr_group = g_string_free (str, FALSE);
637 str = g_string_new ("");
640 else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/') {
641 str = g_string_append_unichar (str, g_utf8_get_char (lp));
643 else {
644 BarryLogf(TRACE_INTERNAL, _("invalid character found in attribute group/name: \"%i\" String so far: %s"), lp[0], str->str);
645 g_string_free (str, TRUE);
646 *p = lp;
647 _skip_to_next_line(p);
648 goto lose;
651 lp = g_utf8_next_char(lp);
654 if (!attr_name) {
655 _skip_to_next_line (p);
656 goto lose;
659 attr = b_vformat_attribute_new (attr_group, attr_name);
660 g_free (attr_group);
661 g_free (attr_name);
663 if (*lp == ';') {
664 /* skip past the ';' */
665 lp = g_utf8_next_char(lp);
666 _read_attribute_params (attr, &lp, &is_qp, &charset);
668 if (*lp == ':') {
669 /* skip past the ':' */
670 lp = g_utf8_next_char(lp);
671 _read_attribute_value (attr, &lp, is_qp, charset);
674 if (charset) g_string_free(charset, TRUE);
675 *p = lp;
677 if (!attr->values)
678 goto lose;
680 return attr;
681 lose:
682 if (attr)
683 b_vformat_attribute_free (attr);
684 return NULL;
687 static void open_block(char **block, const char *block_name)
689 char *start = *block ? *block : "";
690 char *result = NULL;
692 result = g_strconcat(start, "/", block_name, NULL);
693 if( *block )
694 g_free(*block);
695 *block = result;
698 static void close_block(char **block, const char *block_name)
700 int name_len = strlen(block_name);
701 int block_len = *block ? strlen(*block) : 0;
702 char *cmp_start = NULL;
704 if( block_len < name_len + 1 )
705 return;
707 cmp_start = *block + (block_len - name_len - 1);
708 if( cmp_start[0] == '/' &&
709 g_ascii_strcasecmp(cmp_start+1, block_name) == 0 )
711 // end of block hierarchy contains block name,
712 // so safe to remove
714 // cut off the end of the string... no need to free/realloc
715 *cmp_start = '\0';
719 /* we try to be as forgiving as we possibly can here - this isn't a
720 * validator. Almost nothing is considered a fatal error. We always
721 * try to return *something*.
723 static void _parse(b_VFormat *evc, const char *str)
725 char *buf = g_strdup (str);
726 char *p, *end;
727 b_VFormatAttribute *attr;
729 /* first validate the string is valid utf8 */
730 if (!g_utf8_validate (buf, -1, (const char **)&end)) {
731 /* if the string isn't valid, we parse as much as we can from it */
732 BarryLogf(TRACE_INTERNAL, _("invalid utf8 passed to b_VFormat. Limping along."));
733 *end = '\0';
736 buf = _unfold_lines (buf);
738 p = buf;
740 attr = _read_attribute (&p);
741 if (!attr)
742 attr = _read_attribute (&p);
744 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
745 BarryLogf(TRACE_INTERNAL, _("vformat began without a BEGIN\n"));
747 if (attr && !g_ascii_strcasecmp (attr->name, "begin"))
748 b_vformat_attribute_free (attr);
749 else if (attr)
750 b_vformat_add_attribute (evc, attr);
752 char *block = NULL;
753 while (*p) {
754 b_VFormatAttribute *next_attr = _read_attribute (&p);
756 if (next_attr) {
757 if( g_ascii_strcasecmp(next_attr->name, "begin") == 0 ) {
758 // add to block hierarchy string
759 char *value = b_vformat_attribute_get_value(next_attr);
760 open_block(&block, value);
761 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
762 g_free(value);
764 else if( g_ascii_strcasecmp(next_attr->name, "end") == 0 ) {
765 // close off the block
766 char *value = b_vformat_attribute_get_value(next_attr);
767 close_block(&block, value);
768 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
769 g_free(value);
772 // apply the block to the attr
773 next_attr->block = g_strdup(block);
775 // add!
776 b_vformat_add_attribute (evc, next_attr);
777 attr = next_attr;
781 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
782 BarryLogf(TRACE_INTERNAL, _("vformat ended without END"));
785 g_free (buf);
786 g_free (block);
789 char *b_vformat_escape_string (const char *s, b_VFormatType type)
791 GString *str;
792 const char *p;
794 str = g_string_new ("");
796 /* Escape a string as described in RFC2426, section 5 */
797 for (p = s; p && *p; p++) {
798 switch (*p) {
799 case '\n':
800 str = g_string_append (str, "\\n");
801 break;
802 case '\r':
803 if (*(p+1) == '\n')
804 p++;
805 str = g_string_append (str, "\\n");
806 break;
807 case ';':
808 str = g_string_append (str, "\\;");
809 break;
810 case ',':
811 if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20)
812 str = g_string_append (str, "\\,");
813 else
814 str = g_string_append_c (str, *p);
815 break;
816 case '\\':
818 * We won't escape backslashes
819 * on vcard 2.1, unless it is in the end of a value.
820 * See comments above for a better explanation
822 if (*p != '\0' && type == VFORMAT_CARD_21) {
823 BarryLogf(TRACE_INTERNAL, _("[%s]We won't escape backslashes"), __func__);
824 str = g_string_append_c(str, *p);
826 else {
827 BarryLogf(TRACE_INTERNAL, _("[%s] escape backslashes!!"), __func__);
828 str = g_string_append (str, "\\\\");
830 break;
831 default:
832 str = g_string_append_c (str, *p);
833 break;
837 return g_string_free (str, FALSE);
840 char*
841 b_vformat_unescape_string (const char *s)
843 GString *str;
844 const char *p;
846 g_return_val_if_fail (s != NULL, NULL);
848 str = g_string_new ("");
850 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
851 for (p = s; *p; p++) {
852 if (*p == '\\') {
853 p++;
854 if (*p == '\0') {
855 str = g_string_append_c (str, '\\');
856 break;
858 switch (*p) {
859 case 'n': str = g_string_append_c (str, '\n'); break;
860 case 'r': str = g_string_append_c (str, '\r'); break;
861 case ';': str = g_string_append_c (str, ';'); break;
862 case ',': str = g_string_append_c (str, ','); break;
863 case '\\': str = g_string_append_c (str, '\\'); break;
864 case '"': str = g_string_append_c (str, '"'); break;
865 /* \t is (incorrectly) used by kOrganizer, so handle it here */
866 case 't': str = g_string_append_c (str, '\t'); break;
867 default:
868 BarryLogf(TRACE_INTERNAL, _("invalid escape, passing it through. escaped char was %u"), (unsigned int)*p);
869 str = g_string_append_c (str, '\\');
870 str = g_string_append_unichar (str, g_utf8_get_char(p));
871 break;
876 return g_string_free (str, FALSE);
879 void
880 b_vformat_construct (b_VFormat *evc, const char *str)
882 g_return_if_fail (str != NULL);
884 if (*str)
885 _parse (evc, str);
888 void b_vformat_free(b_VFormat *format)
890 g_list_foreach (format->attributes, (GFunc)b_vformat_attribute_free, NULL);
891 g_list_free (format->attributes);
892 g_free(format);
895 b_VFormat *b_vformat_new_from_string (const char *str)
897 g_return_val_if_fail (str != NULL, NULL);
898 b_VFormat *evc = g_malloc0(sizeof(b_VFormat));
900 b_vformat_construct (evc, str);
902 return evc;
905 b_VFormat *b_vformat_new(void)
907 return b_vformat_new_from_string ("");
910 static int _block_match(b_VFormatAttribute *attr, const char *block)
912 // a block matches if the end of the attribute's block
913 // string matches a case insensitive compare with block
915 // for example, a calendar may or may not start with a
916 // BEGIN: VCALENDAR, so DTSTART's block string could be
917 // "/vcalendar/vevent" or just "/vevent". By passing
918 // "/vevent" or even "vevent" as the block argument above,
919 // we should get a match for any of the above.
921 int attr_len = attr->block ? strlen(attr->block) : 0;
922 int block_len = block ? strlen(block) : 0;
924 if( block == NULL )
925 return 1; // if block is null, match everything
927 if( attr_len < block_len )
928 return 0; // not enough string to compare
930 if( attr_len == 0 && block_len == 0 )
931 return 1; // empty and null strings match
933 if( attr->block == NULL )
934 return 0; // don't compare if one side is null
936 return g_ascii_strcasecmp(&attr->block[attr_len - block_len], block) == 0;
939 b_VFormatAttribute *b_vformat_find_attribute(b_VFormat *vcard, const char *name, int nth, const char *block)
941 GList *attributes = b_vformat_get_attributes(vcard);
942 GList *a = NULL;
943 int i = 0;
944 for (a = attributes; a; a = a->next) {
945 b_VFormatAttribute *attr = a->data;
946 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
947 if( block == NULL || _block_match(attr, block) ) {
948 if( i == nth )
949 return attr;
950 i++;
954 return NULL;
958 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
959 const char *name,
960 int nth)
962 GList *attributes = last ? last->next : 0;
963 GList *a = NULL;
964 int i = 0;
965 for (a = attributes; a; a = a->next) {
966 b_VFormatAttribute *attr = a->data;
967 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
968 if( i == nth )
969 return attr;
970 i++;
973 return NULL;
977 char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type)
979 BarryLogf(TRACE_ENTRY, "%s(%p, %i)", __func__, evc, type);
980 GList *l;
981 GList *v;
983 GString *str = g_string_new ("");
985 switch (type) {
986 case VFORMAT_CARD_21:
987 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
988 break;
989 case VFORMAT_CARD_30:
990 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
991 break;
992 case VFORMAT_TODO_10:
993 case VFORMAT_EVENT_10:
994 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
995 break;
996 case VFORMAT_TODO_20:
997 case VFORMAT_EVENT_20:
998 case VFORMAT_JOURNAL:
999 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
1000 break;
1001 case VFORMAT_NOTE:
1002 str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
1003 break;
1006 for (l = evc->attributes; l; l = l->next) {
1007 GList *p;
1008 b_VFormatAttribute *attr = l->data;
1009 GString *attr_str;
1010 int l;
1011 int format_encoding = VF_ENCODING_RAW;
1013 attr_str = g_string_new ("");
1015 /* From rfc2425, 5.8.2
1017 * contentline = [group "."] name *(";" param) ":" value CRLF
1020 if (attr->group) {
1021 attr_str = g_string_append (attr_str, attr->group);
1022 attr_str = g_string_append_c (attr_str, '.');
1024 attr_str = g_string_append (attr_str, attr->name);
1025 /* handle the parameters */
1026 for (p = attr->params; p; p = p->next) {
1027 b_VFormatParam *param = p->data;
1028 /* 5.8.2:
1029 * param = param-name "=" param-value *("," param-value)
1031 if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20
1032 || type == VFORMAT_EVENT_20 || type == VFORMAT_JOURNAL) {
1035 * Character set can only be specified on the CHARSET
1036 * parameter on the Content-Type MIME header field.
1038 if (!g_ascii_strcasecmp (param->name, "CHARSET"))
1039 continue;
1040 attr_str = g_string_append_c (attr_str, ';');
1041 attr_str = g_string_append (attr_str, param->name);
1042 if (param->values) {
1043 attr_str = g_string_append_c (attr_str, '=');
1045 for (v = param->values; v; v = v->next) {
1046 if (_helper_is_base64((const char *) v->data)) {
1047 format_encoding = VF_ENCODING_BASE64;
1048 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1049 v->data=g_strdup("B");
1052 * QUOTED-PRINTABLE inline encoding has been
1053 * eliminated.
1055 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) {
1056 BarryLogf(TRACE_ERROR, _("%s false encoding QUOTED-PRINTABLE is not allowed"), __func__);
1057 format_encoding = VF_ENCODING_QP;
1059 attr_str = g_string_append (attr_str, v->data);
1061 if (v->next)
1062 attr_str = g_string_append_c (attr_str, ',');
1065 else {
1066 attr_str = g_string_append_c (attr_str, ';');
1068 * The "TYPE=" is optional skip it.
1069 * LOGO, PHOTO and SOUND multimedia formats MUST
1070 * have a "TYPE=" parameter
1072 gboolean must_have_type = FALSE;
1073 if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") )
1074 must_have_type = TRUE;
1075 if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") )
1076 attr_str = g_string_append (attr_str, param->name);
1077 if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) )
1078 attr_str = g_string_append_c (attr_str, '=');
1079 for (v = param->values; v; v = v->next) {
1080 // check for quoted-printable encoding
1081 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE"))
1082 format_encoding = VF_ENCODING_QP;
1083 // check for base64 encoding
1084 if (_helper_is_base64((const char *) v->data)) {
1085 format_encoding = VF_ENCODING_BASE64;
1086 v->data=g_strdup("BASE64");
1088 attr_str = g_string_append (attr_str, v->data);
1089 if (v->next)
1090 attr_str = g_string_append_c (attr_str, ',');
1095 attr_str = g_string_append_c (attr_str, ':');
1097 for (v = attr->values; v; v = v->next) {
1098 char *value = v->data;
1099 char *escaped_value = NULL;
1101 if (!g_ascii_strcasecmp (attr->name, "RRULE") &&
1102 strstr (value, "BYDAY") == v->data) {
1103 attr_str = g_string_append (attr_str, value);
1104 } else {
1105 escaped_value = b_vformat_escape_string (value, type);
1106 attr_str = g_string_append (attr_str, escaped_value);
1109 if (v->next) {
1111 /* XXX toshok - i hate you, rfc 2426.
1112 why doesn't CATEGORIES use a ; like
1113 a normal list attribute? */
1114 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
1115 attr_str = g_string_append_c (attr_str, ',');
1116 else
1117 attr_str = g_string_append_c (attr_str, ';');
1120 g_free (escaped_value);
1123 /* Folding lines:
1124 * ^^^^^^^^^^^^^^
1126 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1127 * After generating a content line,
1128 * lines longer than 75 characters SHOULD be folded according to the
1129 * folding procedure described in [MIME-DIR].
1131 * rfc 2445 (iCalendar), 4.1 Content Lines:
1132 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1133 * break. Long content lines SHOULD be split into a multiple line
1134 * representations using a line "folding" technique. That is, a long
1135 * line can be split between any two characters by inserting a CRLF
1136 * immediately followed by a single linear white space character (i.e.,
1137 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1138 * of CRLF followed immediately by a single linear white space character
1139 * is ignored (i.e., removed) when processing the content type.
1141 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1142 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1144 * Differences between encodings:
1145 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1147 * rfc 2425 [MIME-DIR], 5.8.1:
1148 * A logical line MAY be continued on the next physical line anywhere
1149 * between two characters by inserting a CRLF immediately followed by a
1150 * single <WS> (white space) character.
1152 * rfc 2045, 6.7, chapter 5:
1153 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1154 * without follwing <WS>
1156 * UTF-8
1157 * ^^^^^
1159 * Note that all the line folding above is described in terms of characters
1160 * not bytes. In particular, it would be an error to put a line break
1161 * within a UTF-8 character.
1164 l = 0;
1165 do {
1166 if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) {
1167 l += 75;
1169 /* If using QP, must be sure that we do not fold within a quote sequence */
1170 if (format_encoding == VF_ENCODING_QP) {
1171 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--;
1172 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2;
1175 char *p = g_utf8_offset_to_pointer(attr_str->str, l);
1177 if (format_encoding == VF_ENCODING_QP)
1178 attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1);
1179 else
1180 attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1);
1182 else
1183 break;
1184 } while (l < g_utf8_strlen(attr_str->str, attr_str->len));
1186 attr_str = g_string_append (attr_str, CRLF);
1188 * base64= <MIME RFC 1521 base64 text>
1189 * the end of the text is marked with two CRLF sequences
1190 * this results in one blank line before the start of the
1191 * next property
1193 if( format_encoding == VF_ENCODING_BASE64
1194 && (type == VFORMAT_CARD_21))
1195 attr_str = g_string_append (attr_str, CRLF);
1197 str = g_string_append (str, attr_str->str);
1198 g_string_free (attr_str, TRUE);
1201 switch (type) {
1202 case VFORMAT_CARD_21:
1203 str = g_string_append (str, "END:VCARD\r\n");
1204 break;
1205 case VFORMAT_CARD_30:
1206 str = g_string_append (str, "END:VCARD\r\n");
1207 break;
1208 case VFORMAT_TODO_10:
1209 case VFORMAT_EVENT_10:
1210 str = g_string_append (str, "END:VCALENDAR\r\n");
1211 break;
1212 case VFORMAT_TODO_20:
1213 case VFORMAT_EVENT_20:
1214 case VFORMAT_JOURNAL:
1215 str = g_string_append (str, "END:VCALENDAR\r\n");
1216 break;
1217 case VFORMAT_NOTE:
1218 str = g_string_append (str, "END:VNOTE\r\n");
1219 break;
1222 BarryLogf(TRACE_EXIT, "%s", __func__);
1223 return g_string_free (str, FALSE);
1226 void b_vformat_dump_structure (b_VFormat *evc)
1228 GList *a;
1229 GList *v;
1230 int i;
1232 printf ("b_VFormat\n");
1233 for (a = evc->attributes; a; a = a->next) {
1234 GList *p;
1235 b_VFormatAttribute *attr = a->data;
1236 printf ("+-- %s\n", attr->name);
1237 if (attr->params) {
1238 printf (" +- params=\n");
1240 for (p = attr->params, i = 0; p; p = p->next, i++) {
1241 b_VFormatParam *param = p->data;
1242 printf (" | [%d] = %s", i,param->name);
1243 printf ("(");
1244 for (v = param->values; v; v = v->next) {
1245 char *value = b_vformat_escape_string ((char*)v->data, VFORMAT_CARD_21);
1246 printf ("%s", value);
1247 if (v->next)
1248 printf (",");
1249 g_free (value);
1252 printf (")\n");
1255 printf (" +- values=\n");
1256 for (v = attr->values, i = 0; v; v = v->next, i++) {
1257 printf (" [%d] = `%s'\n", i, (char*)v->data);
1262 b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name)
1264 b_VFormatAttribute *attr;
1266 attr = g_new0 (b_VFormatAttribute, 1);
1268 attr->group = g_strdup (attr_group);
1269 attr->name = g_strdup (attr_name);
1271 return attr;
1274 void
1275 b_vformat_attribute_free (b_VFormatAttribute *attr)
1277 g_return_if_fail (attr != NULL);
1279 g_free (attr->block);
1280 g_free (attr->group);
1281 g_free (attr->name);
1283 b_vformat_attribute_remove_values (attr);
1285 b_vformat_attribute_remove_params (attr);
1287 g_free (attr);
1290 b_VFormatAttribute*
1291 b_vformat_attribute_copy (b_VFormatAttribute *attr)
1293 b_VFormatAttribute *a;
1294 GList *p;
1296 g_return_val_if_fail (attr != NULL, NULL);
1298 a = b_vformat_attribute_new (b_vformat_attribute_get_group (attr),
1299 b_vformat_attribute_get_name (attr));
1301 for (p = attr->values; p; p = p->next)
1302 b_vformat_attribute_add_value (a, p->data);
1304 for (p = attr->params; p; p = p->next)
1305 b_vformat_attribute_add_param (a, b_vformat_attribute_param_copy (p->data));
1307 return a;
1310 void
1311 b_vformat_remove_attributes (b_VFormat *evc, const char *attr_group, const char *attr_name)
1313 GList *attr;
1315 g_return_if_fail (attr_name != NULL);
1317 attr = evc->attributes;
1318 while (attr) {
1319 GList *next_attr;
1320 b_VFormatAttribute *a = attr->data;
1322 next_attr = attr->next;
1324 if (((!attr_group && !a->group) ||
1325 (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
1326 ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
1328 /* matches, remove/delete the attribute */
1329 evc->attributes = g_list_remove_link (evc->attributes, attr);
1331 b_vformat_attribute_free (a);
1334 attr = next_attr;
1338 void
1339 b_vformat_remove_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1341 g_return_if_fail (attr != NULL);
1343 evc->attributes = g_list_remove (evc->attributes, attr);
1344 b_vformat_attribute_free (attr);
1347 void
1348 b_vformat_add_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1350 g_return_if_fail (attr != NULL);
1352 evc->attributes = g_list_append (evc->attributes, attr);
1355 void
1356 b_vformat_add_attribute_with_value (b_VFormat *b_VFormat,
1357 b_VFormatAttribute *attr, const char *value)
1359 g_return_if_fail (attr != NULL);
1361 b_vformat_attribute_add_value (attr, value);
1363 b_vformat_add_attribute (b_VFormat, attr);
1366 void
1367 b_vformat_add_attribute_with_values (b_VFormat *b_VFormat, b_VFormatAttribute *attr, ...)
1369 va_list ap;
1370 char *v;
1372 g_return_if_fail (attr != NULL);
1374 va_start (ap, attr);
1376 while ((v = va_arg (ap, char*))) {
1377 b_vformat_attribute_add_value (attr, v);
1380 va_end (ap);
1382 b_vformat_add_attribute (b_VFormat, attr);
1385 void
1386 b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value)
1388 g_return_if_fail (attr != NULL);
1390 attr->values = g_list_append (attr->values, g_strdup (value));
1393 void
1394 b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len)
1396 g_return_if_fail (attr != NULL);
1398 switch (attr->encoding) {
1399 case VF_ENCODING_RAW:
1400 BarryLogf(TRACE_INTERNAL, _("can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first"));
1401 break;
1402 case VF_ENCODING_BASE64: {
1403 char *b64_data = base64_encode_simple (value, len);
1404 GString *decoded = g_string_new_len (value, len);
1406 /* make sure the decoded list is up to date */
1407 b_vformat_attribute_get_values_decoded (attr);
1409 attr->values = g_list_append (attr->values, b64_data);
1410 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1411 break;
1413 case VF_ENCODING_QP: {
1414 char *qp_data = quoted_encode_simple ((unsigned char*)value, len);
1415 GString *decoded = g_string_new (value);
1417 /* make sure the decoded list is up to date */
1418 b_vformat_attribute_get_values_decoded (attr);
1420 attr->values = g_list_append (attr->values, qp_data);
1421 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1422 break;
1424 case VF_ENCODING_8BIT: {
1425 char *data = g_strdup(value);
1426 GString *decoded = g_string_new (value);
1428 /* make sure the decoded list is up to date */
1429 b_vformat_attribute_get_values_decoded (attr);
1431 attr->values = g_list_append (attr->values, data);
1432 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1433 break;
1438 void
1439 b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...)
1441 va_list ap;
1442 char *v;
1444 g_return_if_fail (attr != NULL);
1446 va_start (ap, attr);
1448 while ((v = va_arg (ap, char*))) {
1449 b_vformat_attribute_add_value (attr, v);
1452 va_end (ap);
1455 static void
1456 free_gstring (GString *str)
1458 g_string_free (str, TRUE);
1461 void
1462 b_vformat_attribute_remove_values (b_VFormatAttribute *attr)
1464 g_return_if_fail (attr != NULL);
1466 g_list_foreach (attr->values, (GFunc)g_free, NULL);
1467 g_list_free (attr->values);
1468 attr->values = NULL;
1470 g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
1471 g_list_free (attr->decoded_values);
1472 attr->decoded_values = NULL;
1475 void
1476 b_vformat_attribute_remove_params (b_VFormatAttribute *attr)
1478 g_return_if_fail (attr != NULL);
1480 g_list_foreach (attr->params, (GFunc)b_vformat_attribute_param_free, NULL);
1481 g_list_free (attr->params);
1482 attr->params = NULL;
1484 /* also remove the cached encoding on this attribute */
1485 attr->encoding_set = FALSE;
1486 attr->encoding = VF_ENCODING_RAW;
1489 b_VFormatParam*
1490 b_vformat_attribute_param_new (const char *name)
1492 b_VFormatParam *param = g_new0 (b_VFormatParam, 1);
1493 param->name = g_strdup (name);
1495 return param;
1498 void
1499 b_vformat_attribute_param_free (b_VFormatParam *param)
1501 g_return_if_fail (param != NULL);
1503 g_free (param->name);
1505 b_vformat_attribute_param_remove_values (param);
1507 g_free (param);
1510 b_VFormatParam*
1511 b_vformat_attribute_param_copy (b_VFormatParam *param)
1513 b_VFormatParam *p;
1514 GList *l;
1516 g_return_val_if_fail (param != NULL, NULL);
1518 p = b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param));
1520 for (l = param->values; l; l = l->next) {
1521 b_vformat_attribute_param_add_value (p, l->data);
1524 return p;
1527 void
1528 b_vformat_attribute_add_param (b_VFormatAttribute *attr,
1529 b_VFormatParam *param)
1531 g_return_if_fail (attr != NULL);
1532 g_return_if_fail (param != NULL);
1534 attr->params = g_list_append (attr->params, param);
1536 /* we handle our special encoding stuff here */
1538 if (!g_ascii_strcasecmp (param->name, "ENCODING")) {
1539 if (attr->encoding_set) {
1540 BarryLogf(TRACE_INTERNAL, _("ENCODING specified twice"));
1541 return;
1544 if (param->values && param->values->data) {
1545 if (_helper_is_base64((const char*)param->values->data))
1546 attr->encoding = VF_ENCODING_BASE64;
1547 else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE"))
1548 attr->encoding = VF_ENCODING_QP;
1549 else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT"))
1550 attr->encoding = VF_ENCODING_8BIT;
1551 else {
1552 BarryLogf(TRACE_INTERNAL, _("Unknown value `%s' for ENCODING parameter. values will be treated as raw"), (char*)param->values->data);
1555 attr->encoding_set = TRUE;
1557 else {
1558 BarryLogf(TRACE_INTERNAL, _("ENCODING parameter added with no value"));
1563 b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level)
1565 g_return_val_if_fail (attr != NULL, NULL);
1566 GList *p = NULL;
1567 for (p = attr->params; p; p = p->next) {
1568 b_VFormatParam *param = p->data;
1569 if (!g_ascii_strcasecmp (param->name, name)) {
1570 if( level == 0 )
1571 return param;
1572 else
1573 level--;
1576 return NULL;
1579 void
1580 b_vformat_attribute_set_value (b_VFormatAttribute *attr,
1581 int nth, const char *value)
1583 GList *param = g_list_nth(attr->values, nth);
1584 g_free(param->data);
1585 param->data = g_strdup(value);
1588 void
1589 b_vformat_attribute_param_add_value (b_VFormatParam *param,
1590 const char *value)
1592 g_return_if_fail (param != NULL);
1594 param->values = g_list_append (param->values, g_strdup (value));
1597 void
1598 b_vformat_attribute_param_add_values (b_VFormatParam *param,
1599 ...)
1601 va_list ap;
1602 char *v;
1604 g_return_if_fail (param != NULL);
1606 va_start (ap, param);
1608 while ((v = va_arg (ap, char*))) {
1609 b_vformat_attribute_param_add_value (param, v);
1612 va_end (ap);
1615 void
1616 b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value)
1618 g_return_if_fail (attr != NULL);
1619 g_return_if_fail (name != NULL);
1621 if (!value)
1622 return;
1624 b_VFormatParam *param = b_vformat_attribute_param_new(name);
1626 b_vformat_attribute_param_add_value (param, value);
1628 b_vformat_attribute_add_param (attr, param);
1631 void
1632 b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr,
1633 b_VFormatParam *param, ...)
1635 va_list ap;
1636 char *v;
1638 g_return_if_fail (attr != NULL);
1639 g_return_if_fail (param != NULL);
1641 va_start (ap, param);
1643 while ((v = va_arg (ap, char*))) {
1644 b_vformat_attribute_param_add_value (param, v);
1647 va_end (ap);
1649 b_vformat_attribute_add_param (attr, param);
1652 void
1653 b_vformat_attribute_param_remove_values (b_VFormatParam *param)
1655 g_return_if_fail (param != NULL);
1657 g_list_foreach (param->values, (GFunc)g_free, NULL);
1658 g_list_free (param->values);
1659 param->values = NULL;
1662 GList*
1663 b_vformat_get_attributes (b_VFormat *format)
1665 return format->attributes;
1668 const char*
1669 b_vformat_attribute_get_group (b_VFormatAttribute *attr)
1671 g_return_val_if_fail (attr != NULL, NULL);
1673 return attr->group;
1676 const char*
1677 b_vformat_attribute_get_name (b_VFormatAttribute *attr)
1679 g_return_val_if_fail (attr != NULL, NULL);
1681 return attr->name;
1684 const char*
1685 b_vformat_attribute_get_block (b_VFormatAttribute *attr)
1687 g_return_val_if_fail (attr != NULL, NULL);
1689 return attr->block;
1692 GList*
1693 b_vformat_attribute_get_values (b_VFormatAttribute *attr)
1695 g_return_val_if_fail (attr != NULL, NULL);
1697 return attr->values;
1700 GList*
1701 b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr)
1703 g_return_val_if_fail (attr != NULL, NULL);
1705 if (!attr->decoded_values) {
1706 GList *l;
1707 switch (attr->encoding) {
1708 case VF_ENCODING_RAW:
1709 case VF_ENCODING_8BIT:
1710 for (l = attr->values; l; l = l->next)
1711 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data));
1712 break;
1713 case VF_ENCODING_BASE64:
1714 for (l = attr->values; l; l = l->next) {
1715 char *decoded = g_strdup ((char*)l->data);
1716 int len = base64_decode_simple (decoded, strlen (decoded));
1717 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1718 g_free (decoded);
1720 break;
1721 case VF_ENCODING_QP:
1722 for (l = attr->values; l; l = l->next) {
1723 if (!(l->data))
1724 continue;
1725 char *decoded = g_strdup ((char*)l->data);
1726 int len = quoted_decode_simple (decoded, strlen (decoded));
1727 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1728 g_free (decoded);
1730 break;
1734 return attr->decoded_values;
1737 gboolean
1738 b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr)
1740 g_return_val_if_fail (attr != NULL, FALSE);
1742 if (attr->values == NULL
1743 || attr->values->next != NULL)
1744 return FALSE;
1746 return TRUE;
1749 char*
1750 b_vformat_attribute_get_value (b_VFormatAttribute *attr)
1752 GList *values;
1754 g_return_val_if_fail (attr != NULL, NULL);
1756 values = b_vformat_attribute_get_values (attr);
1758 if (!b_vformat_attribute_is_single_valued (attr))
1759 BarryLogf(TRACE_INTERNAL, _("b_vformat_attribute_get_value called on multivalued attribute"));
1761 return values ? g_strdup ((char*)values->data) : NULL;
1764 GString*
1765 b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr)
1767 GList *values;
1768 GString *str = NULL;
1770 g_return_val_if_fail (attr != NULL, NULL);
1772 values = b_vformat_attribute_get_values_decoded (attr);
1774 if (!b_vformat_attribute_is_single_valued (attr))
1775 BarryLogf(TRACE_INTERNAL, _("b_vformat_attribute_get_value_decoded called on multivalued attribute"));
1777 if (values)
1778 str = values->data;
1780 return str ? g_string_new_len (str->str, str->len) : NULL;
1783 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth)
1785 GList *values = b_vformat_attribute_get_values_decoded(attr);
1786 if (!values)
1787 return NULL;
1788 GString *retstr = (GString *)g_list_nth_data(values, nth);
1789 if (!retstr)
1790 return NULL;
1792 if (!g_utf8_validate(retstr->str, -1, NULL)) {
1793 values = b_vformat_attribute_get_values(attr);
1794 if (!values)
1795 return NULL;
1796 return g_list_nth_data(values, nth);
1799 return retstr->str;
1802 gboolean
1803 b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr)
1805 GList *params;
1806 GList *p;
1808 g_return_val_if_fail (attr != NULL, FALSE);
1809 g_return_val_if_fail (typestr != NULL, FALSE);
1811 params = b_vformat_attribute_get_params (attr);
1813 for (p = params; p; p = p->next) {
1814 b_VFormatParam *param = p->data;
1816 if (!strcasecmp (b_vformat_attribute_param_get_name (param), "TYPE")) {
1817 GList *values = b_vformat_attribute_param_get_values (param);
1818 GList *v;
1820 for (v = values; v; v = v->next) {
1821 if (!strcasecmp ((char*)v->data, typestr))
1822 return TRUE;
1827 return FALSE;
1831 gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name)
1833 g_return_val_if_fail (attr != NULL, FALSE);
1834 g_return_val_if_fail (name != NULL, FALSE);
1836 GList *params = b_vformat_attribute_get_params(attr);
1837 GList *p;
1838 for (p = params; p; p = p->next) {
1839 b_VFormatParam *param = p->data;
1840 if (!strcasecmp(name, b_vformat_attribute_param_get_name(param)))
1841 return TRUE;
1843 return FALSE;
1846 GList*
1847 b_vformat_attribute_get_params (b_VFormatAttribute *attr)
1849 g_return_val_if_fail (attr != NULL, NULL);
1851 return attr->params;
1854 const char*
1855 b_vformat_attribute_param_get_name (b_VFormatParam *param)
1857 g_return_val_if_fail (param != NULL, NULL);
1859 return param->name;
1862 GList*
1863 b_vformat_attribute_param_get_values (b_VFormatParam *param)
1865 g_return_val_if_fail (param != NULL, NULL);
1867 return param->values;
1870 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth)
1872 const char *ret = NULL;
1873 GList *values = b_vformat_attribute_param_get_values(param);
1874 if (!values)
1875 return NULL;
1876 ret = g_list_nth_data(values, nth);
1877 return ret;
1880 static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1882 //static unsigned char _evc_base64_rank[256];
1884 static void base64_init(char *rank)
1886 int i;
1888 memset(rank, 0xff, sizeof(rank));
1889 for (i=0;i<64;i++) {
1890 rank[(unsigned int)base64_alphabet[i]] = i;
1892 rank['='] = 0;
1895 /* call this when finished encoding everything, to
1896 flush off the last little bit */
1897 static size_t base64_encode_close(const unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
1899 int c1, c2;
1900 unsigned char *outptr = out;
1902 if (inlen>0)
1903 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
1905 c1 = ((unsigned char *)save)[1];
1906 c2 = ((unsigned char *)save)[2];
1908 switch (((char *)save)[0]) {
1909 case 2:
1910 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
1911 g_assert(outptr[2] != 0);
1912 goto skip;
1913 case 1:
1914 outptr[2] = '=';
1915 skip:
1916 outptr[0] = base64_alphabet[ c1 >> 2 ];
1917 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
1918 outptr[3] = '=';
1919 outptr += 4;
1920 break;
1922 if (break_lines)
1923 *outptr++ = '\n';
1925 *save = 0;
1926 *state = 0;
1928 return outptr-out;
1932 performs an 'encode step', only encodes blocks of 3 characters to the
1933 output at a time, saves left-over state in state and save (initialise to
1934 0 on first invocation).
1936 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
1938 register const unsigned char *inptr;
1939 register unsigned char *outptr;
1941 if (len<=0)
1942 return 0;
1944 inptr = in;
1945 outptr = out;
1947 if (len + ((char *)save)[0] > 2) {
1948 const unsigned char *inend = in+len-2;
1949 register int c1, c2, c3;
1950 register int already;
1952 already = *state;
1954 switch (((char *)save)[0]) {
1955 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1956 case 2: c1 = ((unsigned char *)save)[1];
1957 c2 = ((unsigned char *)save)[2]; goto skip2;
1960 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1961 while (inptr < inend) {
1962 c1 = *inptr++;
1963 skip1:
1964 c2 = *inptr++;
1965 skip2:
1966 c3 = *inptr++;
1967 *outptr++ = base64_alphabet[ c1 >> 2 ];
1968 *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
1969 *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
1970 *outptr++ = base64_alphabet[ c3 & 0x3f ];
1971 /* this is a bit ugly ... */
1972 if (break_lines && (++already)>=19) {
1973 *outptr++='\n';
1974 already = 0;
1978 ((char *)save)[0] = 0;
1979 len = 2-(inptr-inend);
1980 *state = already;
1983 if (len>0) {
1984 register char *saveout;
1986 /* points to the slot for the next char to save */
1987 saveout = & (((char *)save)[1]) + ((char *)save)[0];
1989 /* len can only be 0 1 or 2 */
1990 switch(len) {
1991 case 2: *saveout++ = *inptr++;
1992 case 1: *saveout++ = *inptr++;
1994 ((char *)save)[0]+=len;
1997 return outptr-out;
2002 * base64_decode_step: decode a chunk of base64 encoded data
2003 * @in: input stream
2004 * @len: max length of data to decode
2005 * @out: output stream
2006 * @state: holds the number of bits that are stored in @save
2007 * @save: leftover bits that have not yet been decoded
2009 * Decodes a chunk of base64 encoded data
2011 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
2013 unsigned char base64_rank[256];
2014 base64_init((char*)base64_rank);
2016 register const unsigned char *inptr;
2017 register unsigned char *outptr;
2018 const unsigned char *inend;
2019 unsigned char c;
2020 register unsigned int v;
2021 int i;
2023 inend = in+len;
2024 outptr = out;
2026 /* convert 4 base64 bytes to 3 normal bytes */
2027 v=*save;
2028 i=*state;
2029 inptr = in;
2030 while (inptr<inend) {
2031 c = base64_rank[*inptr++];
2032 if (c != 0xff) {
2033 v = (v<<6) | c;
2034 i++;
2035 if (i==4) {
2036 *outptr++ = v>>16;
2037 *outptr++ = v>>8;
2038 *outptr++ = v;
2039 i=0;
2044 *save = v;
2045 *state = i;
2047 /* quick scan back for '=' on the end somewhere */
2048 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2049 i=2;
2050 while (inptr>in && i) {
2051 inptr--;
2052 if (base64_rank[*inptr] != 0xff) {
2053 if (*inptr == '=' && outptr>out)
2054 outptr--;
2055 i--;
2059 /* if i!= 0 then there is a truncation error! */
2060 return outptr-out;
2063 static char *base64_encode_simple (const char *data, size_t len)
2065 unsigned char *out;
2066 int state = 0, outlen;
2067 unsigned int save = 0;
2069 g_return_val_if_fail (data != NULL, NULL);
2071 out = g_malloc (len * 4 / 3 + 5);
2072 outlen = base64_encode_close ((unsigned char *)data, len, FALSE,
2073 out, &state, (int*)&save);
2074 out[outlen] = '\0';
2075 return (char *)out;
2078 static size_t base64_decode_simple (char *data, size_t len)
2080 int state = 0;
2081 unsigned int save = 0;
2083 g_return_val_if_fail (data != NULL, 0);
2085 return base64_decode_step ((unsigned char *)data, len,
2086 (unsigned char *)data, &state, &save);
2089 static char *quoted_encode_simple(const unsigned char *string, int len)
2091 GString *tmp = g_string_new("");
2093 int i = 0;
2094 while(string[i] != 0) {
2095 if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') {
2096 g_string_append_printf(tmp, "=%02X", string[i]);
2097 } else {
2098 g_string_append_c(tmp, string[i]);
2100 i++;
2103 char *ret = tmp->str;
2104 g_string_free(tmp, FALSE);
2105 return ret;
2109 static size_t quoted_decode_simple (char *data, size_t len)
2111 g_return_val_if_fail (data != NULL, 0);
2113 GString *string = g_string_new(data);
2114 if (!string)
2115 return 0;
2117 char hex[5];
2118 hex[4] = 0;
2120 while (1) {
2121 //Get the index of the next encoded char
2122 int i = strcspn(string->str, "=");
2123 if (i >= strlen(string->str))
2124 break;
2126 strcpy(hex, "0x");
2127 strncat(hex, &string->str[i + 1], 2);
2128 char rep = ((int)(strtod(hex, NULL)));
2129 g_string_erase(string, i, 2);
2130 g_string_insert_c(string, i, rep);
2133 memset(data, 0, strlen(data));
2134 strcpy(data, string->str);
2135 g_string_free(string, 1);
2137 return strlen(data);