Applied Nicolas Vivien's opensync-plugin-0.4x patch
[barry.git] / opensync-plugin-0.4x / src / vformat.c
blob6bc1b21157c79ea59f43abeabe7848956c040f27
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.
8 *
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"
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <string.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <iconv.h>
34 #include <opensync/opensync.h>
36 static size_t base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save);
37 static size_t base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save);
38 static size_t base64_decode_simple (char *data, size_t len);
39 static char *base64_encode_simple (const char *data, size_t len);
41 static size_t quoted_decode_simple (char *data, size_t len);
42 static char *quoted_encode_simple (const unsigned char *string, int len);
45 /**
46 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
47 * @param check_string string that should be compared with "b" or "base64"
48 * @return 0 if check_string is not base64 and 1 if it is
50 static int _helper_is_base64(const char *check_string)
52 if(!g_ascii_strcasecmp ((char *) check_string, "BASE64") ||
53 !g_ascii_strcasecmp ((char *) check_string, "b") )
54 return (1);
55 return (0);
58 time_t b_vformat_time_to_unix(const char *inptime)
60 char *date = NULL;
61 char *time = NULL;
62 char *ftime = NULL;
63 if ((ftime = g_strrstr(inptime, "T"))) {
65 date = g_strndup(inptime, ftime - inptime);
66 if (ftime[3] == ':')
67 time = g_strndup(ftime + 1, 8);
68 else
69 time = g_strndup(ftime + 1, 6);
70 } else {
71 date = g_strdup(inptime);
74 struct tm btime;
75 memset(&btime, 0, sizeof(struct tm));
76 btime.tm_isdst = -1;
78 if (strlen(date) == 10) {
79 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111 - 1900;
80 btime.tm_mon = date[5] * 10 + date[6] - '0' * 11 - 1;
81 btime.tm_mday = date[8] * 10 + date[9] - '0' * 11;
82 } else {
83 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111- 1900;
84 btime.tm_mon = date[4] * 10 + date[5] - '0' * 11 - 1;
85 btime.tm_mday = date[6] * 10 + date[7] - '0' * 11;
88 if (time && strlen(time) == 8) {
89 //Time
90 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
91 btime.tm_min = time[3] * 10 + time[4] - '0' * 11;
92 btime.tm_sec = time[6] * 10 + time[7] - '0' * 11;
93 } else if (time && strlen(time) == 6) {
94 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
95 btime.tm_min = time[2] * 10 + time[3] - '0' * 11;
96 btime.tm_sec = time[4] * 10 + time[5] - '0' * 11;
99 time_t utime = mktime(&btime);
100 return utime;
103 static char *_fold_lines (char *buf)
105 GString *str = g_string_new ("");
106 GString *line = g_string_new ("");
107 char *p = buf;
108 char *next, *next2, *q;
109 gboolean newline = TRUE;
110 gboolean quotedprintable = FALSE;
113 * We're pretty liberal with line folding here. We handle
114 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
115 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
118 while (*p) {
120 /* search new lines for quoted printable encoding */
121 if (newline) {
122 for (q=p; *q != '\n' && *q != '\0'; q++)
123 line = g_string_append_unichar (line, g_utf8_get_char (q));
125 if (strstr(line->str, "ENCODING=QUOTED-PRINTABLE"))
126 quotedprintable = TRUE;
128 g_string_free(line, TRUE);
129 line = g_string_new ("");
131 newline = FALSE;
135 if ((quotedprintable && *p == '=') || *p == '\r' || *p == '\n') {
136 next = g_utf8_next_char (p);
137 if (*next == '\n' || *next == '\r') {
138 next2 = g_utf8_next_char (next);
139 if (*next2 == '\n' || *next2 == '\r' || *next2 == ' ' || *next2 == '\t') {
140 p = g_utf8_next_char (next2);
142 else {
143 str = g_string_append (str, CRLF);
144 p = g_utf8_next_char (next);
145 newline = TRUE;
146 quotedprintable = FALSE;
149 else if (*p == '=') {
150 str = g_string_append_unichar (str, g_utf8_get_char (p));
151 p = g_utf8_next_char (p);
153 else if (*next == ' ' || *next == '\t') {
154 p = g_utf8_next_char (next);
156 else {
157 str = g_string_append (str, CRLF);
158 p = g_utf8_next_char (p);
159 newline = TRUE;
160 quotedprintable = FALSE;
163 else {
164 str = g_string_append_unichar (str, g_utf8_get_char (p));
165 p = g_utf8_next_char (p);
169 g_free (buf);
170 g_string_free(line, TRUE);
172 return g_string_free (str, FALSE);
175 /* skip forward until we hit the CRLF, or \0 */
176 static void _skip_to_next_line (char **p)
178 char *lp;
179 lp = *p;
181 while (*lp != '\r' && *lp != '\0')
182 lp = g_utf8_next_char (lp);
184 if (*lp == '\r') {
185 lp = g_utf8_next_char (lp); /* \n */
186 lp = g_utf8_next_char (lp); /* start of the next line */
189 *p = lp;
192 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
193 pointing at the character that causes us to stop */
194 static void _skip_until (char **p, char *s)
196 char *lp;
198 lp = *p;
200 while (*lp != '\r' && *lp != '\0') {
201 gboolean s_matches = FALSE;
202 char *ls;
203 for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
204 if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
205 s_matches = TRUE;
206 break;
210 if (s_matches)
211 break;
212 lp++;
215 *p = lp;
218 static void _read_attribute_value_add (b_VFormatAttribute *attr, GString *str, GString *charset)
220 /* don't convert empty strings */
221 if (str->len == 0) {
222 b_vformat_attribute_add_value(attr, str->str);
223 return;
226 char *inbuf, *outbuf, *p;
227 size_t inbytesleft, outbytesleft;
229 inbuf = str->str;
230 p = outbuf = malloc(str->len*2);
231 inbytesleft = str->len;
232 outbytesleft = str->len*2;
234 iconv_t cd;
236 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
237 if (charset) {
239 cd = iconv_open("UTF-8", charset->str);
240 #ifdef SOLARIS
241 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
242 #else
243 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
244 #endif
245 *p = 0;
246 b_vformat_attribute_add_value(attr, outbuf);
248 } else {
250 /* hmm, should not happen */
251 b_vformat_attribute_add_value(attr, str->str);
255 iconv_close(cd);
257 } else {
259 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
260 if (g_utf8_validate (inbuf, -1, NULL)) {
262 b_vformat_attribute_add_value (attr, str->str);
264 } else {
266 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
267 cd = iconv_open("UTF-8", "ISO-8859-1");
268 #ifdef SOLARIS
269 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
270 #else
271 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
272 #endif
273 *p = 0;
274 b_vformat_attribute_add_value (attr, outbuf);
276 } else {
278 b_vformat_attribute_add_value (attr, str->str);
282 iconv_close(cd);
288 free(outbuf);
292 static void _read_attribute_value (b_VFormatAttribute *attr, char **p, int format_encoding, GString *charset)
294 char *lp = *p;
295 GString *str;
297 /* read in the value */
298 str = g_string_new ("");
299 while (*lp != '\r' && *lp != '\0') {
300 if (*lp == '=' && format_encoding == VF_ENCODING_QP) {
301 char a, b, x1=0, x2=0;
303 if ((a = *(++lp)) == '\0') break;
304 if ((b = *(++lp)) == '\0') break;
306 if (isalnum(a)) {
307 if (isalnum(b)) {
308 /* e.g. ...N=C3=BCrnberg\r\n
309 * ^^^
311 x1=a;
312 x2=b;
314 else if (b == '=') {
315 /* e.g. ...N=C=\r\n
316 * ^^^
317 * 3=BCrnberg...
320 char *tmplp = lp;
321 if (*(++tmplp) == '\r' && *(++tmplp) == '\n' && isalnum(*(++tmplp))) {
322 x1 = a;
323 x2 = *tmplp;
324 lp = tmplp;
327 else {
328 /* append malformed input, and
329 continue parsing */
330 str = g_string_append_c(str, a);
331 str = g_string_append_c(str, b);
334 else if (a == '=') {
335 char *tmplp = lp;
336 char c, d, e;
337 c = *(++tmplp);
338 d = *(++tmplp);
339 e = *(++tmplp);
340 if (b == '\r' && c == '\n' && isalnum(d) && isalnum(e)) {
341 x1 = d;
342 x2 = e;
343 lp = tmplp;
345 else {
346 /* append malformed input, and
347 continue parsing */
348 str = g_string_append_c(str, a);
349 str = g_string_append_c(str, b);
352 else {
353 /* append malformed input, and
354 continue parsing */
355 str = g_string_append_c(str, a);
356 str = g_string_append_c(str, b);
358 if (x1 && x2) {
359 char c;
361 a = tolower (x1);
362 b = tolower (x2);
364 c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
365 | ((b>='a'?b-'a'+10:b-'0')&0x0f);
367 str = g_string_append_c (str, c);
369 lp++;
370 x1 = x2 = 0;
372 else if (format_encoding == VF_ENCODING_BASE64) {
373 if((*lp != ' ') && (*lp != '\t') )
374 str = g_string_append_unichar (str, g_utf8_get_char (lp));
375 lp = g_utf8_next_char(lp);
377 else if (*lp == '\\') {
378 /* convert back to the non-escaped version of
379 the characters */
380 lp = g_utf8_next_char(lp);
381 if (*lp == '\0') {
382 str = g_string_append_c (str, '\\');
383 break;
385 switch (*lp) {
386 case 'n': str = g_string_append_c (str, '\n'); break;
387 case 'r': str = g_string_append_c (str, '\r'); break;
388 case ';': str = g_string_append_c (str, ';'); break;
389 case ',':
390 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) {
391 //We need to handle categories here to work
392 //aroung a bug in evo2
393 _read_attribute_value_add (attr, str, charset);
394 g_string_assign (str, "");
395 } else
396 str = g_string_append_c (str, ',');
397 break;
398 case '\\': str = g_string_append_c (str, '\\'); break;
399 case '"': str = g_string_append_c (str, '"'); break;
400 /* \t is (incorrectly) used by kOrganizer, so handle it here */
401 case 't': str = g_string_append_c (str, '\t'); break;
402 default:
403 osync_trace(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %i", *lp);
404 str = g_string_append_c (str, '\\');
405 str = g_string_append_unichar (str, g_utf8_get_char(lp));
406 break;
408 lp = g_utf8_next_char(lp);
410 else if ((*lp == ';') ||
411 (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) {
412 _read_attribute_value_add (attr, str, charset);
413 g_string_assign (str, "");
414 lp = g_utf8_next_char(lp);
416 else {
417 str = g_string_append_unichar (str, g_utf8_get_char (lp));
418 lp = g_utf8_next_char(lp);
421 if (str) {
422 _read_attribute_value_add (attr, str, charset);
423 g_string_free (str, TRUE);
426 if (*lp == '\r') {
427 lp = g_utf8_next_char (lp); /* \n */
428 lp = g_utf8_next_char (lp); /* start of the next line */
431 *p = lp;
434 static void _read_attribute_params(b_VFormatAttribute *attr, char **p, int *format_encoding, GString **charset)
436 char *lp = *p;
437 GString *str;
438 b_VFormatParam *param = NULL;
439 gboolean in_quote = FALSE;
440 str = g_string_new ("");
442 while (*lp != '\0') {
443 if (*lp == '"') {
444 in_quote = !in_quote;
445 lp = g_utf8_next_char (lp);
447 else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/' || *lp == '.' || *lp == ' ') {
448 str = g_string_append_unichar (str, g_utf8_get_char (lp));
449 lp = g_utf8_next_char (lp);
451 /* accumulate until we hit the '=' or ';'. If we hit
452 * a '=' the string contains the parameter name. if
453 * we hit a ';' the string contains the parameter
454 * value and the name is either ENCODING (if value ==
455 * QUOTED-PRINTABLE) or TYPE (in any other case.)
457 else if (*lp == '=') {
458 if (str->len > 0) {
459 param = b_vformat_attribute_param_new (str->str);
460 g_string_assign (str, "");
461 lp = g_utf8_next_char (lp);
463 else {
464 _skip_until (&lp, ":;");
465 if (*lp == '\r') {
466 lp = g_utf8_next_char (lp); /* \n */
467 lp = g_utf8_next_char (lp); /* start of the next line */
468 break;
470 else if (*lp == ';')
471 lp = g_utf8_next_char (lp);
474 else if (*lp == ';' || *lp == ':' || *lp == ',') {
475 gboolean colon = (*lp == ':');
476 gboolean comma = (*lp == ',');
478 if (param) {
479 if (str->len > 0) {
480 b_vformat_attribute_param_add_value (param, str->str);
481 g_string_assign (str, "");
482 if (!colon)
483 lp = g_utf8_next_char (lp);
485 else {
486 /* we've got a parameter of the form:
487 * PARAM=(.*,)?[:;]
488 * so what we do depends on if there are already values
489 * for the parameter. If there are, we just finish
490 * this parameter and skip past the offending character
491 * (unless it's the ':'). If there aren't values, we free
492 * the parameter then skip past the character.
494 if (!param->values) {
495 b_vformat_attribute_param_free (param);
496 param = NULL;
497 if (!colon)
498 lp = g_utf8_next_char (lp);
502 if (param
503 && !g_ascii_strcasecmp (param->name, "encoding")) {
504 if (!g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
505 *format_encoding = VF_ENCODING_QP;
506 b_vformat_attribute_param_free (param);
507 param = NULL;
508 } else if ( _helper_is_base64(param->values->data)) {
509 *format_encoding = VF_ENCODING_BASE64;
510 // b_vformat_attribute_param_free (param);
511 // param = NULL;
513 } else if (param && !g_ascii_strcasecmp(param->name, "charset")) {
514 *charset = g_string_new(param->values->data);
515 b_vformat_attribute_param_free (param);
516 param = NULL;
519 else {
520 if (str->len > 0) {
521 char *param_name;
522 if (!g_ascii_strcasecmp (str->str,
523 "quoted-printable")) {
524 param_name = "ENCODING";
525 *format_encoding = VF_ENCODING_QP;
527 /* apple's broken addressbook app outputs naked BASE64
528 parameters, which aren't even vcard 3.0 compliant. */
529 else if (!g_ascii_strcasecmp (str->str,
530 "base64")) {
531 param_name = "ENCODING";
532 g_string_assign (str, "b");
533 *format_encoding = VF_ENCODING_BASE64;
535 else {
536 param_name = "TYPE";
539 if (param_name) {
540 param = b_vformat_attribute_param_new (param_name);
541 b_vformat_attribute_param_add_value (param, str->str);
543 g_string_assign (str, "");
544 if (!colon)
545 lp = g_utf8_next_char (lp);
547 else {
548 /* we've got an attribute with a truly empty
549 attribute parameter. So it's of the form:
551 ATTR;[PARAM=value;]*;[PARAM=value;]*:
553 (note the extra ';')
555 the only thing to do here is, well.. nothing.
556 we skip over the character if it's not a colon,
557 and the rest is handled for us: We'll either
558 continue through the loop again if we hit a ';',
559 or we'll break out correct below if it was a ':' */
560 if (!colon)
561 lp = g_utf8_next_char (lp);
564 if (param && !comma) {
565 b_vformat_attribute_add_param (attr, param);
566 param = NULL;
568 if (colon)
569 break;
571 else {
572 osync_trace(TRACE_INTERNAL, "invalid character found in parameter spec: \"%i\" String so far: %s", lp[0], str->str);
573 g_string_assign (str, "");
574 _skip_until (&lp, ":;");
578 if (str)
579 g_string_free (str, TRUE);
581 *p = lp;
584 /* reads an entire attribute from the input buffer, leaving p pointing
585 at the start of the next line (past the \r\n) */
586 static b_VFormatAttribute *_read_attribute (char **p)
588 char *attr_group = NULL;
589 char *attr_name = NULL;
590 b_VFormatAttribute *attr = NULL;
591 GString *str, *charset = NULL;
592 char *lp = *p;
594 gboolean is_qp = FALSE;
596 /* first read in the group/name */
597 str = g_string_new ("");
598 while (*lp != '\r' && *lp != '\0') {
599 if (*lp == ':' || *lp == ';') {
600 if (str->len != 0) {
601 /* we've got a name, break out to the value/attribute parsing */
602 attr_name = g_string_free (str, FALSE);
603 break;
605 else {
606 /* a line of the form:
607 * (group.)?[:;]
609 * since we don't have an attribute
610 * name, skip to the end of the line
611 * and try again.
613 g_string_free (str, TRUE);
614 *p = lp;
615 _skip_to_next_line(p);
616 goto lose;
619 else if (*lp == '.') {
620 if (attr_group) {
621 osync_trace(TRACE_INTERNAL, "extra `.' in attribute specification. ignoring extra group `%s'",
622 str->str);
623 g_string_free (str, TRUE);
624 str = g_string_new ("");
626 if (str->len != 0) {
627 attr_group = g_string_free (str, FALSE);
628 str = g_string_new ("");
631 else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/') {
632 str = g_string_append_unichar (str, g_utf8_get_char (lp));
634 else {
635 osync_trace(TRACE_INTERNAL, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp[0], str->str);
636 g_string_free (str, TRUE);
637 *p = lp;
638 _skip_to_next_line(p);
639 goto lose;
642 lp = g_utf8_next_char(lp);
645 if (!attr_name) {
646 _skip_to_next_line (p);
647 goto lose;
650 attr = b_vformat_attribute_new (attr_group, attr_name);
651 g_free (attr_group);
652 g_free (attr_name);
654 if (*lp == ';') {
655 /* skip past the ';' */
656 lp = g_utf8_next_char(lp);
657 _read_attribute_params (attr, &lp, &is_qp, &charset);
659 if (*lp == ':') {
660 /* skip past the ':' */
661 lp = g_utf8_next_char(lp);
662 _read_attribute_value (attr, &lp, is_qp, charset);
665 if (charset) g_string_free(charset, TRUE);
666 *p = lp;
668 if (!attr->values)
669 goto lose;
671 return attr;
672 lose:
673 if (attr)
674 b_vformat_attribute_free (attr);
675 return NULL;
678 static void open_block(char **block, const char *block_name)
680 char *start = *block ? *block : "";
681 char *result = NULL;
683 result = g_strconcat(start, "/", block_name, NULL);
684 if( *block )
685 g_free(*block);
686 *block = result;
689 static void close_block(char **block, const char *block_name)
691 int name_len = strlen(block_name);
692 int block_len = *block ? strlen(*block) : 0;
693 char *cmp_start = NULL;
695 if( block_len < name_len + 1 )
696 return;
698 cmp_start = *block + (block_len - name_len - 1);
699 if( cmp_start[0] == '/' &&
700 g_ascii_strcasecmp(cmp_start+1, block_name) == 0 )
702 // end of block hierarchy contains block name,
703 // so safe to remove
705 // cut off the end of the string... no need to free/realloc
706 *cmp_start = '\0';
710 /* we try to be as forgiving as we possibly can here - this isn't a
711 * validator. Almost nothing is considered a fatal error. We always
712 * try to return *something*.
714 static void _parse(b_VFormat *evc, const char *str)
716 char *buf = g_strdup (str);
717 char *p, *end;
718 b_VFormatAttribute *attr;
720 /* first validate the string is valid utf8 */
721 if (!g_utf8_validate (buf, -1, (const char **)&end)) {
722 /* if the string isn't valid, we parse as much as we can from it */
723 osync_trace(TRACE_INTERNAL, "invalid utf8 passed to b_VFormat. Limping along.");
724 *end = '\0';
727 buf = _fold_lines (buf);
729 p = buf;
731 attr = _read_attribute (&p);
732 if (!attr)
733 attr = _read_attribute (&p);
735 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
736 osync_trace(TRACE_INTERNAL, "vformat began without a BEGIN\n");
738 if (attr && !g_ascii_strcasecmp (attr->name, "begin"))
739 b_vformat_attribute_free (attr);
740 else if (attr)
741 b_vformat_add_attribute (evc, attr);
743 char *block = NULL;
744 while (*p) {
745 b_VFormatAttribute *next_attr = _read_attribute (&p);
747 if (next_attr) {
748 if( g_ascii_strcasecmp(next_attr->name, "begin") == 0 ) {
749 // add to block hierarchy string
750 char *value = b_vformat_attribute_get_value(next_attr);
751 open_block(&block, value);
752 //osync_trace(TRACE_INTERNAL, "open block: %s", block);
753 g_free(value);
755 else if( g_ascii_strcasecmp(next_attr->name, "end") == 0 ) {
756 // close off the block
757 char *value = b_vformat_attribute_get_value(next_attr);
758 close_block(&block, value);
759 //osync_trace(TRACE_INTERNAL, "close block: %s", block);
760 g_free(value);
763 // apply the block to the attr
764 next_attr->block = g_strdup(block);
766 // add!
767 b_vformat_add_attribute (evc, next_attr);
768 attr = next_attr;
772 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
773 osync_trace(TRACE_INTERNAL, "vformat ended without END");
776 g_free (buf);
777 g_free (block);
780 char *b_vformat_escape_string (const char *s, b_VFormatType type)
782 GString *str;
783 const char *p;
785 str = g_string_new ("");
787 /* Escape a string as described in RFC2426, section 5 */
788 for (p = s; p && *p; p++) {
789 switch (*p) {
790 case '\n':
791 str = g_string_append (str, "\\n");
792 break;
793 case '\r':
794 if (*(p+1) == '\n')
795 p++;
796 str = g_string_append (str, "\\n");
797 break;
798 case ';':
799 str = g_string_append (str, "\\;");
800 break;
801 case ',':
802 if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20)
803 str = g_string_append (str, "\\,");
804 else
805 str = g_string_append_c (str, *p);
806 break;
807 case '\\':
808 /**
809 * We won't escape backslashes
810 * on vcard 2.1, unless it is in the end of a value.
811 * See comments above for a better explanation
813 if (*p != '\0' && type == VFORMAT_CARD_21) {
814 osync_trace(TRACE_INTERNAL, "[%s]We won't escape backslashes", __func__);
815 str = g_string_append_c(str, *p);
817 else {
818 osync_trace(TRACE_INTERNAL, "[%s] escape backslashes!!", __func__);
819 str = g_string_append (str, "\\\\");
821 break;
822 default:
823 str = g_string_append_c (str, *p);
824 break;
828 return g_string_free (str, FALSE);
831 char*
832 b_vformat_unescape_string (const char *s)
834 GString *str;
835 const char *p;
837 g_return_val_if_fail (s != NULL, NULL);
839 str = g_string_new ("");
841 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
842 for (p = s; *p; p++) {
843 if (*p == '\\') {
844 p++;
845 if (*p == '\0') {
846 str = g_string_append_c (str, '\\');
847 break;
849 switch (*p) {
850 case 'n': str = g_string_append_c (str, '\n'); break;
851 case 'r': str = g_string_append_c (str, '\r'); break;
852 case ';': str = g_string_append_c (str, ';'); break;
853 case ',': str = g_string_append_c (str, ','); break;
854 case '\\': str = g_string_append_c (str, '\\'); break;
855 case '"': str = g_string_append_c (str, '"'); break;
856 /* \t is (incorrectly) used by kOrganizer, so handle it here */
857 case 't': str = g_string_append_c (str, '\t'); break;
858 default:
859 osync_trace(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %s", *p);
860 str = g_string_append_c (str, '\\');
861 str = g_string_append_unichar (str, g_utf8_get_char(p));
862 break;
867 return g_string_free (str, FALSE);
870 void
871 b_vformat_construct (b_VFormat *evc, const char *str)
873 g_return_if_fail (str != NULL);
875 if (*str)
876 _parse (evc, str);
879 void b_vformat_free(b_VFormat *format)
881 g_list_foreach (format->attributes, (GFunc)b_vformat_attribute_free, NULL);
882 g_list_free (format->attributes);
883 g_free(format);
886 b_VFormat *b_vformat_new_from_string (const char *str)
888 g_return_val_if_fail (str != NULL, NULL);
889 b_VFormat *evc = g_malloc0(sizeof(b_VFormat));
891 b_vformat_construct (evc, str);
893 return evc;
896 b_VFormat *b_vformat_new(void)
898 return b_vformat_new_from_string ("");
901 static int _block_match(b_VFormatAttribute *attr, const char *block)
903 // a block matches if the end of the attribute's block
904 // string matches a case insensitive compare with block
906 // for example, a calendar may or may not start with a
907 // BEGIN: VCALENDAR, so DTSTART's block string could be
908 // "/vcalendar/vevent" or just "/vevent". By passing
909 // "/vevent" or even "vevent" as the block argument above,
910 // we should get a match for any of the above.
912 int attr_len = attr->block ? strlen(attr->block) : 0;
913 int block_len = block ? strlen(block) : 0;
915 if( block == NULL )
916 return 1; // if block is null, match everything
918 if( attr_len < block_len )
919 return 0; // not enough string to compare
921 if( attr_len == 0 && block_len == 0 )
922 return 1; // empty and null strings match
924 if( attr->block == NULL )
925 return 0; // don't compare if one side is null
927 return g_ascii_strcasecmp(&attr->block[attr_len - block_len], block) == 0;
930 b_VFormatAttribute *b_vformat_find_attribute(b_VFormat *vcard, const char *name, int nth, const char *block)
932 GList *attributes = b_vformat_get_attributes(vcard);
933 GList *a = NULL;
934 int i = 0;
935 for (a = attributes; a; a = a->next) {
936 b_VFormatAttribute *attr = a->data;
937 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
938 if( block == NULL || _block_match(attr, block) ) {
939 if( i == nth )
940 return attr;
941 i++;
945 return NULL;
949 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
950 const char *name,
951 int nth)
953 GList *attributes = last ? last->next : 0;
954 GList *a = NULL;
955 int i = 0;
956 for (a = attributes; a; a = a->next) {
957 b_VFormatAttribute *attr = a->data;
958 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
959 if( i == nth )
960 return attr;
961 i++;
964 return NULL;
968 char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type)
970 osync_trace(TRACE_ENTRY, "%s(%p, %i)", __func__, type);
971 GList *l;
972 GList *v;
974 GString *str = g_string_new ("");
976 switch (type) {
977 case VFORMAT_CARD_21:
978 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
979 break;
980 case VFORMAT_CARD_30:
981 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
982 break;
983 case VFORMAT_TODO_10:
984 case VFORMAT_EVENT_10:
985 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
986 break;
987 case VFORMAT_TODO_20:
988 case VFORMAT_EVENT_20:
989 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
990 break;
991 case VFORMAT_NOTE:
992 str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
993 break;
996 for (l = evc->attributes; l; l = l->next) {
997 GList *p;
998 b_VFormatAttribute *attr = l->data;
999 GString *attr_str;
1000 int l;
1001 int format_encoding = VF_ENCODING_RAW;
1003 attr_str = g_string_new ("");
1005 /* From rfc2425, 5.8.2
1007 * contentline = [group "."] name *(";" param) ":" value CRLF
1010 if (attr->group) {
1011 attr_str = g_string_append (attr_str, attr->group);
1012 attr_str = g_string_append_c (attr_str, '.');
1014 attr_str = g_string_append (attr_str, attr->name);
1015 /* handle the parameters */
1016 for (p = attr->params; p; p = p->next) {
1017 b_VFormatParam *param = p->data;
1018 /* 5.8.2:
1019 * param = param-name "=" param-value *("," param-value)
1021 if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20
1022 || type == VFORMAT_EVENT_20) {
1025 * Character set can only be specified on the CHARSET
1026 * parameter on the Content-Type MIME header field.
1028 if (!g_ascii_strcasecmp (param->name, "CHARSET"))
1029 continue;
1030 attr_str = g_string_append_c (attr_str, ';');
1031 attr_str = g_string_append (attr_str, param->name);
1032 if (param->values) {
1033 attr_str = g_string_append_c (attr_str, '=');
1035 for (v = param->values; v; v = v->next) {
1036 if (_helper_is_base64((const char *) v->data)) {
1037 format_encoding = VF_ENCODING_BASE64;
1038 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1039 v->data="B";
1042 * QUOTED-PRINTABLE inline encoding has been
1043 * eliminated.
1045 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) {
1046 osync_trace(TRACE_ERROR, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__);
1047 format_encoding = VF_ENCODING_QP;
1049 attr_str = g_string_append (attr_str, v->data);
1051 if (v->next)
1052 attr_str = g_string_append_c (attr_str, ',');
1055 else {
1056 attr_str = g_string_append_c (attr_str, ';');
1058 * The "TYPE=" is optional skip it.
1059 * LOGO, PHOTO and SOUND multimedia formats MUST
1060 * have a "TYPE=" parameter
1062 gboolean must_have_type = FALSE;
1063 if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") )
1064 must_have_type = TRUE;
1065 if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") )
1066 attr_str = g_string_append (attr_str, param->name);
1067 if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) )
1068 attr_str = g_string_append_c (attr_str, '=');
1069 for (v = param->values; v; v = v->next) {
1070 // check for quoted-printable encoding
1071 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE"))
1072 format_encoding = VF_ENCODING_QP;
1073 // check for base64 encoding
1074 if (_helper_is_base64((const char *) v->data)) {
1075 format_encoding = VF_ENCODING_BASE64;
1076 v->data="BASE64";
1078 attr_str = g_string_append (attr_str, v->data);
1079 if (v->next)
1080 attr_str = g_string_append_c (attr_str, ',');
1085 attr_str = g_string_append_c (attr_str, ':');
1087 for (v = attr->values; v; v = v->next) {
1088 char *value = v->data;
1089 char *escaped_value = NULL;
1091 if (!g_ascii_strcasecmp (attr->name, "RRULE") &&
1092 strstr (value, "BYDAY") == v->data) {
1093 attr_str = g_string_append (attr_str, value);
1094 } else {
1095 escaped_value = b_vformat_escape_string (value, type);
1096 attr_str = g_string_append (attr_str, escaped_value);
1099 if (v->next) {
1101 /* XXX toshok - i hate you, rfc 2426.
1102 why doesn't CATEGORIES use a ; like
1103 a normal list attribute? */
1104 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
1105 attr_str = g_string_append_c (attr_str, ',');
1106 else
1107 attr_str = g_string_append_c (attr_str, ';');
1110 g_free (escaped_value);
1113 /* Folding lines:
1114 * ^^^^^^^^^^^^^^
1116 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1117 * After generating a content line,
1118 * lines longer than 75 characters SHOULD be folded according to the
1119 * folding procedure described in [MIME-DIR].
1121 * rfc 2445 (iCalendar), 4.1 Content Lines:
1122 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1123 * break. Long content lines SHOULD be split into a multiple line
1124 * representations using a line "folding" technique. That is, a long
1125 * line can be split between any two characters by inserting a CRLF
1126 * immediately followed by a single linear white space character (i.e.,
1127 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1128 * of CRLF followed immediately by a single linear white space character
1129 * is ignored (i.e., removed) when processing the content type.
1131 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1132 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1134 * Differences between encodings:
1135 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1137 * rfc 2425 [MIME-DIR], 5.8.1:
1138 * A logical line MAY be continued on the next physical line anywhere
1139 * between two characters by inserting a CRLF immediately followed by a
1140 * single <WS> (white space) character.
1142 * rfc 2045, 6.7, chapter 5:
1143 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1144 * without follwing <WS>
1146 * UTF-8
1147 * ^^^^^
1149 * Note that all the line folding above is described in terms of characters
1150 * not bytes. In particular, it would be an error to put a line break
1151 * within a UTF-8 character.
1154 l = 0;
1155 do {
1156 if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) {
1157 l += 75;
1159 /* If using QP, must be sure that we do not fold within a quote sequence */
1160 if (format_encoding == VF_ENCODING_QP) {
1161 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--;
1162 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2;
1165 char *p = g_utf8_offset_to_pointer(attr_str->str, l);
1167 if (format_encoding == VF_ENCODING_QP)
1168 attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1);
1169 else
1170 attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1);
1172 else
1173 break;
1174 } while (l < g_utf8_strlen(attr_str->str, attr_str->len));
1176 attr_str = g_string_append (attr_str, CRLF);
1178 * base64= <MIME RFC 1521 base64 text>
1179 * the end of the text is marked with two CRLF sequences
1180 * this results in one blank line before the start of the
1181 * next property
1183 if( format_encoding == VF_ENCODING_BASE64
1184 && (type == VFORMAT_CARD_21))
1185 attr_str = g_string_append (attr_str, CRLF);
1187 str = g_string_append (str, attr_str->str);
1188 g_string_free (attr_str, TRUE);
1191 switch (type) {
1192 case VFORMAT_CARD_21:
1193 str = g_string_append (str, "END:VCARD\r\n");
1194 break;
1195 case VFORMAT_CARD_30:
1196 str = g_string_append (str, "END:VCARD\r\n");
1197 break;
1198 case VFORMAT_TODO_10:
1199 case VFORMAT_EVENT_10:
1200 str = g_string_append (str, "END:VCALENDAR\r\n");
1201 break;
1202 case VFORMAT_TODO_20:
1203 case VFORMAT_EVENT_20:
1204 str = g_string_append (str, "END:VCALENDAR\r\n");
1205 break;
1206 case VFORMAT_NOTE:
1207 str = g_string_append (str, "END:VNOTE\r\n");
1208 break;
1211 osync_trace(TRACE_EXIT, "%s(%p, %i)", __func__, type);
1212 return g_string_free (str, FALSE);
1215 void b_vformat_dump_structure (b_VFormat *evc)
1217 GList *a;
1218 GList *v;
1219 int i;
1221 printf ("b_VFormat\n");
1222 for (a = evc->attributes; a; a = a->next) {
1223 GList *p;
1224 b_VFormatAttribute *attr = a->data;
1225 printf ("+-- %s\n", attr->name);
1226 if (attr->params) {
1227 printf (" +- params=\n");
1229 for (p = attr->params, i = 0; p; p = p->next, i++) {
1230 b_VFormatParam *param = p->data;
1231 printf (" | [%d] = %s", i,param->name);
1232 printf ("(");
1233 for (v = param->values; v; v = v->next) {
1234 char *value = b_vformat_escape_string ((char*)v->data, VFORMAT_CARD_21);
1235 printf ("%s", value);
1236 if (v->next)
1237 printf (",");
1238 g_free (value);
1241 printf (")\n");
1244 printf (" +- values=\n");
1245 for (v = attr->values, i = 0; v; v = v->next, i++) {
1246 printf (" [%d] = `%s'\n", i, (char*)v->data);
1251 b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name)
1253 b_VFormatAttribute *attr;
1255 attr = g_new0 (b_VFormatAttribute, 1);
1257 attr->group = g_strdup (attr_group);
1258 attr->name = g_strdup (attr_name);
1260 return attr;
1263 void
1264 b_vformat_attribute_free (b_VFormatAttribute *attr)
1266 g_return_if_fail (attr != NULL);
1268 g_free (attr->block);
1269 g_free (attr->group);
1270 g_free (attr->name);
1272 b_vformat_attribute_remove_values (attr);
1274 b_vformat_attribute_remove_params (attr);
1276 g_free (attr);
1279 b_VFormatAttribute*
1280 b_vformat_attribute_copy (b_VFormatAttribute *attr)
1282 b_VFormatAttribute *a;
1283 GList *p;
1285 g_return_val_if_fail (attr != NULL, NULL);
1287 a = b_vformat_attribute_new (b_vformat_attribute_get_group (attr),
1288 b_vformat_attribute_get_name (attr));
1290 for (p = attr->values; p; p = p->next)
1291 b_vformat_attribute_add_value (a, p->data);
1293 for (p = attr->params; p; p = p->next)
1294 b_vformat_attribute_add_param (a, b_vformat_attribute_param_copy (p->data));
1296 return a;
1299 void
1300 b_vformat_remove_attributes (b_VFormat *evc, const char *attr_group, const char *attr_name)
1302 GList *attr;
1304 g_return_if_fail (attr_name != NULL);
1306 attr = evc->attributes;
1307 while (attr) {
1308 GList *next_attr;
1309 b_VFormatAttribute *a = attr->data;
1311 next_attr = attr->next;
1313 if (((!attr_group && !a->group) ||
1314 (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
1315 ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
1317 /* matches, remove/delete the attribute */
1318 evc->attributes = g_list_remove_link (evc->attributes, attr);
1320 b_vformat_attribute_free (a);
1323 attr = next_attr;
1327 void
1328 b_vformat_remove_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1330 g_return_if_fail (attr != NULL);
1332 evc->attributes = g_list_remove (evc->attributes, attr);
1333 b_vformat_attribute_free (attr);
1336 void
1337 b_vformat_add_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
1339 g_return_if_fail (attr != NULL);
1341 evc->attributes = g_list_append (evc->attributes, attr);
1344 void
1345 b_vformat_add_attribute_with_value (b_VFormat *b_VFormat,
1346 b_VFormatAttribute *attr, const char *value)
1348 g_return_if_fail (attr != NULL);
1350 b_vformat_attribute_add_value (attr, value);
1352 b_vformat_add_attribute (b_VFormat, attr);
1355 void
1356 b_vformat_add_attribute_with_values (b_VFormat *b_VFormat, b_VFormatAttribute *attr, ...)
1358 va_list ap;
1359 char *v;
1361 g_return_if_fail (attr != NULL);
1363 va_start (ap, attr);
1365 while ((v = va_arg (ap, char*))) {
1366 b_vformat_attribute_add_value (attr, v);
1369 va_end (ap);
1371 b_vformat_add_attribute (b_VFormat, attr);
1374 void
1375 b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value)
1377 g_return_if_fail (attr != NULL);
1379 attr->values = g_list_append (attr->values, g_strdup (value));
1382 void
1383 b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len)
1385 g_return_if_fail (attr != NULL);
1387 switch (attr->encoding) {
1388 case VF_ENCODING_RAW:
1389 osync_trace(TRACE_INTERNAL, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1390 break;
1391 case VF_ENCODING_BASE64: {
1392 char *b64_data = base64_encode_simple (value, len);
1393 GString *decoded = g_string_new_len (value, len);
1395 /* make sure the decoded list is up to date */
1396 b_vformat_attribute_get_values_decoded (attr);
1398 attr->values = g_list_append (attr->values, b64_data);
1399 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1400 break;
1402 case VF_ENCODING_QP: {
1403 char *qp_data = quoted_encode_simple ((unsigned char*)value, len);
1404 GString *decoded = g_string_new (value);
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, qp_data);
1410 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1411 break;
1413 case VF_ENCODING_8BIT: {
1414 char *data = g_strdup(value);
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, data);
1421 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1422 break;
1427 void
1428 b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...)
1430 va_list ap;
1431 char *v;
1433 g_return_if_fail (attr != NULL);
1435 va_start (ap, attr);
1437 while ((v = va_arg (ap, char*))) {
1438 b_vformat_attribute_add_value (attr, v);
1441 va_end (ap);
1444 static void
1445 free_gstring (GString *str)
1447 g_string_free (str, TRUE);
1450 void
1451 b_vformat_attribute_remove_values (b_VFormatAttribute *attr)
1453 g_return_if_fail (attr != NULL);
1455 g_list_foreach (attr->values, (GFunc)g_free, NULL);
1456 g_list_free (attr->values);
1457 attr->values = NULL;
1459 g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
1460 g_list_free (attr->decoded_values);
1461 attr->decoded_values = NULL;
1464 void
1465 b_vformat_attribute_remove_params (b_VFormatAttribute *attr)
1467 g_return_if_fail (attr != NULL);
1469 g_list_foreach (attr->params, (GFunc)b_vformat_attribute_param_free, NULL);
1470 g_list_free (attr->params);
1471 attr->params = NULL;
1473 /* also remove the cached encoding on this attribute */
1474 attr->encoding_set = FALSE;
1475 attr->encoding = VF_ENCODING_RAW;
1478 b_VFormatParam*
1479 b_vformat_attribute_param_new (const char *name)
1481 b_VFormatParam *param = g_new0 (b_VFormatParam, 1);
1482 param->name = g_strdup (name);
1484 return param;
1487 void
1488 b_vformat_attribute_param_free (b_VFormatParam *param)
1490 g_return_if_fail (param != NULL);
1492 g_free (param->name);
1494 b_vformat_attribute_param_remove_values (param);
1496 g_free (param);
1499 b_VFormatParam*
1500 b_vformat_attribute_param_copy (b_VFormatParam *param)
1502 b_VFormatParam *p;
1503 GList *l;
1505 g_return_val_if_fail (param != NULL, NULL);
1507 p = b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param));
1509 for (l = param->values; l; l = l->next) {
1510 b_vformat_attribute_param_add_value (p, l->data);
1513 return p;
1516 void
1517 b_vformat_attribute_add_param (b_VFormatAttribute *attr,
1518 b_VFormatParam *param)
1520 g_return_if_fail (attr != NULL);
1521 g_return_if_fail (param != NULL);
1523 attr->params = g_list_append (attr->params, param);
1525 /* we handle our special encoding stuff here */
1527 if (!g_ascii_strcasecmp (param->name, "ENCODING")) {
1528 if (attr->encoding_set) {
1529 osync_trace(TRACE_INTERNAL, "ENCODING specified twice");
1530 return;
1533 if (param->values && param->values->data) {
1534 if (_helper_is_base64((const char*)param->values->data))
1535 attr->encoding = VF_ENCODING_BASE64;
1536 else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE"))
1537 attr->encoding = VF_ENCODING_QP;
1538 else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT"))
1539 attr->encoding = VF_ENCODING_8BIT;
1540 else {
1541 osync_trace(TRACE_INTERNAL, "Unknown value `%s' for ENCODING parameter. values will be treated as raw",
1542 (char*)param->values->data);
1545 attr->encoding_set = TRUE;
1547 else {
1548 osync_trace(TRACE_INTERNAL, "ENCODING parameter added with no value");
1553 b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level)
1555 g_return_val_if_fail (attr != NULL, NULL);
1556 GList *p = NULL;
1557 for (p = attr->params; p; p = p->next) {
1558 b_VFormatParam *param = p->data;
1559 if (!g_ascii_strcasecmp (param->name, name)) {
1560 if( level == 0 )
1561 return param;
1562 else
1563 level--;
1566 return NULL;
1569 void
1570 b_vformat_attribute_set_value (b_VFormatAttribute *attr,
1571 int nth, const char *value)
1573 GList *param = g_list_nth(attr->values, nth);
1574 g_free(param->data);
1575 param->data = g_strdup(value);
1578 void
1579 b_vformat_attribute_param_add_value (b_VFormatParam *param,
1580 const char *value)
1582 g_return_if_fail (param != NULL);
1584 param->values = g_list_append (param->values, g_strdup (value));
1587 void
1588 b_vformat_attribute_param_add_values (b_VFormatParam *param,
1589 ...)
1591 va_list ap;
1592 char *v;
1594 g_return_if_fail (param != NULL);
1596 va_start (ap, param);
1598 while ((v = va_arg (ap, char*))) {
1599 b_vformat_attribute_param_add_value (param, v);
1602 va_end (ap);
1605 void
1606 b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value)
1608 g_return_if_fail (attr != NULL);
1609 g_return_if_fail (name != NULL);
1611 if (!value)
1612 return;
1614 b_VFormatParam *param = b_vformat_attribute_param_new(name);
1616 b_vformat_attribute_param_add_value (param, value);
1618 b_vformat_attribute_add_param (attr, param);
1621 void
1622 b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr,
1623 b_VFormatParam *param, ...)
1625 va_list ap;
1626 char *v;
1628 g_return_if_fail (attr != NULL);
1629 g_return_if_fail (param != NULL);
1631 va_start (ap, param);
1633 while ((v = va_arg (ap, char*))) {
1634 b_vformat_attribute_param_add_value (param, v);
1637 va_end (ap);
1639 b_vformat_attribute_add_param (attr, param);
1642 void
1643 b_vformat_attribute_param_remove_values (b_VFormatParam *param)
1645 g_return_if_fail (param != NULL);
1647 g_list_foreach (param->values, (GFunc)g_free, NULL);
1648 g_list_free (param->values);
1649 param->values = NULL;
1652 GList*
1653 b_vformat_get_attributes (b_VFormat *format)
1655 return format->attributes;
1658 const char*
1659 b_vformat_attribute_get_group (b_VFormatAttribute *attr)
1661 g_return_val_if_fail (attr != NULL, NULL);
1663 return attr->group;
1666 const char*
1667 b_vformat_attribute_get_name (b_VFormatAttribute *attr)
1669 g_return_val_if_fail (attr != NULL, NULL);
1671 return attr->name;
1674 const char*
1675 b_vformat_attribute_get_block (b_VFormatAttribute *attr)
1677 g_return_val_if_fail (attr != NULL, NULL);
1679 return attr->block;
1682 GList*
1683 b_vformat_attribute_get_values (b_VFormatAttribute *attr)
1685 g_return_val_if_fail (attr != NULL, NULL);
1687 return attr->values;
1690 GList*
1691 b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr)
1693 g_return_val_if_fail (attr != NULL, NULL);
1695 if (!attr->decoded_values) {
1696 GList *l;
1697 switch (attr->encoding) {
1698 case VF_ENCODING_RAW:
1699 case VF_ENCODING_8BIT:
1700 for (l = attr->values; l; l = l->next)
1701 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data));
1702 break;
1703 case VF_ENCODING_BASE64:
1704 for (l = attr->values; l; l = l->next) {
1705 char *decoded = g_strdup ((char*)l->data);
1706 int len = base64_decode_simple (decoded, strlen (decoded));
1707 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1708 g_free (decoded);
1710 break;
1711 case VF_ENCODING_QP:
1712 for (l = attr->values; l; l = l->next) {
1713 if (!(l->data))
1714 continue;
1715 char *decoded = g_strdup ((char*)l->data);
1716 int len = quoted_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;
1724 return attr->decoded_values;
1727 gboolean
1728 b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr)
1730 g_return_val_if_fail (attr != NULL, FALSE);
1732 if (attr->values == NULL
1733 || attr->values->next != NULL)
1734 return FALSE;
1736 return TRUE;
1739 char*
1740 b_vformat_attribute_get_value (b_VFormatAttribute *attr)
1742 GList *values;
1744 g_return_val_if_fail (attr != NULL, NULL);
1746 values = b_vformat_attribute_get_values (attr);
1748 if (!b_vformat_attribute_is_single_valued (attr))
1749 osync_trace(TRACE_INTERNAL, "b_vformat_attribute_get_value called on multivalued attribute");
1751 return values ? g_strdup ((char*)values->data) : NULL;
1754 GString*
1755 b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr)
1757 GList *values;
1758 GString *str = NULL;
1760 g_return_val_if_fail (attr != NULL, NULL);
1762 values = b_vformat_attribute_get_values_decoded (attr);
1764 if (!b_vformat_attribute_is_single_valued (attr))
1765 osync_trace(TRACE_INTERNAL, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1767 if (values)
1768 str = values->data;
1770 return str ? g_string_new_len (str->str, str->len) : NULL;
1773 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth)
1775 GList *values = b_vformat_attribute_get_values_decoded(attr);
1776 if (!values)
1777 return NULL;
1778 GString *retstr = (GString *)g_list_nth_data(values, nth);
1779 if (!retstr)
1780 return NULL;
1782 if (!g_utf8_validate(retstr->str, -1, NULL)) {
1783 values = b_vformat_attribute_get_values(attr);
1784 if (!values)
1785 return NULL;
1786 return g_list_nth_data(values, nth);
1789 return retstr->str;
1792 gboolean
1793 b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr)
1795 GList *params;
1796 GList *p;
1798 g_return_val_if_fail (attr != NULL, FALSE);
1799 g_return_val_if_fail (typestr != NULL, FALSE);
1801 params = b_vformat_attribute_get_params (attr);
1803 for (p = params; p; p = p->next) {
1804 b_VFormatParam *param = p->data;
1806 if (!strcasecmp (b_vformat_attribute_param_get_name (param), "TYPE")) {
1807 GList *values = b_vformat_attribute_param_get_values (param);
1808 GList *v;
1810 for (v = values; v; v = v->next) {
1811 if (!strcasecmp ((char*)v->data, typestr))
1812 return TRUE;
1817 return FALSE;
1821 gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name)
1823 g_return_val_if_fail (attr != NULL, FALSE);
1824 g_return_val_if_fail (name != NULL, FALSE);
1826 GList *params = b_vformat_attribute_get_params(attr);
1827 GList *p;
1828 for (p = params; p; p = p->next) {
1829 b_VFormatParam *param = p->data;
1830 if (!strcasecmp(name, b_vformat_attribute_param_get_name(param)))
1831 return TRUE;
1833 return FALSE;
1836 GList*
1837 b_vformat_attribute_get_params (b_VFormatAttribute *attr)
1839 g_return_val_if_fail (attr != NULL, NULL);
1841 return attr->params;
1844 const char*
1845 b_vformat_attribute_param_get_name (b_VFormatParam *param)
1847 g_return_val_if_fail (param != NULL, NULL);
1849 return param->name;
1852 GList*
1853 b_vformat_attribute_param_get_values (b_VFormatParam *param)
1855 g_return_val_if_fail (param != NULL, NULL);
1857 return param->values;
1860 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth)
1862 const char *ret = NULL;
1863 GList *values = b_vformat_attribute_param_get_values(param);
1864 if (!values)
1865 return NULL;
1866 ret = g_list_nth_data(values, nth);
1867 return ret;
1870 static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1872 //static unsigned char _evc_base64_rank[256];
1874 static void base64_init(char *rank)
1876 int i;
1878 memset(rank, 0xff, sizeof(rank));
1879 for (i=0;i<64;i++) {
1880 rank[(unsigned int)base64_alphabet[i]] = i;
1882 rank['='] = 0;
1885 /* call this when finished encoding everything, to
1886 flush off the last little bit */
1887 static size_t base64_encode_close(unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
1889 int c1, c2;
1890 unsigned char *outptr = out;
1892 if (inlen>0)
1893 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
1895 c1 = ((unsigned char *)save)[1];
1896 c2 = ((unsigned char *)save)[2];
1898 switch (((char *)save)[0]) {
1899 case 2:
1900 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
1901 g_assert(outptr[2] != 0);
1902 goto skip;
1903 case 1:
1904 outptr[2] = '=';
1905 skip:
1906 outptr[0] = base64_alphabet[ c1 >> 2 ];
1907 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
1908 outptr[3] = '=';
1909 outptr += 4;
1910 break;
1912 if (break_lines)
1913 *outptr++ = '\n';
1915 *save = 0;
1916 *state = 0;
1918 return outptr-out;
1922 performs an 'encode step', only encodes blocks of 3 characters to the
1923 output at a time, saves left-over state in state and save (initialise to
1924 0 on first invocation).
1926 static size_t base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
1928 register unsigned char *inptr, *outptr;
1930 if (len<=0)
1931 return 0;
1933 inptr = in;
1934 outptr = out;
1936 if (len + ((char *)save)[0] > 2) {
1937 unsigned char *inend = in+len-2;
1938 register int c1, c2, c3;
1939 register int already;
1941 already = *state;
1943 switch (((char *)save)[0]) {
1944 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1945 case 2: c1 = ((unsigned char *)save)[1];
1946 c2 = ((unsigned char *)save)[2]; goto skip2;
1949 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1950 while (inptr < inend) {
1951 c1 = *inptr++;
1952 skip1:
1953 c2 = *inptr++;
1954 skip2:
1955 c3 = *inptr++;
1956 *outptr++ = base64_alphabet[ c1 >> 2 ];
1957 *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
1958 *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
1959 *outptr++ = base64_alphabet[ c3 & 0x3f ];
1960 /* this is a bit ugly ... */
1961 if (break_lines && (++already)>=19) {
1962 *outptr++='\n';
1963 already = 0;
1967 ((char *)save)[0] = 0;
1968 len = 2-(inptr-inend);
1969 *state = already;
1972 if (len>0) {
1973 register char *saveout;
1975 /* points to the slot for the next char to save */
1976 saveout = & (((char *)save)[1]) + ((char *)save)[0];
1978 /* len can only be 0 1 or 2 */
1979 switch(len) {
1980 case 2: *saveout++ = *inptr++;
1981 case 1: *saveout++ = *inptr++;
1983 ((char *)save)[0]+=len;
1986 return outptr-out;
1991 * base64_decode_step: decode a chunk of base64 encoded data
1992 * @in: input stream
1993 * @len: max length of data to decode
1994 * @out: output stream
1995 * @state: holds the number of bits that are stored in @save
1996 * @save: leftover bits that have not yet been decoded
1998 * Decodes a chunk of base64 encoded data
2000 static size_t base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
2002 unsigned char base64_rank[256];
2003 base64_init((char*)base64_rank);
2005 register unsigned char *inptr, *outptr;
2006 unsigned char *inend, c;
2007 register unsigned int v;
2008 int i;
2010 inend = in+len;
2011 outptr = out;
2013 /* convert 4 base64 bytes to 3 normal bytes */
2014 v=*save;
2015 i=*state;
2016 inptr = in;
2017 while (inptr<inend) {
2018 c = base64_rank[*inptr++];
2019 if (c != 0xff) {
2020 v = (v<<6) | c;
2021 i++;
2022 if (i==4) {
2023 *outptr++ = v>>16;
2024 *outptr++ = v>>8;
2025 *outptr++ = v;
2026 i=0;
2031 *save = v;
2032 *state = i;
2034 /* quick scan back for '=' on the end somewhere */
2035 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2036 i=2;
2037 while (inptr>in && i) {
2038 inptr--;
2039 if (base64_rank[*inptr] != 0xff) {
2040 if (*inptr == '=' && outptr>out)
2041 outptr--;
2042 i--;
2046 /* if i!= 0 then there is a truncation error! */
2047 return outptr-out;
2050 static char *base64_encode_simple (const char *data, size_t len)
2052 unsigned char *out;
2053 int state = 0, outlen;
2054 unsigned int save = 0;
2056 g_return_val_if_fail (data != NULL, NULL);
2058 out = g_malloc (len * 4 / 3 + 5);
2059 outlen = base64_encode_close ((unsigned char *)data, len, FALSE,
2060 out, &state, (int*)&save);
2061 out[outlen] = '\0';
2062 return (char *)out;
2065 static size_t base64_decode_simple (char *data, size_t len)
2067 int state = 0;
2068 unsigned int save = 0;
2070 g_return_val_if_fail (data != NULL, 0);
2072 return base64_decode_step ((unsigned char *)data, len,
2073 (unsigned char *)data, &state, &save);
2076 static char *quoted_encode_simple(const unsigned char *string, int len)
2078 GString *tmp = g_string_new("");
2080 int i = 0;
2081 while(string[i] != 0) {
2082 if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') {
2083 g_string_append_printf(tmp, "=%02X", string[i]);
2084 } else {
2085 g_string_append_c(tmp, string[i]);
2087 i++;
2090 char *ret = tmp->str;
2091 g_string_free(tmp, FALSE);
2092 return ret;
2096 static size_t quoted_decode_simple (char *data, size_t len)
2098 g_return_val_if_fail (data != NULL, 0);
2100 GString *string = g_string_new(data);
2101 if (!string)
2102 return 0;
2104 char hex[5];
2105 hex[4] = 0;
2107 while (1) {
2108 //Get the index of the next encoded char
2109 int i = strcspn(string->str, "=");
2110 if (i >= strlen(string->str))
2111 break;
2113 strcpy(hex, "0x");
2114 strncat(hex, &string->str[i + 1], 2);
2115 char rep = ((int)(strtod(hex, NULL)));
2116 g_string_erase(string, i, 2);
2117 g_string_insert_c(string, i, rep);
2120 memset(data, 0, strlen(data));
2121 strcpy(data, string->str);
2122 g_string_free(string, 1);
2124 return strlen(data);