desktop: CalEditDlg: fixed dialog title bar
[barry.git] / src / vformat.c
blob4ce0383868b4e88e71218d74fccc3629d3b326bd
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 "vformat.h"
24 #include "clog.h"
26 //#ifdef HAVE_CONFIG_H
27 //#include "config.h"
28 //#endif
30 #include <string.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <iconv.h>
35 //#include <opensync/opensync.h>
37 #define TRACE_INTERNAL 1
38 #define TRACE_ENTRY 1
39 #define TRACE_EXIT 1
40 #define TRACE_ERROR 0
42 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save);
43 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save);
44 static size_t base64_decode_simple (char *data, size_t len);
45 static char *base64_encode_simple (const char *data, size_t len);
47 static size_t quoted_decode_simple (char *data, size_t len);
48 static char *quoted_encode_simple (const unsigned char *string, int len);
51 /**
52 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
53 * @param check_string string that should be compared with "b" or "base64"
54 * @return 0 if check_string is not base64 and 1 if it is
56 static int _helper_is_base64(const char *check_string)
58 if(!g_ascii_strcasecmp ((char *) check_string, "BASE64") ||
59 !g_ascii_strcasecmp ((char *) check_string, "b") )
60 return (1);
61 return (0);
64 time_t b_vformat_time_to_unix(const char *inptime)
66 char *date = NULL;
67 char *time = NULL;
68 char *ftime = NULL;
69 if ((ftime = g_strrstr(inptime, "T"))) {
71 date = g_strndup(inptime, ftime - inptime);
72 if (ftime[3] == ':')
73 time = g_strndup(ftime + 1, 8);
74 else
75 time = g_strndup(ftime + 1, 6);
76 } else {
77 date = g_strdup(inptime);
80 struct tm btime;
81 memset(&btime, 0, sizeof(struct tm));
82 btime.tm_isdst = -1;
84 if (strlen(date) == 10) {
85 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111 - 1900;
86 btime.tm_mon = date[5] * 10 + date[6] - '0' * 11 - 1;
87 btime.tm_mday = date[8] * 10 + date[9] - '0' * 11;
88 } else {
89 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111- 1900;
90 btime.tm_mon = date[4] * 10 + date[5] - '0' * 11 - 1;
91 btime.tm_mday = date[6] * 10 + date[7] - '0' * 11;
94 if (time && strlen(time) == 8) {
95 //Time
96 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
97 btime.tm_min = time[3] * 10 + time[4] - '0' * 11;
98 btime.tm_sec = time[6] * 10 + time[7] - '0' * 11;
99 } else if (time && strlen(time) == 6) {
100 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
101 btime.tm_min = time[2] * 10 + time[3] - '0' * 11;
102 btime.tm_sec = time[4] * 10 + time[5] - '0' * 11;
105 time_t utime = mktime(&btime);
106 return utime;
109 static char *_fold_lines (char *buf)
111 GString *str = g_string_new ("");
112 GString *line = g_string_new ("");
113 char *p = buf;
114 char *next, *next2, *q;
115 gboolean newline = TRUE;
116 gboolean quotedprintable = FALSE;
119 * We're pretty liberal with line folding here. We handle
120 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
121 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
124 while (*p) {
126 /* search new lines for quoted printable encoding */
127 if (newline) {
128 for (q=p; *q != '\n' && *q != '\0'; q++)
129 line = g_string_append_unichar (line, g_utf8_get_char (q));
131 if (strstr(line->str, "ENCODING=QUOTED-PRINTABLE"))
132 quotedprintable = TRUE;
134 g_string_free(line, TRUE);
135 line = g_string_new ("");
137 newline = FALSE;
141 if ((quotedprintable && *p == '=') || *p == '\r' || *p == '\n') {
142 next = g_utf8_next_char (p);
143 if (*next == '\n' || *next == '\r') {
144 next2 = g_utf8_next_char (next);
145 if (*next2 == '\n' || *next2 == '\r' || *next2 == ' ' || *next2 == '\t') {
146 p = g_utf8_next_char (next2);
148 else if(quotedprintable) {
149 p = g_utf8_next_char (next);
151 else {
152 str = g_string_append (str, CRLF);
153 p = g_utf8_next_char (next);
154 newline = TRUE;
155 quotedprintable = FALSE;
158 else if (*p == '=') {
159 str = g_string_append_unichar (str, g_utf8_get_char (p));
160 p = g_utf8_next_char (p);
162 else if (*next == ' ' || *next == '\t') {
163 p = g_utf8_next_char (next);
165 else {
166 str = g_string_append (str, CRLF);
167 p = g_utf8_next_char (p);
168 newline = TRUE;
169 quotedprintable = FALSE;
172 else {
173 str = g_string_append_unichar (str, g_utf8_get_char (p));
174 p = g_utf8_next_char (p);
178 g_free (buf);
179 g_string_free(line, TRUE);
181 return g_string_free (str, FALSE);
184 /* skip forward until we hit the CRLF, or \0 */
185 static void _skip_to_next_line (char **p)
187 char *lp;
188 lp = *p;
190 while (*lp != '\r' && *lp != '\0')
191 lp = g_utf8_next_char (lp);
193 if (*lp == '\r') {
194 lp = g_utf8_next_char (lp); /* \n */
195 lp = g_utf8_next_char (lp); /* start of the next line */
198 *p = lp;
201 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
202 pointing at the character that causes us to stop */
203 static void _skip_until (char **p, char *s)
205 char *lp;
207 lp = *p;
209 while (*lp != '\r' && *lp != '\0') {
210 gboolean s_matches = FALSE;
211 char *ls;
212 for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
213 if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
214 s_matches = TRUE;
215 break;
219 if (s_matches)
220 break;
221 lp++;
224 *p = lp;
227 static void _read_attribute_value_add (b_VFormatAttribute *attr, GString *str, GString *charset)
229 /* don't convert empty strings */
230 if (str->len == 0) {
231 b_vformat_attribute_add_value(attr, str->str);
232 return;
235 char *inbuf, *outbuf, *p;
236 size_t inbytesleft, outbytesleft;
238 inbuf = str->str;
239 p = outbuf = malloc(str->len*2);
240 inbytesleft = str->len;
241 outbytesleft = str->len*2;
243 iconv_t cd;
245 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
246 if (charset) {
248 cd = iconv_open("UTF-8", charset->str);
249 #ifdef SOLARIS
250 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
251 #else
252 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
253 #endif
254 *p = 0;
255 b_vformat_attribute_add_value(attr, outbuf);
257 } else {
259 /* hmm, should not happen */
260 b_vformat_attribute_add_value(attr, str->str);
264 iconv_close(cd);
266 } else {
268 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
269 if (g_utf8_validate (inbuf, -1, NULL)) {
271 b_vformat_attribute_add_value (attr, str->str);
273 } else {
275 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
276 cd = iconv_open("UTF-8", "ISO-8859-1");
277 #ifdef SOLARIS
278 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
279 #else
280 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
281 #endif
282 *p = 0;
283 b_vformat_attribute_add_value (attr, outbuf);
285 } else {
287 b_vformat_attribute_add_value (attr, str->str);
291 iconv_close(cd);
297 free(outbuf);
301 static void _read_attribute_value (b_VFormatAttribute *attr, char **p, int format_encoding, GString *charset)
303 char *lp = *p;
304 GString *str;
306 /* read in the value */
307 str = g_string_new ("");
308 while (*lp != '\r' && *lp != '\0') {
309 if (*lp == '=' && format_encoding == VF_ENCODING_QP) {
310 char a, b, x1=0, x2=0;
312 if ((a = *(++lp)) == '\0') break;
313 if ((b = *(++lp)) == '\0') break;
315 if (isalnum(a)) {
316 if (isalnum(b)) {
317 /* e.g. ...N=C3=BCrnberg\r\n
318 * ^^^
320 x1=a;
321 x2=b;
323 else if (b == '=') {
324 /* e.g. ...N=C=\r\n
325 * ^^^
326 * 3=BCrnberg...
329 char *tmplp = lp;
330 if (*(++tmplp) == '\r' && *(++tmplp) == '\n' && isalnum(*(++tmplp))) {
331 x1 = a;
332 x2 = *tmplp;
333 lp = tmplp;
336 else {
337 /* append malformed input, and
338 continue parsing */
339 str = g_string_append_c(str, a);
340 str = g_string_append_c(str, b);
343 else if (a == '=') {
344 char *tmplp = lp;
345 char c, d, e;
346 c = *(++tmplp);
347 d = *(++tmplp);
348 e = *(++tmplp);
349 if (b == '\r' && c == '\n' && isalnum(d) && isalnum(e)) {
350 x1 = d;
351 x2 = e;
352 lp = tmplp;
354 else {
355 /* append malformed input, and
356 continue parsing */
357 str = g_string_append_c(str, a);
358 str = g_string_append_c(str, b);
361 else {
362 /* append malformed input, and
363 continue parsing */
364 str = g_string_append_c(str, a);
365 str = g_string_append_c(str, b);
367 if (x1 && x2) {
368 char c;
370 a = tolower (x1);
371 b = tolower (x2);
373 c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
374 | ((b>='a'?b-'a'+10:b-'0')&0x0f);
376 str = g_string_append_c (str, c);
378 lp++;
379 x1 = x2 = 0;
381 else if (format_encoding == VF_ENCODING_BASE64) {
382 if((*lp != ' ') && (*lp != '\t') )
383 str = g_string_append_unichar (str, g_utf8_get_char (lp));
384 lp = g_utf8_next_char(lp);
386 else if (*lp == '\\') {
387 /* convert back to the non-escaped version of
388 the characters */
389 lp = g_utf8_next_char(lp);
390 if (*lp == '\0') {
391 str = g_string_append_c (str, '\\');
392 break;
394 switch (*lp) {
395 case 'n': str = g_string_append_c (str, '\n'); break;
396 case 'r': str = g_string_append_c (str, '\r'); break;
397 case ';': str = g_string_append_c (str, ';'); break;
398 case ',':
399 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) {
400 //We need to handle categories here to work
401 //aroung a bug in evo2
402 _read_attribute_value_add (attr, str, charset);
403 g_string_assign (str, "");
404 } else
405 str = g_string_append_c (str, ',');
406 break;
407 case '\\': str = g_string_append_c (str, '\\'); break;
408 case '"': str = g_string_append_c (str, '"'); break;
409 /* \t is (incorrectly) used by kOrganizer, so handle it here */
410 case 't': str = g_string_append_c (str, '\t'); break;
411 default:
412 BarryLogf(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp);
413 str = g_string_append_c (str, '\\');
414 str = g_string_append_unichar (str, g_utf8_get_char(lp));
415 break;
417 lp = g_utf8_next_char(lp);
419 else if ((*lp == ';') ||
420 (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) {
421 _read_attribute_value_add (attr, str, charset);
422 g_string_assign (str, "");
423 lp = g_utf8_next_char(lp);
425 else {
426 str = g_string_append_unichar (str, g_utf8_get_char (lp));
427 lp = g_utf8_next_char(lp);
430 if (str) {
431 _read_attribute_value_add (attr, str, charset);
432 g_string_free (str, TRUE);
435 if (*lp == '\r') {
436 lp = g_utf8_next_char (lp); /* \n */
437 lp = g_utf8_next_char (lp); /* start of the next line */
440 *p = lp;
443 static void _read_attribute_params(b_VFormatAttribute *attr, char **p, int *format_encoding, GString **charset)
445 char *lp = *p;
446 GString *str;
447 b_VFormatParam *param = NULL;
448 gboolean in_quote = FALSE;
449 str = g_string_new ("");
451 while (*lp != '\0') {
452 if (*lp == '"') {
453 in_quote = !in_quote;
454 lp = g_utf8_next_char (lp);
456 else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/' || *lp == '.' || *lp == ' ') {
457 str = g_string_append_unichar (str, g_utf8_get_char (lp));
458 lp = g_utf8_next_char (lp);
460 /* accumulate until we hit the '=' or ';'. If we hit
461 * a '=' the string contains the parameter name. if
462 * we hit a ';' the string contains the parameter
463 * value and the name is either ENCODING (if value ==
464 * QUOTED-PRINTABLE) or TYPE (in any other case.)
466 else if (*lp == '=') {
467 if (str->len > 0) {
468 param = b_vformat_attribute_param_new (str->str);
469 g_string_assign (str, "");
470 lp = g_utf8_next_char (lp);
472 else {
473 _skip_until (&lp, ":;");
474 if (*lp == '\r') {
475 lp = g_utf8_next_char (lp); /* \n */
476 lp = g_utf8_next_char (lp); /* start of the next line */
477 break;
479 else if (*lp == ';')
480 lp = g_utf8_next_char (lp);
483 else if (*lp == ';' || *lp == ':' || *lp == ',') {
484 gboolean colon = (*lp == ':');
485 gboolean comma = (*lp == ',');
487 if (param) {
488 if (str->len > 0) {
489 b_vformat_attribute_param_add_value (param, str->str);
490 g_string_assign (str, "");
491 if (!colon)
492 lp = g_utf8_next_char (lp);
494 else {
495 /* we've got a parameter of the form:
496 * PARAM=(.*,)?[:;]
497 * so what we do depends on if there are already values
498 * for the parameter. If there are, we just finish
499 * this parameter and skip past the offending character
500 * (unless it's the ':'). If there aren't values, we free
501 * the parameter then skip past the character.
503 if (!param->values) {
504 b_vformat_attribute_param_free (param);
505 param = NULL;
506 if (!colon)
507 lp = g_utf8_next_char (lp);
511 if (param
512 && !g_ascii_strcasecmp (param->name, "encoding")) {
513 if (!g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
514 *format_encoding = VF_ENCODING_QP;
515 b_vformat_attribute_param_free (param);
516 param = NULL;
517 } else if ( _helper_is_base64(param->values->data)) {
518 *format_encoding = VF_ENCODING_BASE64;
519 // b_vformat_attribute_param_free (param);
520 // param = NULL;
522 } else if (param && !g_ascii_strcasecmp(param->name, "charset")) {
523 *charset = g_string_new(param->values->data);
524 b_vformat_attribute_param_free (param);
525 param = NULL;
528 else {
529 if (str->len > 0) {
530 char *param_name;
531 if (!g_ascii_strcasecmp (str->str,
532 "quoted-printable")) {
533 param_name = "ENCODING";
534 *format_encoding = VF_ENCODING_QP;
536 /* apple's broken addressbook app outputs naked BASE64
537 parameters, which aren't even vcard 3.0 compliant. */
538 else if (!g_ascii_strcasecmp (str->str,
539 "base64")) {
540 param_name = "ENCODING";
541 g_string_assign (str, "b");
542 *format_encoding = VF_ENCODING_BASE64;
544 else {
545 param_name = "TYPE";
548 if (param_name) {
549 param = b_vformat_attribute_param_new (param_name);
550 b_vformat_attribute_param_add_value (param, str->str);
552 g_string_assign (str, "");
553 if (!colon)
554 lp = g_utf8_next_char (lp);
556 else {
557 /* we've got an attribute with a truly empty
558 attribute parameter. So it's of the form:
560 ATTR;[PARAM=value;]*;[PARAM=value;]*:
562 (note the extra ';')
564 the only thing to do here is, well.. nothing.
565 we skip over the character if it's not a colon,
566 and the rest is handled for us: We'll either
567 continue through the loop again if we hit a ';',
568 or we'll break out correct below if it was a ':' */
569 if (!colon)
570 lp = g_utf8_next_char (lp);
573 if (param && !comma) {
574 b_vformat_attribute_add_param (attr, param);
575 param = NULL;
577 if (colon)
578 break;
580 else {
581 BarryLogf(TRACE_INTERNAL, "invalid character found in parameter spec: \"%i\" String so far: %s", lp[0], str->str);
582 g_string_assign (str, "");
583 _skip_until (&lp, ":;");
587 if (str)
588 g_string_free (str, TRUE);
590 *p = lp;
593 /* reads an entire attribute from the input buffer, leaving p pointing
594 at the start of the next line (past the \r\n) */
595 static b_VFormatAttribute *_read_attribute (char **p)
597 char *attr_group = NULL;
598 char *attr_name = NULL;
599 b_VFormatAttribute *attr = NULL;
600 GString *str, *charset = NULL;
601 char *lp = *p;
603 gboolean is_qp = FALSE;
605 /* first read in the group/name */
606 str = g_string_new ("");
607 while (*lp != '\r' && *lp != '\0') {
608 if (*lp == ':' || *lp == ';') {
609 if (str->len != 0) {
610 /* we've got a name, break out to the value/attribute parsing */
611 attr_name = g_string_free (str, FALSE);
612 break;
614 else {
615 /* a line of the form:
616 * (group.)?[:;]
618 * since we don't have an attribute
619 * name, skip to the end of the line
620 * and try again.
622 g_string_free (str, TRUE);
623 *p = lp;
624 _skip_to_next_line(p);
625 goto lose;
628 else if (*lp == '.') {
629 if (attr_group) {
630 BarryLogf(TRACE_INTERNAL, "extra `.' in attribute specification. ignoring extra group `%s'", str->str);
631 g_string_free (str, TRUE);
632 str = g_string_new ("");
634 if (str->len != 0) {
635 attr_group = g_string_free (str, FALSE);
636 str = g_string_new ("");
639 else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/') {
640 str = g_string_append_unichar (str, g_utf8_get_char (lp));
642 else {
643 BarryLogf(TRACE_INTERNAL, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp[0], str->str);
644 g_string_free (str, TRUE);
645 *p = lp;
646 _skip_to_next_line(p);
647 goto lose;
650 lp = g_utf8_next_char(lp);
653 if (!attr_name) {
654 _skip_to_next_line (p);
655 goto lose;
658 attr = b_vformat_attribute_new (attr_group, attr_name);
659 g_free (attr_group);
660 g_free (attr_name);
662 if (*lp == ';') {
663 /* skip past the ';' */
664 lp = g_utf8_next_char(lp);
665 _read_attribute_params (attr, &lp, &is_qp, &charset);
667 if (*lp == ':') {
668 /* skip past the ':' */
669 lp = g_utf8_next_char(lp);
670 _read_attribute_value (attr, &lp, is_qp, charset);
673 if (charset) g_string_free(charset, TRUE);
674 *p = lp;
676 if (!attr->values)
677 goto lose;
679 return attr;
680 lose:
681 if (attr)
682 b_vformat_attribute_free (attr);
683 return NULL;
686 static void open_block(char **block, const char *block_name)
688 char *start = *block ? *block : "";
689 char *result = NULL;
691 result = g_strconcat(start, "/", block_name, NULL);
692 if( *block )
693 g_free(*block);
694 *block = result;
697 static void close_block(char **block, const char *block_name)
699 int name_len = strlen(block_name);
700 int block_len = *block ? strlen(*block) : 0;
701 char *cmp_start = NULL;
703 if( block_len < name_len + 1 )
704 return;
706 cmp_start = *block + (block_len - name_len - 1);
707 if( cmp_start[0] == '/' &&
708 g_ascii_strcasecmp(cmp_start+1, block_name) == 0 )
710 // end of block hierarchy contains block name,
711 // so safe to remove
713 // cut off the end of the string... no need to free/realloc
714 *cmp_start = '\0';
718 /* we try to be as forgiving as we possibly can here - this isn't a
719 * validator. Almost nothing is considered a fatal error. We always
720 * try to return *something*.
722 static void _parse(b_VFormat *evc, const char *str)
724 char *buf = g_strdup (str);
725 char *p, *end;
726 b_VFormatAttribute *attr;
728 /* first validate the string is valid utf8 */
729 if (!g_utf8_validate (buf, -1, (const char **)&end)) {
730 /* if the string isn't valid, we parse as much as we can from it */
731 BarryLogf(TRACE_INTERNAL, "invalid utf8 passed to b_VFormat. Limping along.");
732 *end = '\0';
735 buf = _fold_lines (buf);
737 p = buf;
739 attr = _read_attribute (&p);
740 if (!attr)
741 attr = _read_attribute (&p);
743 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
744 BarryLogf(TRACE_INTERNAL, "vformat began without a BEGIN\n");
746 if (attr && !g_ascii_strcasecmp (attr->name, "begin"))
747 b_vformat_attribute_free (attr);
748 else if (attr)
749 b_vformat_add_attribute (evc, attr);
751 char *block = NULL;
752 while (*p) {
753 b_VFormatAttribute *next_attr = _read_attribute (&p);
755 if (next_attr) {
756 if( g_ascii_strcasecmp(next_attr->name, "begin") == 0 ) {
757 // add to block hierarchy string
758 char *value = b_vformat_attribute_get_value(next_attr);
759 open_block(&block, value);
760 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
761 g_free(value);
763 else if( g_ascii_strcasecmp(next_attr->name, "end") == 0 ) {
764 // close off the block
765 char *value = b_vformat_attribute_get_value(next_attr);
766 close_block(&block, value);
767 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
768 g_free(value);
771 // apply the block to the attr
772 next_attr->block = g_strdup(block);
774 // add!
775 b_vformat_add_attribute (evc, next_attr);
776 attr = next_attr;
780 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
781 BarryLogf(TRACE_INTERNAL, "vformat ended without END");
784 g_free (buf);
785 g_free (block);
788 char *b_vformat_escape_string (const char *s, b_VFormatType type)
790 GString *str;
791 const char *p;
793 str = g_string_new ("");
795 /* Escape a string as described in RFC2426, section 5 */
796 for (p = s; p && *p; p++) {
797 switch (*p) {
798 case '\n':
799 str = g_string_append (str, "\\n");
800 break;
801 case '\r':
802 if (*(p+1) == '\n')
803 p++;
804 str = g_string_append (str, "\\n");
805 break;
806 case ';':
807 str = g_string_append (str, "\\;");
808 break;
809 case ',':
810 if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20)
811 str = g_string_append (str, "\\,");
812 else
813 str = g_string_append_c (str, *p);
814 break;
815 case '\\':
817 * We won't escape backslashes
818 * on vcard 2.1, unless it is in the end of a value.
819 * See comments above for a better explanation
821 if (*p != '\0' && type == VFORMAT_CARD_21) {
822 BarryLogf(TRACE_INTERNAL, "[%s]We won't escape backslashes", __func__);
823 str = g_string_append_c(str, *p);
825 else {
826 BarryLogf(TRACE_INTERNAL, "[%s] escape backslashes!!", __func__);
827 str = g_string_append (str, "\\\\");
829 break;
830 default:
831 str = g_string_append_c (str, *p);
832 break;
836 return g_string_free (str, FALSE);
839 char*
840 b_vformat_unescape_string (const char *s)
842 GString *str;
843 const char *p;
845 g_return_val_if_fail (s != NULL, NULL);
847 str = g_string_new ("");
849 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
850 for (p = s; *p; p++) {
851 if (*p == '\\') {
852 p++;
853 if (*p == '\0') {
854 str = g_string_append_c (str, '\\');
855 break;
857 switch (*p) {
858 case 'n': str = g_string_append_c (str, '\n'); break;
859 case 'r': str = g_string_append_c (str, '\r'); break;
860 case ';': str = g_string_append_c (str, ';'); 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 /* \t is (incorrectly) used by kOrganizer, so handle it here */
865 case 't': str = g_string_append_c (str, '\t'); break;
866 default:
867 BarryLogf(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p);
868 str = g_string_append_c (str, '\\');
869 str = g_string_append_unichar (str, g_utf8_get_char(p));
870 break;
875 return g_string_free (str, FALSE);
878 void
879 b_vformat_construct (b_VFormat *evc, const char *str)
881 g_return_if_fail (str != NULL);
883 if (*str)
884 _parse (evc, str);
887 void b_vformat_free(b_VFormat *format)
889 g_list_foreach (format->attributes, (GFunc)b_vformat_attribute_free, NULL);
890 g_list_free (format->attributes);
891 g_free(format);
894 b_VFormat *b_vformat_new_from_string (const char *str)
896 g_return_val_if_fail (str != NULL, NULL);
897 b_VFormat *evc = g_malloc0(sizeof(b_VFormat));
899 b_vformat_construct (evc, str);
901 return evc;
904 b_VFormat *b_vformat_new(void)
906 return b_vformat_new_from_string ("");
909 static int _block_match(b_VFormatAttribute *attr, const char *block)
911 // a block matches if the end of the attribute's block
912 // string matches a case insensitive compare with block
914 // for example, a calendar may or may not start with a
915 // BEGIN: VCALENDAR, so DTSTART's block string could be
916 // "/vcalendar/vevent" or just "/vevent". By passing
917 // "/vevent" or even "vevent" as the block argument above,
918 // we should get a match for any of the above.
920 int attr_len = attr->block ? strlen(attr->block) : 0;
921 int block_len = block ? strlen(block) : 0;
923 if( block == NULL )
924 return 1; // if block is null, match everything
926 if( attr_len < block_len )
927 return 0; // not enough string to compare
929 if( attr_len == 0 && block_len == 0 )
930 return 1; // empty and null strings match
932 if( attr->block == NULL )
933 return 0; // don't compare if one side is null
935 return g_ascii_strcasecmp(&attr->block[attr_len - block_len], block) == 0;
938 b_VFormatAttribute *b_vformat_find_attribute(b_VFormat *vcard, const char *name, int nth, const char *block)
940 GList *attributes = b_vformat_get_attributes(vcard);
941 GList *a = NULL;
942 int i = 0;
943 for (a = attributes; a; a = a->next) {
944 b_VFormatAttribute *attr = a->data;
945 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
946 if( block == NULL || _block_match(attr, block) ) {
947 if( i == nth )
948 return attr;
949 i++;
953 return NULL;
957 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
958 const char *name,
959 int nth)
961 GList *attributes = last ? last->next : 0;
962 GList *a = NULL;
963 int i = 0;
964 for (a = attributes; a; a = a->next) {
965 b_VFormatAttribute *attr = a->data;
966 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
967 if( i == nth )
968 return attr;
969 i++;
972 return NULL;
976 char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type)
978 BarryLogf(TRACE_ENTRY, "%s(%p, %i)", __func__, evc, type);
979 GList *l;
980 GList *v;
982 GString *str = g_string_new ("");
984 switch (type) {
985 case VFORMAT_CARD_21:
986 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
987 break;
988 case VFORMAT_CARD_30:
989 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
990 break;
991 case VFORMAT_TODO_10:
992 case VFORMAT_EVENT_10:
993 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
994 break;
995 case VFORMAT_TODO_20:
996 case VFORMAT_EVENT_20:
997 case VFORMAT_JOURNAL:
998 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
999 break;
1000 case VFORMAT_NOTE:
1001 str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
1002 break;
1005 for (l = evc->attributes; l; l = l->next) {
1006 GList *p;
1007 b_VFormatAttribute *attr = l->data;
1008 GString *attr_str;
1009 int l;
1010 int format_encoding = VF_ENCODING_RAW;
1012 attr_str = g_string_new ("");
1014 /* From rfc2425, 5.8.2
1016 * contentline = [group "."] name *(";" param) ":" value CRLF
1019 if (attr->group) {
1020 attr_str = g_string_append (attr_str, attr->group);
1021 attr_str = g_string_append_c (attr_str, '.');
1023 attr_str = g_string_append (attr_str, attr->name);
1024 /* handle the parameters */
1025 for (p = attr->params; p; p = p->next) {
1026 b_VFormatParam *param = p->data;
1027 /* 5.8.2:
1028 * param = param-name "=" param-value *("," param-value)
1030 if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20
1031 || type == VFORMAT_EVENT_20 || type == VFORMAT_JOURNAL) {
1034 * Character set can only be specified on the CHARSET
1035 * parameter on the Content-Type MIME header field.
1037 if (!g_ascii_strcasecmp (param->name, "CHARSET"))
1038 continue;
1039 attr_str = g_string_append_c (attr_str, ';');
1040 attr_str = g_string_append (attr_str, param->name);
1041 if (param->values) {
1042 attr_str = g_string_append_c (attr_str, '=');
1044 for (v = param->values; v; v = v->next) {
1045 if (_helper_is_base64((const char *) v->data)) {
1046 format_encoding = VF_ENCODING_BASE64;
1047 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1048 v->data=g_strdup("B");
1051 * QUOTED-PRINTABLE inline encoding has been
1052 * eliminated.
1054 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) {
1055 BarryLogf(TRACE_ERROR, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__);
1056 format_encoding = VF_ENCODING_QP;
1058 attr_str = g_string_append (attr_str, v->data);
1060 if (v->next)
1061 attr_str = g_string_append_c (attr_str, ',');
1064 else {
1065 attr_str = g_string_append_c (attr_str, ';');
1067 * The "TYPE=" is optional skip it.
1068 * LOGO, PHOTO and SOUND multimedia formats MUST
1069 * have a "TYPE=" parameter
1071 gboolean must_have_type = FALSE;
1072 if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") )
1073 must_have_type = TRUE;
1074 if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") )
1075 attr_str = g_string_append (attr_str, param->name);
1076 if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) )
1077 attr_str = g_string_append_c (attr_str, '=');
1078 for (v = param->values; v; v = v->next) {
1079 // check for quoted-printable encoding
1080 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE"))
1081 format_encoding = VF_ENCODING_QP;
1082 // check for base64 encoding
1083 if (_helper_is_base64((const char *) v->data)) {
1084 format_encoding = VF_ENCODING_BASE64;
1085 v->data=g_strdup("BASE64");
1087 attr_str = g_string_append (attr_str, v->data);
1088 if (v->next)
1089 attr_str = g_string_append_c (attr_str, ',');
1094 attr_str = g_string_append_c (attr_str, ':');
1096 for (v = attr->values; v; v = v->next) {
1097 char *value = v->data;
1098 char *escaped_value = NULL;
1100 if (!g_ascii_strcasecmp (attr->name, "RRULE") &&
1101 strstr (value, "BYDAY") == v->data) {
1102 attr_str = g_string_append (attr_str, value);
1103 } else {
1104 escaped_value = b_vformat_escape_string (value, type);
1105 attr_str = g_string_append (attr_str, escaped_value);
1108 if (v->next) {
1110 /* XXX toshok - i hate you, rfc 2426.
1111 why doesn't CATEGORIES use a ; like
1112 a normal list attribute? */
1113 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
1114 attr_str = g_string_append_c (attr_str, ',');
1115 else
1116 attr_str = g_string_append_c (attr_str, ';');
1119 g_free (escaped_value);
1122 /* Folding lines:
1123 * ^^^^^^^^^^^^^^
1125 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1126 * After generating a content line,
1127 * lines longer than 75 characters SHOULD be folded according to the
1128 * folding procedure described in [MIME-DIR].
1130 * rfc 2445 (iCalendar), 4.1 Content Lines:
1131 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1132 * break. Long content lines SHOULD be split into a multiple line
1133 * representations using a line "folding" technique. That is, a long
1134 * line can be split between any two characters by inserting a CRLF
1135 * immediately followed by a single linear white space character (i.e.,
1136 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1137 * of CRLF followed immediately by a single linear white space character
1138 * is ignored (i.e., removed) when processing the content type.
1140 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1141 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1143 * Differences between encodings:
1144 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1146 * rfc 2425 [MIME-DIR], 5.8.1:
1147 * A logical line MAY be continued on the next physical line anywhere
1148 * between two characters by inserting a CRLF immediately followed by a
1149 * single <WS> (white space) character.
1151 * rfc 2045, 6.7, chapter 5:
1152 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1153 * without follwing <WS>
1155 * UTF-8
1156 * ^^^^^
1158 * Note that all the line folding above is described in terms of characters
1159 * not bytes. In particular, it would be an error to put a line break
1160 * within a UTF-8 character.
1163 l = 0;
1164 do {
1165 if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) {
1166 l += 75;
1168 /* If using QP, must be sure that we do not fold within a quote sequence */
1169 if (format_encoding == VF_ENCODING_QP) {
1170 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--;
1171 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2;
1174 char *p = g_utf8_offset_to_pointer(attr_str->str, l);
1176 if (format_encoding == VF_ENCODING_QP)
1177 attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1);
1178 else
1179 attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1);
1181 else
1182 break;
1183 } while (l < g_utf8_strlen(attr_str->str, attr_str->len));
1185 attr_str = g_string_append (attr_str, CRLF);
1187 * base64= <MIME RFC 1521 base64 text>
1188 * the end of the text is marked with two CRLF sequences
1189 * this results in one blank line before the start of the
1190 * next property
1192 if( format_encoding == VF_ENCODING_BASE64
1193 && (type == VFORMAT_CARD_21))
1194 attr_str = g_string_append (attr_str, CRLF);
1196 str = g_string_append (str, attr_str->str);
1197 g_string_free (attr_str, TRUE);
1200 switch (type) {
1201 case VFORMAT_CARD_21:
1202 str = g_string_append (str, "END:VCARD\r\n");
1203 break;
1204 case VFORMAT_CARD_30:
1205 str = g_string_append (str, "END:VCARD\r\n");
1206 break;
1207 case VFORMAT_TODO_10:
1208 case VFORMAT_EVENT_10:
1209 str = g_string_append (str, "END:VCALENDAR\r\n");
1210 break;
1211 case VFORMAT_TODO_20:
1212 case VFORMAT_EVENT_20:
1213 case VFORMAT_JOURNAL:
1214 str = g_string_append (str, "END:VCALENDAR\r\n");
1215 break;
1216 case VFORMAT_NOTE:
1217 str = g_string_append (str, "END:VNOTE\r\n");
1218 break;
1221 BarryLogf(TRACE_EXIT, "%s", __func__);
1222 return g_string_free (str, FALSE);
1225 void b_vformat_dump_structure (b_VFormat *evc)
1227 GList *a;
1228 GList *v;
1229 int i;
1231 printf ("b_VFormat\n");
1232 for (a = evc->attributes; a; a = a->next) {
1233 GList *p;
1234 b_VFormatAttribute *attr = a->data;
1235 printf ("+-- %s\n", attr->name);
1236 if (attr->params) {
1237 printf (" +- params=\n");
1239 for (p = attr->params, i = 0; p; p = p->next, i++) {
1240 b_VFormatParam *param = p->data;
1241 printf (" | [%d] = %s", i,param->name);
1242 printf ("(");
1243 for (v = param->values; v; v = v->next) {
1244 char *value = b_vformat_escape_string ((char*)v->data, VFORMAT_CARD_21);
1245 printf ("%s", value);
1246 if (v->next)
1247 printf (",");
1248 g_free (value);
1251 printf (")\n");
1254 printf (" +- values=\n");
1255 for (v = attr->values, i = 0; v; v = v->next, i++) {
1256 printf (" [%d] = `%s'\n", i, (char*)v->data);
1261 b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name)
1263 b_VFormatAttribute *attr;
1265 attr = g_new0 (b_VFormatAttribute, 1);
1267 attr->group = g_strdup (attr_group);
1268 attr->name = g_strdup (attr_name);
1270 return attr;
1273 void
1274 b_vformat_attribute_free (b_VFormatAttribute *attr)
1276 g_return_if_fail (attr != NULL);
1278 g_free (attr->block);
1279 g_free (attr->group);
1280 g_free (attr->name);
1282 b_vformat_attribute_remove_values (attr);
1284 b_vformat_attribute_remove_params (attr);
1286 g_free (attr);
1289 b_VFormatAttribute*
1290 b_vformat_attribute_copy (b_VFormatAttribute *attr)
1292 b_VFormatAttribute *a;
1293 GList *p;
1295 g_return_val_if_fail (attr != NULL, NULL);
1297 a = b_vformat_attribute_new (b_vformat_attribute_get_group (attr),
1298 b_vformat_attribute_get_name (attr));
1300 for (p = attr->values; p; p = p->next)
1301 b_vformat_attribute_add_value (a, p->data);
1303 for (p = attr->params; p; p = p->next)
1304 b_vformat_attribute_add_param (a, b_vformat_attribute_param_copy (p->data));
1306 return a;
1309 void
1310 b_vformat_remove_attributes (b_VFormat *evc, const char *attr_group, const char *attr_name)
1312 GList *attr;
1314 g_return_if_fail (attr_name != NULL);
1316 attr = evc->attributes;
1317 while (attr) {
1318 GList *next_attr;
1319 b_VFormatAttribute *a = attr->data;
1321 next_attr = attr->next;
1323 if (((!attr_group && !a->group) ||
1324 (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
1325 ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
1327 /* matches, remove/delete the attribute */
1328 evc->attributes = g_list_remove_link (evc->attributes, attr);
1330 b_vformat_attribute_free (a);
1333 attr = next_attr;
1337 void
1338 b_vformat_remove_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1340 g_return_if_fail (attr != NULL);
1342 evc->attributes = g_list_remove (evc->attributes, attr);
1343 b_vformat_attribute_free (attr);
1346 void
1347 b_vformat_add_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1349 g_return_if_fail (attr != NULL);
1351 evc->attributes = g_list_append (evc->attributes, attr);
1354 void
1355 b_vformat_add_attribute_with_value (b_VFormat *b_VFormat,
1356 b_VFormatAttribute *attr, const char *value)
1358 g_return_if_fail (attr != NULL);
1360 b_vformat_attribute_add_value (attr, value);
1362 b_vformat_add_attribute (b_VFormat, attr);
1365 void
1366 b_vformat_add_attribute_with_values (b_VFormat *b_VFormat, b_VFormatAttribute *attr, ...)
1368 va_list ap;
1369 char *v;
1371 g_return_if_fail (attr != NULL);
1373 va_start (ap, attr);
1375 while ((v = va_arg (ap, char*))) {
1376 b_vformat_attribute_add_value (attr, v);
1379 va_end (ap);
1381 b_vformat_add_attribute (b_VFormat, attr);
1384 void
1385 b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value)
1387 g_return_if_fail (attr != NULL);
1389 attr->values = g_list_append (attr->values, g_strdup (value));
1392 void
1393 b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len)
1395 g_return_if_fail (attr != NULL);
1397 switch (attr->encoding) {
1398 case VF_ENCODING_RAW:
1399 BarryLogf(TRACE_INTERNAL, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1400 break;
1401 case VF_ENCODING_BASE64: {
1402 char *b64_data = base64_encode_simple (value, len);
1403 GString *decoded = g_string_new_len (value, len);
1405 /* make sure the decoded list is up to date */
1406 b_vformat_attribute_get_values_decoded (attr);
1408 attr->values = g_list_append (attr->values, b64_data);
1409 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1410 break;
1412 case VF_ENCODING_QP: {
1413 char *qp_data = quoted_encode_simple ((unsigned char*)value, len);
1414 GString *decoded = g_string_new (value);
1416 /* make sure the decoded list is up to date */
1417 b_vformat_attribute_get_values_decoded (attr);
1419 attr->values = g_list_append (attr->values, qp_data);
1420 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1421 break;
1423 case VF_ENCODING_8BIT: {
1424 char *data = g_strdup(value);
1425 GString *decoded = g_string_new (value);
1427 /* make sure the decoded list is up to date */
1428 b_vformat_attribute_get_values_decoded (attr);
1430 attr->values = g_list_append (attr->values, data);
1431 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1432 break;
1437 void
1438 b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...)
1440 va_list ap;
1441 char *v;
1443 g_return_if_fail (attr != NULL);
1445 va_start (ap, attr);
1447 while ((v = va_arg (ap, char*))) {
1448 b_vformat_attribute_add_value (attr, v);
1451 va_end (ap);
1454 static void
1455 free_gstring (GString *str)
1457 g_string_free (str, TRUE);
1460 void
1461 b_vformat_attribute_remove_values (b_VFormatAttribute *attr)
1463 g_return_if_fail (attr != NULL);
1465 g_list_foreach (attr->values, (GFunc)g_free, NULL);
1466 g_list_free (attr->values);
1467 attr->values = NULL;
1469 g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
1470 g_list_free (attr->decoded_values);
1471 attr->decoded_values = NULL;
1474 void
1475 b_vformat_attribute_remove_params (b_VFormatAttribute *attr)
1477 g_return_if_fail (attr != NULL);
1479 g_list_foreach (attr->params, (GFunc)b_vformat_attribute_param_free, NULL);
1480 g_list_free (attr->params);
1481 attr->params = NULL;
1483 /* also remove the cached encoding on this attribute */
1484 attr->encoding_set = FALSE;
1485 attr->encoding = VF_ENCODING_RAW;
1488 b_VFormatParam*
1489 b_vformat_attribute_param_new (const char *name)
1491 b_VFormatParam *param = g_new0 (b_VFormatParam, 1);
1492 param->name = g_strdup (name);
1494 return param;
1497 void
1498 b_vformat_attribute_param_free (b_VFormatParam *param)
1500 g_return_if_fail (param != NULL);
1502 g_free (param->name);
1504 b_vformat_attribute_param_remove_values (param);
1506 g_free (param);
1509 b_VFormatParam*
1510 b_vformat_attribute_param_copy (b_VFormatParam *param)
1512 b_VFormatParam *p;
1513 GList *l;
1515 g_return_val_if_fail (param != NULL, NULL);
1517 p = b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param));
1519 for (l = param->values; l; l = l->next) {
1520 b_vformat_attribute_param_add_value (p, l->data);
1523 return p;
1526 void
1527 b_vformat_attribute_add_param (b_VFormatAttribute *attr,
1528 b_VFormatParam *param)
1530 g_return_if_fail (attr != NULL);
1531 g_return_if_fail (param != NULL);
1533 attr->params = g_list_append (attr->params, param);
1535 /* we handle our special encoding stuff here */
1537 if (!g_ascii_strcasecmp (param->name, "ENCODING")) {
1538 if (attr->encoding_set) {
1539 BarryLogf(TRACE_INTERNAL, "ENCODING specified twice");
1540 return;
1543 if (param->values && param->values->data) {
1544 if (_helper_is_base64((const char*)param->values->data))
1545 attr->encoding = VF_ENCODING_BASE64;
1546 else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE"))
1547 attr->encoding = VF_ENCODING_QP;
1548 else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT"))
1549 attr->encoding = VF_ENCODING_8BIT;
1550 else {
1551 BarryLogf(TRACE_INTERNAL, "Unknown value `%s' for ENCODING parameter. values will be treated as raw", (char*)param->values->data);
1554 attr->encoding_set = TRUE;
1556 else {
1557 BarryLogf(TRACE_INTERNAL, "ENCODING parameter added with no value");
1562 b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level)
1564 g_return_val_if_fail (attr != NULL, NULL);
1565 GList *p = NULL;
1566 for (p = attr->params; p; p = p->next) {
1567 b_VFormatParam *param = p->data;
1568 if (!g_ascii_strcasecmp (param->name, name)) {
1569 if( level == 0 )
1570 return param;
1571 else
1572 level--;
1575 return NULL;
1578 void
1579 b_vformat_attribute_set_value (b_VFormatAttribute *attr,
1580 int nth, const char *value)
1582 GList *param = g_list_nth(attr->values, nth);
1583 g_free(param->data);
1584 param->data = g_strdup(value);
1587 void
1588 b_vformat_attribute_param_add_value (b_VFormatParam *param,
1589 const char *value)
1591 g_return_if_fail (param != NULL);
1593 param->values = g_list_append (param->values, g_strdup (value));
1596 void
1597 b_vformat_attribute_param_add_values (b_VFormatParam *param,
1598 ...)
1600 va_list ap;
1601 char *v;
1603 g_return_if_fail (param != NULL);
1605 va_start (ap, param);
1607 while ((v = va_arg (ap, char*))) {
1608 b_vformat_attribute_param_add_value (param, v);
1611 va_end (ap);
1614 void
1615 b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value)
1617 g_return_if_fail (attr != NULL);
1618 g_return_if_fail (name != NULL);
1620 if (!value)
1621 return;
1623 b_VFormatParam *param = b_vformat_attribute_param_new(name);
1625 b_vformat_attribute_param_add_value (param, value);
1627 b_vformat_attribute_add_param (attr, param);
1630 void
1631 b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr,
1632 b_VFormatParam *param, ...)
1634 va_list ap;
1635 char *v;
1637 g_return_if_fail (attr != NULL);
1638 g_return_if_fail (param != NULL);
1640 va_start (ap, param);
1642 while ((v = va_arg (ap, char*))) {
1643 b_vformat_attribute_param_add_value (param, v);
1646 va_end (ap);
1648 b_vformat_attribute_add_param (attr, param);
1651 void
1652 b_vformat_attribute_param_remove_values (b_VFormatParam *param)
1654 g_return_if_fail (param != NULL);
1656 g_list_foreach (param->values, (GFunc)g_free, NULL);
1657 g_list_free (param->values);
1658 param->values = NULL;
1661 GList*
1662 b_vformat_get_attributes (b_VFormat *format)
1664 return format->attributes;
1667 const char*
1668 b_vformat_attribute_get_group (b_VFormatAttribute *attr)
1670 g_return_val_if_fail (attr != NULL, NULL);
1672 return attr->group;
1675 const char*
1676 b_vformat_attribute_get_name (b_VFormatAttribute *attr)
1678 g_return_val_if_fail (attr != NULL, NULL);
1680 return attr->name;
1683 const char*
1684 b_vformat_attribute_get_block (b_VFormatAttribute *attr)
1686 g_return_val_if_fail (attr != NULL, NULL);
1688 return attr->block;
1691 GList*
1692 b_vformat_attribute_get_values (b_VFormatAttribute *attr)
1694 g_return_val_if_fail (attr != NULL, NULL);
1696 return attr->values;
1699 GList*
1700 b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr)
1702 g_return_val_if_fail (attr != NULL, NULL);
1704 if (!attr->decoded_values) {
1705 GList *l;
1706 switch (attr->encoding) {
1707 case VF_ENCODING_RAW:
1708 case VF_ENCODING_8BIT:
1709 for (l = attr->values; l; l = l->next)
1710 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data));
1711 break;
1712 case VF_ENCODING_BASE64:
1713 for (l = attr->values; l; l = l->next) {
1714 char *decoded = g_strdup ((char*)l->data);
1715 int len = base64_decode_simple (decoded, strlen (decoded));
1716 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1717 g_free (decoded);
1719 break;
1720 case VF_ENCODING_QP:
1721 for (l = attr->values; l; l = l->next) {
1722 if (!(l->data))
1723 continue;
1724 char *decoded = g_strdup ((char*)l->data);
1725 int len = quoted_decode_simple (decoded, strlen (decoded));
1726 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1727 g_free (decoded);
1729 break;
1733 return attr->decoded_values;
1736 gboolean
1737 b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr)
1739 g_return_val_if_fail (attr != NULL, FALSE);
1741 if (attr->values == NULL
1742 || attr->values->next != NULL)
1743 return FALSE;
1745 return TRUE;
1748 char*
1749 b_vformat_attribute_get_value (b_VFormatAttribute *attr)
1751 GList *values;
1753 g_return_val_if_fail (attr != NULL, NULL);
1755 values = b_vformat_attribute_get_values (attr);
1757 if (!b_vformat_attribute_is_single_valued (attr))
1758 BarryLogf(TRACE_INTERNAL, "b_vformat_attribute_get_value called on multivalued attribute");
1760 return values ? g_strdup ((char*)values->data) : NULL;
1763 GString*
1764 b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr)
1766 GList *values;
1767 GString *str = NULL;
1769 g_return_val_if_fail (attr != NULL, NULL);
1771 values = b_vformat_attribute_get_values_decoded (attr);
1773 if (!b_vformat_attribute_is_single_valued (attr))
1774 BarryLogf(TRACE_INTERNAL, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1776 if (values)
1777 str = values->data;
1779 return str ? g_string_new_len (str->str, str->len) : NULL;
1782 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth)
1784 GList *values = b_vformat_attribute_get_values_decoded(attr);
1785 if (!values)
1786 return NULL;
1787 GString *retstr = (GString *)g_list_nth_data(values, nth);
1788 if (!retstr)
1789 return NULL;
1791 if (!g_utf8_validate(retstr->str, -1, NULL)) {
1792 values = b_vformat_attribute_get_values(attr);
1793 if (!values)
1794 return NULL;
1795 return g_list_nth_data(values, nth);
1798 return retstr->str;
1801 gboolean
1802 b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr)
1804 GList *params;
1805 GList *p;
1807 g_return_val_if_fail (attr != NULL, FALSE);
1808 g_return_val_if_fail (typestr != NULL, FALSE);
1810 params = b_vformat_attribute_get_params (attr);
1812 for (p = params; p; p = p->next) {
1813 b_VFormatParam *param = p->data;
1815 if (!strcasecmp (b_vformat_attribute_param_get_name (param), "TYPE")) {
1816 GList *values = b_vformat_attribute_param_get_values (param);
1817 GList *v;
1819 for (v = values; v; v = v->next) {
1820 if (!strcasecmp ((char*)v->data, typestr))
1821 return TRUE;
1826 return FALSE;
1830 gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name)
1832 g_return_val_if_fail (attr != NULL, FALSE);
1833 g_return_val_if_fail (name != NULL, FALSE);
1835 GList *params = b_vformat_attribute_get_params(attr);
1836 GList *p;
1837 for (p = params; p; p = p->next) {
1838 b_VFormatParam *param = p->data;
1839 if (!strcasecmp(name, b_vformat_attribute_param_get_name(param)))
1840 return TRUE;
1842 return FALSE;
1845 GList*
1846 b_vformat_attribute_get_params (b_VFormatAttribute *attr)
1848 g_return_val_if_fail (attr != NULL, NULL);
1850 return attr->params;
1853 const char*
1854 b_vformat_attribute_param_get_name (b_VFormatParam *param)
1856 g_return_val_if_fail (param != NULL, NULL);
1858 return param->name;
1861 GList*
1862 b_vformat_attribute_param_get_values (b_VFormatParam *param)
1864 g_return_val_if_fail (param != NULL, NULL);
1866 return param->values;
1869 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth)
1871 const char *ret = NULL;
1872 GList *values = b_vformat_attribute_param_get_values(param);
1873 if (!values)
1874 return NULL;
1875 ret = g_list_nth_data(values, nth);
1876 return ret;
1879 static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1881 //static unsigned char _evc_base64_rank[256];
1883 static void base64_init(char *rank)
1885 int i;
1887 memset(rank, 0xff, sizeof(rank));
1888 for (i=0;i<64;i++) {
1889 rank[(unsigned int)base64_alphabet[i]] = i;
1891 rank['='] = 0;
1894 /* call this when finished encoding everything, to
1895 flush off the last little bit */
1896 static size_t base64_encode_close(const unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
1898 int c1, c2;
1899 unsigned char *outptr = out;
1901 if (inlen>0)
1902 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
1904 c1 = ((unsigned char *)save)[1];
1905 c2 = ((unsigned char *)save)[2];
1907 switch (((char *)save)[0]) {
1908 case 2:
1909 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
1910 g_assert(outptr[2] != 0);
1911 goto skip;
1912 case 1:
1913 outptr[2] = '=';
1914 skip:
1915 outptr[0] = base64_alphabet[ c1 >> 2 ];
1916 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
1917 outptr[3] = '=';
1918 outptr += 4;
1919 break;
1921 if (break_lines)
1922 *outptr++ = '\n';
1924 *save = 0;
1925 *state = 0;
1927 return outptr-out;
1931 performs an 'encode step', only encodes blocks of 3 characters to the
1932 output at a time, saves left-over state in state and save (initialise to
1933 0 on first invocation).
1935 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
1937 register const unsigned char *inptr;
1938 register unsigned char *outptr;
1940 if (len<=0)
1941 return 0;
1943 inptr = in;
1944 outptr = out;
1946 if (len + ((char *)save)[0] > 2) {
1947 const unsigned char *inend = in+len-2;
1948 register int c1, c2, c3;
1949 register int already;
1951 already = *state;
1953 switch (((char *)save)[0]) {
1954 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1955 case 2: c1 = ((unsigned char *)save)[1];
1956 c2 = ((unsigned char *)save)[2]; goto skip2;
1959 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1960 while (inptr < inend) {
1961 c1 = *inptr++;
1962 skip1:
1963 c2 = *inptr++;
1964 skip2:
1965 c3 = *inptr++;
1966 *outptr++ = base64_alphabet[ c1 >> 2 ];
1967 *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
1968 *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
1969 *outptr++ = base64_alphabet[ c3 & 0x3f ];
1970 /* this is a bit ugly ... */
1971 if (break_lines && (++already)>=19) {
1972 *outptr++='\n';
1973 already = 0;
1977 ((char *)save)[0] = 0;
1978 len = 2-(inptr-inend);
1979 *state = already;
1982 if (len>0) {
1983 register char *saveout;
1985 /* points to the slot for the next char to save */
1986 saveout = & (((char *)save)[1]) + ((char *)save)[0];
1988 /* len can only be 0 1 or 2 */
1989 switch(len) {
1990 case 2: *saveout++ = *inptr++;
1991 case 1: *saveout++ = *inptr++;
1993 ((char *)save)[0]+=len;
1996 return outptr-out;
2001 * base64_decode_step: decode a chunk of base64 encoded data
2002 * @in: input stream
2003 * @len: max length of data to decode
2004 * @out: output stream
2005 * @state: holds the number of bits that are stored in @save
2006 * @save: leftover bits that have not yet been decoded
2008 * Decodes a chunk of base64 encoded data
2010 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
2012 unsigned char base64_rank[256];
2013 base64_init((char*)base64_rank);
2015 register const unsigned char *inptr;
2016 register unsigned char *outptr;
2017 const unsigned char *inend;
2018 unsigned char c;
2019 register unsigned int v;
2020 int i;
2022 inend = in+len;
2023 outptr = out;
2025 /* convert 4 base64 bytes to 3 normal bytes */
2026 v=*save;
2027 i=*state;
2028 inptr = in;
2029 while (inptr<inend) {
2030 c = base64_rank[*inptr++];
2031 if (c != 0xff) {
2032 v = (v<<6) | c;
2033 i++;
2034 if (i==4) {
2035 *outptr++ = v>>16;
2036 *outptr++ = v>>8;
2037 *outptr++ = v;
2038 i=0;
2043 *save = v;
2044 *state = i;
2046 /* quick scan back for '=' on the end somewhere */
2047 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2048 i=2;
2049 while (inptr>in && i) {
2050 inptr--;
2051 if (base64_rank[*inptr] != 0xff) {
2052 if (*inptr == '=' && outptr>out)
2053 outptr--;
2054 i--;
2058 /* if i!= 0 then there is a truncation error! */
2059 return outptr-out;
2062 static char *base64_encode_simple (const char *data, size_t len)
2064 unsigned char *out;
2065 int state = 0, outlen;
2066 unsigned int save = 0;
2068 g_return_val_if_fail (data != NULL, NULL);
2070 out = g_malloc (len * 4 / 3 + 5);
2071 outlen = base64_encode_close ((unsigned char *)data, len, FALSE,
2072 out, &state, (int*)&save);
2073 out[outlen] = '\0';
2074 return (char *)out;
2077 static size_t base64_decode_simple (char *data, size_t len)
2079 int state = 0;
2080 unsigned int save = 0;
2082 g_return_val_if_fail (data != NULL, 0);
2084 return base64_decode_step ((unsigned char *)data, len,
2085 (unsigned char *)data, &state, &save);
2088 static char *quoted_encode_simple(const unsigned char *string, int len)
2090 GString *tmp = g_string_new("");
2092 int i = 0;
2093 while(string[i] != 0) {
2094 if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') {
2095 g_string_append_printf(tmp, "=%02X", string[i]);
2096 } else {
2097 g_string_append_c(tmp, string[i]);
2099 i++;
2102 char *ret = tmp->str;
2103 g_string_free(tmp, FALSE);
2104 return ret;
2108 static size_t quoted_decode_simple (char *data, size_t len)
2110 g_return_val_if_fail (data != NULL, 0);
2112 GString *string = g_string_new(data);
2113 if (!string)
2114 return 0;
2116 char hex[5];
2117 hex[4] = 0;
2119 while (1) {
2120 //Get the index of the next encoded char
2121 int i = strcspn(string->str, "=");
2122 if (i >= strlen(string->str))
2123 break;
2125 strcpy(hex, "0x");
2126 strncat(hex, &string->str[i + 1], 2);
2127 char rep = ((int)(strtod(hex, NULL)));
2128 g_string_erase(string, i, 2);
2129 g_string_insert_c(string, i, rep);
2132 memset(data, 0, strlen(data));
2133 strcpy(data, string->str);
2134 g_string_free(string, 1);
2136 return strlen(data);