- added code to force struct tm.tm_isdst to -1 before calling
[barry.git] / opensync-plugin / src / vformat.c
blob8f7ea2d072c8048ca7882f5b0492ecd5e499ebb3
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 size_t base64_decode_simple (char *data, size_t len);
39 char *base64_encode_simple (const char *data, size_t len);
41 size_t quoted_decode_simple (char *data, size_t len);
42 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 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.is_dst = -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 (VFormatAttribute *attr, GString *str, GString *charset)
220 /* don't convert empty strings */
221 if (str->len == 0) {
222 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 vformat_attribute_add_value(attr, outbuf);
248 } else {
250 /* hmm, should not happen */
251 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 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 vformat_attribute_add_value (attr, outbuf);
276 } else {
278 vformat_attribute_add_value (attr, str->str);
282 iconv_close(cd);
288 free(outbuf);
292 static void _read_attribute_value (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(VFormatAttribute *attr, char **p, int *format_encoding, GString **charset)
436 char *lp = *p;
437 GString *str;
438 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 = 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 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 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 vformat_attribute_param_free (param);
507 param = NULL;
508 } else if ( _helper_is_base64(param->values->data)) {
509 *format_encoding = VF_ENCODING_BASE64;
510 // 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 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 = vformat_attribute_param_new (param_name);
541 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 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 VFormatAttribute *_read_attribute (char **p)
588 char *attr_group = NULL;
589 char *attr_name = NULL;
590 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 = 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 vformat_attribute_free (attr);
675 return NULL;
678 /* we try to be as forgiving as we possibly can here - this isn't a
679 * validator. Almost nothing is considered a fatal error. We always
680 * try to return *something*.
682 static void _parse(VFormat *evc, const char *str)
684 char *buf = g_strdup (str);
685 char *p, *end;
686 VFormatAttribute *attr;
688 /* first validate the string is valid utf8 */
689 if (!g_utf8_validate (buf, -1, (const char **)&end)) {
690 /* if the string isn't valid, we parse as much as we can from it */
691 osync_trace(TRACE_INTERNAL, "invalid utf8 passed to VFormat. Limping along.");
692 *end = '\0';
695 buf = _fold_lines (buf);
697 p = buf;
699 attr = _read_attribute (&p);
700 if (!attr)
701 attr = _read_attribute (&p);
703 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
704 osync_trace(TRACE_INTERNAL, "vformat began without a BEGIN\n");
706 if (attr && !g_ascii_strcasecmp (attr->name, "begin"))
707 vformat_attribute_free (attr);
708 else if (attr)
709 vformat_add_attribute (evc, attr);
711 while (*p) {
712 VFormatAttribute *next_attr = _read_attribute (&p);
714 if (next_attr) {
715 //if (g_ascii_strcasecmp (next_attr->name, "end"))
716 vformat_add_attribute (evc, next_attr);
717 attr = next_attr;
721 if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
722 osync_trace(TRACE_INTERNAL, "vformat ended without END");
725 g_free (buf);
728 char *vformat_escape_string (const char *s, VFormatType type)
730 GString *str;
731 const char *p;
733 str = g_string_new ("");
735 /* Escape a string as described in RFC2426, section 5 */
736 for (p = s; p && *p; p++) {
737 switch (*p) {
738 case '\n':
739 str = g_string_append (str, "\\n");
740 break;
741 case '\r':
742 if (*(p+1) == '\n')
743 p++;
744 str = g_string_append (str, "\\n");
745 break;
746 case ';':
747 str = g_string_append (str, "\\;");
748 break;
749 case ',':
750 if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20)
751 str = g_string_append (str, "\\,");
752 else
753 str = g_string_append_c (str, *p);
754 break;
755 case '\\':
756 /**
757 * We won't escape backslashes
758 * on vcard 2.1, unless it is in the end of a value.
759 * See comments above for a better explanation
761 if (*p != '\0' && type == VFORMAT_CARD_21) {
762 osync_trace(TRACE_INTERNAL, "[%s]We won't escape backslashes", __func__);
763 str = g_string_append_c(str, *p);
765 else {
766 osync_trace(TRACE_INTERNAL, "[%s] escape backslashes!!", __func__);
767 str = g_string_append (str, "\\\\");
769 break;
770 default:
771 str = g_string_append_c (str, *p);
772 break;
776 return g_string_free (str, FALSE);
779 char*
780 vformat_unescape_string (const char *s)
782 GString *str;
783 const char *p;
785 g_return_val_if_fail (s != NULL, NULL);
787 str = g_string_new ("");
789 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
790 for (p = s; *p; p++) {
791 if (*p == '\\') {
792 p++;
793 if (*p == '\0') {
794 str = g_string_append_c (str, '\\');
795 break;
797 switch (*p) {
798 case 'n': str = g_string_append_c (str, '\n'); break;
799 case 'r': str = g_string_append_c (str, '\r'); break;
800 case ';': str = g_string_append_c (str, ';'); break;
801 case ',': str = g_string_append_c (str, ','); break;
802 case '\\': str = g_string_append_c (str, '\\'); break;
803 case '"': str = g_string_append_c (str, '"'); break;
804 /* \t is (incorrectly) used by kOrganizer, so handle it here */
805 case 't': str = g_string_append_c (str, '\t'); break;
806 default:
807 osync_trace(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %s", *p);
808 str = g_string_append_c (str, '\\');
809 str = g_string_append_unichar (str, g_utf8_get_char(p));
810 break;
815 return g_string_free (str, FALSE);
818 void
819 vformat_construct (VFormat *evc, const char *str)
821 g_return_if_fail (str != NULL);
823 if (*str)
824 _parse (evc, str);
827 void vformat_free(VFormat *format)
829 g_list_foreach (format->attributes, (GFunc)vformat_attribute_free, NULL);
830 g_list_free (format->attributes);
831 g_free(format);
834 VFormat *vformat_new_from_string (const char *str)
836 g_return_val_if_fail (str != NULL, NULL);
837 VFormat *evc = g_malloc0(sizeof(VFormat));
839 vformat_construct (evc, str);
841 return evc;
844 VFormat *vformat_new(void)
846 return vformat_new_from_string ("");
849 VFormatAttribute *vformat_find_attribute(VFormat *vcard, const char *name, int nth)
851 GList *attributes = vformat_get_attributes(vcard);
852 GList *a = NULL;
853 int i = 0;
854 for (a = attributes; a; a = a->next) {
855 VFormatAttribute *attr = a->data;
856 if (!g_ascii_strcasecmp(vformat_attribute_get_name(attr), name)) {
857 if( i == nth )
858 return attr;
859 i++;
862 return NULL;
865 char *vformat_to_string (VFormat *evc, VFormatType type)
867 osync_trace(TRACE_ENTRY, "%s(%p, %i)", __func__, type);
868 GList *l;
869 GList *v;
871 GString *str = g_string_new ("");
873 switch (type) {
874 case VFORMAT_CARD_21:
875 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
876 break;
877 case VFORMAT_CARD_30:
878 str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
879 break;
880 case VFORMAT_TODO_10:
881 case VFORMAT_EVENT_10:
882 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
883 break;
884 case VFORMAT_TODO_20:
885 case VFORMAT_EVENT_20:
886 str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
887 break;
888 case VFORMAT_NOTE:
889 str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
890 break;
893 for (l = evc->attributes; l; l = l->next) {
894 GList *p;
895 VFormatAttribute *attr = l->data;
896 GString *attr_str;
897 int l;
898 int format_encoding = VF_ENCODING_RAW;
900 attr_str = g_string_new ("");
902 /* From rfc2425, 5.8.2
904 * contentline = [group "."] name *(";" param) ":" value CRLF
907 if (attr->group) {
908 attr_str = g_string_append (attr_str, attr->group);
909 attr_str = g_string_append_c (attr_str, '.');
911 attr_str = g_string_append (attr_str, attr->name);
912 /* handle the parameters */
913 for (p = attr->params; p; p = p->next) {
914 VFormatParam *param = p->data;
915 /* 5.8.2:
916 * param = param-name "=" param-value *("," param-value)
918 if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20
919 || type == VFORMAT_EVENT_20) {
922 * Character set can only be specified on the CHARSET
923 * parameter on the Content-Type MIME header field.
925 if (!g_ascii_strcasecmp (param->name, "CHARSET"))
926 continue;
927 attr_str = g_string_append_c (attr_str, ';');
928 attr_str = g_string_append (attr_str, param->name);
929 if (param->values) {
930 attr_str = g_string_append_c (attr_str, '=');
932 for (v = param->values; v; v = v->next) {
933 if (_helper_is_base64((const char *) v->data)) {
934 format_encoding = VF_ENCODING_BASE64;
935 /*Only the "B" encoding of [RFC 2047] is an allowed*/
936 v->data="B";
939 * QUOTED-PRINTABLE inline encoding has been
940 * eliminated.
942 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) {
943 osync_trace(TRACE_ERROR, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__);
944 format_encoding = VF_ENCODING_QP;
946 attr_str = g_string_append (attr_str, v->data);
948 if (v->next)
949 attr_str = g_string_append_c (attr_str, ',');
952 else {
953 attr_str = g_string_append_c (attr_str, ';');
955 * The "TYPE=" is optional skip it.
956 * LOGO, PHOTO and SOUND multimedia formats MUST
957 * have a "TYPE=" parameter
959 gboolean must_have_type = FALSE;
960 if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") )
961 must_have_type = TRUE;
962 if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") )
963 attr_str = g_string_append (attr_str, param->name);
964 if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) )
965 attr_str = g_string_append_c (attr_str, '=');
966 for (v = param->values; v; v = v->next) {
967 // check for quoted-printable encoding
968 if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE"))
969 format_encoding = VF_ENCODING_QP;
970 // check for base64 encoding
971 if (_helper_is_base64((const char *) v->data)) {
972 format_encoding = VF_ENCODING_BASE64;
973 v->data="BASE64";
975 attr_str = g_string_append (attr_str, v->data);
976 if (v->next)
977 attr_str = g_string_append_c (attr_str, ',');
982 attr_str = g_string_append_c (attr_str, ':');
984 for (v = attr->values; v; v = v->next) {
985 char *value = v->data;
986 char *escaped_value = NULL;
988 if (!g_ascii_strcasecmp (attr->name, "RRULE") &&
989 strstr (value, "BYDAY") == v->data) {
990 attr_str = g_string_append (attr_str, value);
991 } else {
992 escaped_value = vformat_escape_string (value, type);
993 attr_str = g_string_append (attr_str, escaped_value);
996 if (v->next) {
998 /* XXX toshok - i hate you, rfc 2426.
999 why doesn't CATEGORIES use a ; like
1000 a normal list attribute? */
1001 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
1002 attr_str = g_string_append_c (attr_str, ',');
1003 else
1004 attr_str = g_string_append_c (attr_str, ';');
1007 g_free (escaped_value);
1010 /* Folding lines:
1011 * ^^^^^^^^^^^^^^
1013 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1014 * After generating a content line,
1015 * lines longer than 75 characters SHOULD be folded according to the
1016 * folding procedure described in [MIME-DIR].
1018 * rfc 2445 (iCalendar), 4.1 Content Lines:
1019 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1020 * break. Long content lines SHOULD be split into a multiple line
1021 * representations using a line "folding" technique. That is, a long
1022 * line can be split between any two characters by inserting a CRLF
1023 * immediately followed by a single linear white space character (i.e.,
1024 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1025 * of CRLF followed immediately by a single linear white space character
1026 * is ignored (i.e., removed) when processing the content type.
1028 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1029 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1031 * Differences between encodings:
1032 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1034 * rfc 2425 [MIME-DIR], 5.8.1:
1035 * A logical line MAY be continued on the next physical line anywhere
1036 * between two characters by inserting a CRLF immediately followed by a
1037 * single <WS> (white space) character.
1039 * rfc 2045, 6.7, chapter 5:
1040 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1041 * without follwing <WS>
1043 * UTF-8
1044 * ^^^^^
1046 * Note that all the line folding above is described in terms of characters
1047 * not bytes. In particular, it would be an error to put a line break
1048 * within a UTF-8 character.
1051 l = 0;
1052 do {
1053 if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) {
1054 l += 75;
1056 /* If using QP, must be sure that we do not fold within a quote sequence */
1057 if (format_encoding == VF_ENCODING_QP) {
1058 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--;
1059 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2;
1062 char *p = g_utf8_offset_to_pointer(attr_str->str, l);
1064 if (format_encoding == VF_ENCODING_QP)
1065 attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1);
1066 else
1067 attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1);
1069 else
1070 break;
1071 } while (l < g_utf8_strlen(attr_str->str, attr_str->len));
1073 attr_str = g_string_append (attr_str, CRLF);
1075 * base64= <MIME RFC 1521 base64 text>
1076 * the end of the text is marked with two CRLF sequences
1077 * this results in one blank line before the start of the
1078 * next property
1080 if( format_encoding == VF_ENCODING_BASE64
1081 && (type == VFORMAT_CARD_21))
1082 attr_str = g_string_append (attr_str, CRLF);
1084 str = g_string_append (str, attr_str->str);
1085 g_string_free (attr_str, TRUE);
1088 switch (type) {
1089 case VFORMAT_CARD_21:
1090 str = g_string_append (str, "END:VCARD\r\n");
1091 break;
1092 case VFORMAT_CARD_30:
1093 str = g_string_append (str, "END:VCARD\r\n");
1094 break;
1095 case VFORMAT_TODO_10:
1096 case VFORMAT_EVENT_10:
1097 str = g_string_append (str, "END:VCALENDAR\r\n");
1098 break;
1099 case VFORMAT_TODO_20:
1100 case VFORMAT_EVENT_20:
1101 str = g_string_append (str, "END:VCALENDAR\r\n");
1102 break;
1103 case VFORMAT_NOTE:
1104 str = g_string_append (str, "END:VNOTE\r\n");
1105 break;
1108 osync_trace(TRACE_EXIT, "%s(%p, %i)", __func__, type);
1109 return g_string_free (str, FALSE);
1112 void vformat_dump_structure (VFormat *evc)
1114 GList *a;
1115 GList *v;
1116 int i;
1118 printf ("VFormat\n");
1119 for (a = evc->attributes; a; a = a->next) {
1120 GList *p;
1121 VFormatAttribute *attr = a->data;
1122 printf ("+-- %s\n", attr->name);
1123 if (attr->params) {
1124 printf (" +- params=\n");
1126 for (p = attr->params, i = 0; p; p = p->next, i++) {
1127 VFormatParam *param = p->data;
1128 printf (" | [%d] = %s", i,param->name);
1129 printf ("(");
1130 for (v = param->values; v; v = v->next) {
1131 char *value = vformat_escape_string ((char*)v->data, VFORMAT_CARD_21);
1132 printf ("%s", value);
1133 if (v->next)
1134 printf (",");
1135 g_free (value);
1138 printf (")\n");
1141 printf (" +- values=\n");
1142 for (v = attr->values, i = 0; v; v = v->next, i++) {
1143 printf (" [%d] = `%s'\n", i, (char*)v->data);
1148 VFormatAttribute *vformat_attribute_new (const char *attr_group, const char *attr_name)
1150 VFormatAttribute *attr;
1152 attr = g_new0 (VFormatAttribute, 1);
1154 attr->group = g_strdup (attr_group);
1155 attr->name = g_strdup (attr_name);
1157 return attr;
1160 void
1161 vformat_attribute_free (VFormatAttribute *attr)
1163 g_return_if_fail (attr != NULL);
1165 g_free (attr->group);
1166 g_free (attr->name);
1168 vformat_attribute_remove_values (attr);
1170 vformat_attribute_remove_params (attr);
1172 g_free (attr);
1175 VFormatAttribute*
1176 vformat_attribute_copy (VFormatAttribute *attr)
1178 VFormatAttribute *a;
1179 GList *p;
1181 g_return_val_if_fail (attr != NULL, NULL);
1183 a = vformat_attribute_new (vformat_attribute_get_group (attr),
1184 vformat_attribute_get_name (attr));
1186 for (p = attr->values; p; p = p->next)
1187 vformat_attribute_add_value (a, p->data);
1189 for (p = attr->params; p; p = p->next)
1190 vformat_attribute_add_param (a, vformat_attribute_param_copy (p->data));
1192 return a;
1195 void
1196 vformat_remove_attributes (VFormat *evc, const char *attr_group, const char *attr_name)
1198 GList *attr;
1200 g_return_if_fail (attr_name != NULL);
1202 attr = evc->attributes;
1203 while (attr) {
1204 GList *next_attr;
1205 VFormatAttribute *a = attr->data;
1207 next_attr = attr->next;
1209 if (((!attr_group && !a->group) ||
1210 (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
1211 ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
1213 /* matches, remove/delete the attribute */
1214 evc->attributes = g_list_remove_link (evc->attributes, attr);
1216 vformat_attribute_free (a);
1219 attr = next_attr;
1223 void
1224 vformat_remove_attribute (VFormat *evc, VFormatAttribute *attr)
1226 g_return_if_fail (attr != NULL);
1228 evc->attributes = g_list_remove (evc->attributes, attr);
1229 vformat_attribute_free (attr);
1232 void
1233 vformat_add_attribute (VFormat *evc, VFormatAttribute *attr)
1235 g_return_if_fail (attr != NULL);
1237 evc->attributes = g_list_append (evc->attributes, attr);
1240 void
1241 vformat_add_attribute_with_value (VFormat *VFormat,
1242 VFormatAttribute *attr, const char *value)
1244 g_return_if_fail (attr != NULL);
1246 vformat_attribute_add_value (attr, value);
1248 vformat_add_attribute (VFormat, attr);
1251 void
1252 vformat_add_attribute_with_values (VFormat *VFormat, VFormatAttribute *attr, ...)
1254 va_list ap;
1255 char *v;
1257 g_return_if_fail (attr != NULL);
1259 va_start (ap, attr);
1261 while ((v = va_arg (ap, char*))) {
1262 vformat_attribute_add_value (attr, v);
1265 va_end (ap);
1267 vformat_add_attribute (VFormat, attr);
1270 void
1271 vformat_attribute_add_value (VFormatAttribute *attr, const char *value)
1273 g_return_if_fail (attr != NULL);
1275 attr->values = g_list_append (attr->values, g_strdup (value));
1278 void
1279 vformat_attribute_add_value_decoded (VFormatAttribute *attr, const char *value, int len)
1281 g_return_if_fail (attr != NULL);
1283 switch (attr->encoding) {
1284 case VF_ENCODING_RAW:
1285 osync_trace(TRACE_INTERNAL, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1286 break;
1287 case VF_ENCODING_BASE64: {
1288 char *b64_data = base64_encode_simple (value, len);
1289 GString *decoded = g_string_new_len (value, len);
1291 /* make sure the decoded list is up to date */
1292 vformat_attribute_get_values_decoded (attr);
1294 attr->values = g_list_append (attr->values, b64_data);
1295 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1296 break;
1298 case VF_ENCODING_QP: {
1299 char *qp_data = quoted_encode_simple ((unsigned char*)value, len);
1300 GString *decoded = g_string_new (value);
1302 /* make sure the decoded list is up to date */
1303 vformat_attribute_get_values_decoded (attr);
1305 attr->values = g_list_append (attr->values, qp_data);
1306 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1307 break;
1309 case VF_ENCODING_8BIT: {
1310 char *data = g_strdup(value);
1311 GString *decoded = g_string_new (value);
1313 /* make sure the decoded list is up to date */
1314 vformat_attribute_get_values_decoded (attr);
1316 attr->values = g_list_append (attr->values, data);
1317 attr->decoded_values = g_list_append (attr->decoded_values, decoded);
1318 break;
1323 void
1324 vformat_attribute_add_values (VFormatAttribute *attr, ...)
1326 va_list ap;
1327 char *v;
1329 g_return_if_fail (attr != NULL);
1331 va_start (ap, attr);
1333 while ((v = va_arg (ap, char*))) {
1334 vformat_attribute_add_value (attr, v);
1337 va_end (ap);
1340 static void
1341 free_gstring (GString *str)
1343 g_string_free (str, TRUE);
1346 void
1347 vformat_attribute_remove_values (VFormatAttribute *attr)
1349 g_return_if_fail (attr != NULL);
1351 g_list_foreach (attr->values, (GFunc)g_free, NULL);
1352 g_list_free (attr->values);
1353 attr->values = NULL;
1355 g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
1356 g_list_free (attr->decoded_values);
1357 attr->decoded_values = NULL;
1360 void
1361 vformat_attribute_remove_params (VFormatAttribute *attr)
1363 g_return_if_fail (attr != NULL);
1365 g_list_foreach (attr->params, (GFunc)vformat_attribute_param_free, NULL);
1366 g_list_free (attr->params);
1367 attr->params = NULL;
1369 /* also remove the cached encoding on this attribute */
1370 attr->encoding_set = FALSE;
1371 attr->encoding = VF_ENCODING_RAW;
1374 VFormatParam*
1375 vformat_attribute_param_new (const char *name)
1377 VFormatParam *param = g_new0 (VFormatParam, 1);
1378 param->name = g_strdup (name);
1380 return param;
1383 void
1384 vformat_attribute_param_free (VFormatParam *param)
1386 g_return_if_fail (param != NULL);
1388 g_free (param->name);
1390 vformat_attribute_param_remove_values (param);
1392 g_free (param);
1395 VFormatParam*
1396 vformat_attribute_param_copy (VFormatParam *param)
1398 VFormatParam *p;
1399 GList *l;
1401 g_return_val_if_fail (param != NULL, NULL);
1403 p = vformat_attribute_param_new (vformat_attribute_param_get_name (param));
1405 for (l = param->values; l; l = l->next) {
1406 vformat_attribute_param_add_value (p, l->data);
1409 return p;
1412 void
1413 vformat_attribute_add_param (VFormatAttribute *attr,
1414 VFormatParam *param)
1416 g_return_if_fail (attr != NULL);
1417 g_return_if_fail (param != NULL);
1419 attr->params = g_list_append (attr->params, param);
1421 /* we handle our special encoding stuff here */
1423 if (!g_ascii_strcasecmp (param->name, "ENCODING")) {
1424 if (attr->encoding_set) {
1425 osync_trace(TRACE_INTERNAL, "ENCODING specified twice");
1426 return;
1429 if (param->values && param->values->data) {
1430 if (_helper_is_base64((const char*)param->values->data))
1431 attr->encoding = VF_ENCODING_BASE64;
1432 else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE"))
1433 attr->encoding = VF_ENCODING_QP;
1434 else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT"))
1435 attr->encoding = VF_ENCODING_8BIT;
1436 else {
1437 osync_trace(TRACE_INTERNAL, "Unknown value `%s' for ENCODING parameter. values will be treated as raw",
1438 (char*)param->values->data);
1441 attr->encoding_set = TRUE;
1443 else {
1444 osync_trace(TRACE_INTERNAL, "ENCODING parameter added with no value");
1449 VFormatParam *vformat_attribute_find_param(VFormatAttribute *attr, const char *name)
1451 g_return_val_if_fail (attr != NULL, NULL);
1452 GList *p = NULL;
1453 for (p = attr->params; p; p = p->next) {
1454 VFormatParam *param = p->data;
1455 if (!g_ascii_strcasecmp (param->name, name))
1456 return param;
1458 return NULL;
1461 void
1462 vformat_attribute_set_value (VFormatAttribute *attr,
1463 int nth, const char *value)
1465 GList *param = g_list_nth(attr->values, nth);
1466 g_free(param->data);
1467 param->data = g_strdup(value);
1470 void
1471 vformat_attribute_param_add_value (VFormatParam *param,
1472 const char *value)
1474 g_return_if_fail (param != NULL);
1476 param->values = g_list_append (param->values, g_strdup (value));
1479 void
1480 vformat_attribute_param_add_values (VFormatParam *param,
1481 ...)
1483 va_list ap;
1484 char *v;
1486 g_return_if_fail (param != NULL);
1488 va_start (ap, param);
1490 while ((v = va_arg (ap, char*))) {
1491 vformat_attribute_param_add_value (param, v);
1494 va_end (ap);
1497 void
1498 vformat_attribute_add_param_with_value (VFormatAttribute *attr, const char *name, const char *value)
1500 g_return_if_fail (attr != NULL);
1501 g_return_if_fail (name != NULL);
1503 if (!value)
1504 return;
1506 VFormatParam *param = vformat_attribute_param_new(name);
1508 vformat_attribute_param_add_value (param, value);
1510 vformat_attribute_add_param (attr, param);
1513 void
1514 vformat_attribute_add_param_with_values (VFormatAttribute *attr,
1515 VFormatParam *param, ...)
1517 va_list ap;
1518 char *v;
1520 g_return_if_fail (attr != NULL);
1521 g_return_if_fail (param != NULL);
1523 va_start (ap, param);
1525 while ((v = va_arg (ap, char*))) {
1526 vformat_attribute_param_add_value (param, v);
1529 va_end (ap);
1531 vformat_attribute_add_param (attr, param);
1534 void
1535 vformat_attribute_param_remove_values (VFormatParam *param)
1537 g_return_if_fail (param != NULL);
1539 g_list_foreach (param->values, (GFunc)g_free, NULL);
1540 g_list_free (param->values);
1541 param->values = NULL;
1544 GList*
1545 vformat_get_attributes (VFormat *format)
1547 return format->attributes;
1550 const char*
1551 vformat_attribute_get_group (VFormatAttribute *attr)
1553 g_return_val_if_fail (attr != NULL, NULL);
1555 return attr->group;
1558 const char*
1559 vformat_attribute_get_name (VFormatAttribute *attr)
1561 g_return_val_if_fail (attr != NULL, NULL);
1563 return attr->name;
1566 GList*
1567 vformat_attribute_get_values (VFormatAttribute *attr)
1569 g_return_val_if_fail (attr != NULL, NULL);
1571 return attr->values;
1574 GList*
1575 vformat_attribute_get_values_decoded (VFormatAttribute *attr)
1577 g_return_val_if_fail (attr != NULL, NULL);
1579 if (!attr->decoded_values) {
1580 GList *l;
1581 switch (attr->encoding) {
1582 case VF_ENCODING_RAW:
1583 case VF_ENCODING_8BIT:
1584 for (l = attr->values; l; l = l->next)
1585 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data));
1586 break;
1587 case VF_ENCODING_BASE64:
1588 for (l = attr->values; l; l = l->next) {
1589 char *decoded = g_strdup ((char*)l->data);
1590 int len = base64_decode_simple (decoded, strlen (decoded));
1591 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1592 g_free (decoded);
1594 break;
1595 case VF_ENCODING_QP:
1596 for (l = attr->values; l; l = l->next) {
1597 if (!(l->data))
1598 continue;
1599 char *decoded = g_strdup ((char*)l->data);
1600 int len = quoted_decode_simple (decoded, strlen (decoded));
1601 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
1602 g_free (decoded);
1604 break;
1608 return attr->decoded_values;
1611 gboolean
1612 vformat_attribute_is_single_valued (VFormatAttribute *attr)
1614 g_return_val_if_fail (attr != NULL, FALSE);
1616 if (attr->values == NULL
1617 || attr->values->next != NULL)
1618 return FALSE;
1620 return TRUE;
1623 char*
1624 vformat_attribute_get_value (VFormatAttribute *attr)
1626 GList *values;
1628 g_return_val_if_fail (attr != NULL, NULL);
1630 values = vformat_attribute_get_values (attr);
1632 if (!vformat_attribute_is_single_valued (attr))
1633 osync_trace(TRACE_INTERNAL, "vformat_attribute_get_value called on multivalued attribute");
1635 return values ? g_strdup ((char*)values->data) : NULL;
1638 GString*
1639 vformat_attribute_get_value_decoded (VFormatAttribute *attr)
1641 GList *values;
1642 GString *str = NULL;
1644 g_return_val_if_fail (attr != NULL, NULL);
1646 values = vformat_attribute_get_values_decoded (attr);
1648 if (!vformat_attribute_is_single_valued (attr))
1649 osync_trace(TRACE_INTERNAL, "vformat_attribute_get_value_decoded called on multivalued attribute");
1651 if (values)
1652 str = values->data;
1654 return str ? g_string_new_len (str->str, str->len) : NULL;
1657 const char *vformat_attribute_get_nth_value(VFormatAttribute *attr, int nth)
1659 GList *values = vformat_attribute_get_values_decoded(attr);
1660 if (!values)
1661 return NULL;
1662 GString *retstr = (GString *)g_list_nth_data(values, nth);
1663 if (!retstr)
1664 return NULL;
1666 if (!g_utf8_validate(retstr->str, -1, NULL)) {
1667 values = vformat_attribute_get_values(attr);
1668 if (!values)
1669 return NULL;
1670 return g_list_nth_data(values, nth);
1673 return retstr->str;
1676 gboolean
1677 vformat_attribute_has_type (VFormatAttribute *attr, const char *typestr)
1679 GList *params;
1680 GList *p;
1682 g_return_val_if_fail (attr != NULL, FALSE);
1683 g_return_val_if_fail (typestr != NULL, FALSE);
1685 params = vformat_attribute_get_params (attr);
1687 for (p = params; p; p = p->next) {
1688 VFormatParam *param = p->data;
1690 if (!strcasecmp (vformat_attribute_param_get_name (param), "TYPE")) {
1691 GList *values = vformat_attribute_param_get_values (param);
1692 GList *v;
1694 for (v = values; v; v = v->next) {
1695 if (!strcasecmp ((char*)v->data, typestr))
1696 return TRUE;
1701 return FALSE;
1705 gboolean vformat_attribute_has_param(VFormatAttribute *attr, const char *name)
1707 g_return_val_if_fail (attr != NULL, FALSE);
1708 g_return_val_if_fail (name != NULL, FALSE);
1710 GList *params = vformat_attribute_get_params(attr);
1711 GList *p;
1712 for (p = params; p; p = p->next) {
1713 VFormatParam *param = p->data;
1714 if (!strcasecmp(name, vformat_attribute_param_get_name(param)))
1715 return TRUE;
1717 return FALSE;
1720 GList*
1721 vformat_attribute_get_params (VFormatAttribute *attr)
1723 g_return_val_if_fail (attr != NULL, NULL);
1725 return attr->params;
1728 const char*
1729 vformat_attribute_param_get_name (VFormatParam *param)
1731 g_return_val_if_fail (param != NULL, NULL);
1733 return param->name;
1736 GList*
1737 vformat_attribute_param_get_values (VFormatParam *param)
1739 g_return_val_if_fail (param != NULL, NULL);
1741 return param->values;
1744 const char *vformat_attribute_param_get_nth_value(VFormatParam *param, int nth)
1746 const char *ret = NULL;
1747 GList *values = vformat_attribute_param_get_values(param);
1748 if (!values)
1749 return NULL;
1750 ret = g_list_nth_data(values, nth);
1751 return ret;
1754 static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1756 //static unsigned char _evc_base64_rank[256];
1758 static void base64_init(char *rank)
1760 int i;
1762 memset(rank, 0xff, sizeof(rank));
1763 for (i=0;i<64;i++) {
1764 rank[(unsigned int)base64_alphabet[i]] = i;
1766 rank['='] = 0;
1769 /* call this when finished encoding everything, to
1770 flush off the last little bit */
1771 static size_t base64_encode_close(unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
1773 int c1, c2;
1774 unsigned char *outptr = out;
1776 if (inlen>0)
1777 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
1779 c1 = ((unsigned char *)save)[1];
1780 c2 = ((unsigned char *)save)[2];
1782 switch (((char *)save)[0]) {
1783 case 2:
1784 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
1785 g_assert(outptr[2] != 0);
1786 goto skip;
1787 case 1:
1788 outptr[2] = '=';
1789 skip:
1790 outptr[0] = base64_alphabet[ c1 >> 2 ];
1791 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
1792 outptr[3] = '=';
1793 outptr += 4;
1794 break;
1796 if (break_lines)
1797 *outptr++ = '\n';
1799 *save = 0;
1800 *state = 0;
1802 return outptr-out;
1806 performs an 'encode step', only encodes blocks of 3 characters to the
1807 output at a time, saves left-over state in state and save (initialise to
1808 0 on first invocation).
1810 static size_t base64_encode_step(unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
1812 register unsigned char *inptr, *outptr;
1814 if (len<=0)
1815 return 0;
1817 inptr = in;
1818 outptr = out;
1820 if (len + ((char *)save)[0] > 2) {
1821 unsigned char *inend = in+len-2;
1822 register int c1, c2, c3;
1823 register int already;
1825 already = *state;
1827 switch (((char *)save)[0]) {
1828 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1829 case 2: c1 = ((unsigned char *)save)[1];
1830 c2 = ((unsigned char *)save)[2]; goto skip2;
1833 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1834 while (inptr < inend) {
1835 c1 = *inptr++;
1836 skip1:
1837 c2 = *inptr++;
1838 skip2:
1839 c3 = *inptr++;
1840 *outptr++ = base64_alphabet[ c1 >> 2 ];
1841 *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
1842 *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
1843 *outptr++ = base64_alphabet[ c3 & 0x3f ];
1844 /* this is a bit ugly ... */
1845 if (break_lines && (++already)>=19) {
1846 *outptr++='\n';
1847 already = 0;
1851 ((char *)save)[0] = 0;
1852 len = 2-(inptr-inend);
1853 *state = already;
1856 if (len>0) {
1857 register char *saveout;
1859 /* points to the slot for the next char to save */
1860 saveout = & (((char *)save)[1]) + ((char *)save)[0];
1862 /* len can only be 0 1 or 2 */
1863 switch(len) {
1864 case 2: *saveout++ = *inptr++;
1865 case 1: *saveout++ = *inptr++;
1867 ((char *)save)[0]+=len;
1870 return outptr-out;
1875 * base64_decode_step: decode a chunk of base64 encoded data
1876 * @in: input stream
1877 * @len: max length of data to decode
1878 * @out: output stream
1879 * @state: holds the number of bits that are stored in @save
1880 * @save: leftover bits that have not yet been decoded
1882 * Decodes a chunk of base64 encoded data
1884 static size_t base64_decode_step(unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
1886 unsigned char base64_rank[256];
1887 base64_init((char*)base64_rank);
1889 register unsigned char *inptr, *outptr;
1890 unsigned char *inend, c;
1891 register unsigned int v;
1892 int i;
1894 inend = in+len;
1895 outptr = out;
1897 /* convert 4 base64 bytes to 3 normal bytes */
1898 v=*save;
1899 i=*state;
1900 inptr = in;
1901 while (inptr<inend) {
1902 c = base64_rank[*inptr++];
1903 if (c != 0xff) {
1904 v = (v<<6) | c;
1905 i++;
1906 if (i==4) {
1907 *outptr++ = v>>16;
1908 *outptr++ = v>>8;
1909 *outptr++ = v;
1910 i=0;
1915 *save = v;
1916 *state = i;
1918 /* quick scan back for '=' on the end somewhere */
1919 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
1920 i=2;
1921 while (inptr>in && i) {
1922 inptr--;
1923 if (base64_rank[*inptr] != 0xff) {
1924 if (*inptr == '=' && outptr>out)
1925 outptr--;
1926 i--;
1930 /* if i!= 0 then there is a truncation error! */
1931 return outptr-out;
1934 char *base64_encode_simple (const char *data, size_t len)
1936 unsigned char *out;
1937 int state = 0, outlen;
1938 unsigned int save = 0;
1940 g_return_val_if_fail (data != NULL, NULL);
1942 out = g_malloc (len * 4 / 3 + 5);
1943 outlen = base64_encode_close ((unsigned char *)data, len, FALSE,
1944 out, &state, (int*)&save);
1945 out[outlen] = '\0';
1946 return (char *)out;
1949 size_t base64_decode_simple (char *data, size_t len)
1951 int state = 0;
1952 unsigned int save = 0;
1954 g_return_val_if_fail (data != NULL, 0);
1956 return base64_decode_step ((unsigned char *)data, len,
1957 (unsigned char *)data, &state, &save);
1960 char *quoted_encode_simple(const unsigned char *string, int len)
1962 GString *tmp = g_string_new("");
1964 int i = 0;
1965 while(string[i] != 0) {
1966 if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') {
1967 g_string_append_printf(tmp, "=%02X", string[i]);
1968 } else {
1969 g_string_append_c(tmp, string[i]);
1971 i++;
1974 char *ret = tmp->str;
1975 g_string_free(tmp, FALSE);
1976 return ret;
1980 size_t quoted_decode_simple (char *data, size_t len)
1982 g_return_val_if_fail (data != NULL, 0);
1984 GString *string = g_string_new(data);
1985 if (!string)
1986 return 0;
1988 char hex[5];
1989 hex[4] = 0;
1991 while (1) {
1992 //Get the index of the next encoded char
1993 int i = strcspn(string->str, "=");
1994 if (i >= strlen(string->str))
1995 break;
1997 strcpy(hex, "0x");
1998 strncat(hex, &string->str[i + 1], 2);
1999 char rep = ((int)(strtod(hex, NULL)));
2000 g_string_erase(string, i, 2);
2001 g_string_insert_c(string, i, rep);
2004 memset(data, 0, strlen(data));
2005 strcpy(data, string->str);
2006 g_string_free(string, 1);
2008 return strlen(data);