2 * Copyright (C) 2003 Ximian, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Author: Chris Toshok (toshok@ximian.com)
19 * Author: Armin Bauer (armin.bauer@opensync.org)
34 #include <opensync/opensync.h>
36 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
);
37 static size_t base64_decode_step(const 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
);
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") )
58 time_t b_vformat_time_to_unix(const char *inptime
)
63 if ((ftime
= g_strrstr(inptime
, "T"))) {
65 date
= g_strndup(inptime
, ftime
- inptime
);
67 time
= g_strndup(ftime
+ 1, 8);
69 time
= g_strndup(ftime
+ 1, 6);
71 date
= g_strdup(inptime
);
75 memset(&btime
, 0, sizeof(struct tm
));
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;
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) {
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
);
103 static char *_fold_lines (char *buf
)
105 GString
*str
= g_string_new ("");
106 GString
*line
= g_string_new ("");
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.
120 /* search new lines for quoted printable encoding */
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 ("");
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
);
143 str
= g_string_append (str
, CRLF
);
144 p
= g_utf8_next_char (next
);
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
);
157 str
= g_string_append (str
, CRLF
);
158 p
= g_utf8_next_char (p
);
160 quotedprintable
= FALSE
;
164 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
165 p
= g_utf8_next_char (p
);
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
)
181 while (*lp
!= '\r' && *lp
!= '\0')
182 lp
= g_utf8_next_char (lp
);
185 lp
= g_utf8_next_char (lp
); /* \n */
186 lp
= g_utf8_next_char (lp
); /* start of the next line */
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
)
200 while (*lp
!= '\r' && *lp
!= '\0') {
201 gboolean s_matches
= FALSE
;
203 for (ls
= s
; *ls
; ls
= g_utf8_next_char (ls
)) {
204 if (g_utf8_get_char (ls
) == g_utf8_get_char (lp
)) {
218 static void _read_attribute_value_add (b_VFormatAttribute
*attr
, GString
*str
, GString
*charset
)
220 /* don't convert empty strings */
222 b_vformat_attribute_add_value(attr
, str
->str
);
226 char *inbuf
, *outbuf
, *p
;
227 size_t inbytesleft
, outbytesleft
;
230 p
= outbuf
= malloc(str
->len
*2);
231 inbytesleft
= str
->len
;
232 outbytesleft
= str
->len
*2;
236 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
239 cd
= iconv_open("UTF-8", charset
->str
);
241 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
243 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
246 b_vformat_attribute_add_value(attr
, outbuf
);
250 /* hmm, should not happen */
251 b_vformat_attribute_add_value(attr
, str
->str
);
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
);
266 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
267 cd
= iconv_open("UTF-8", "ISO-8859-1");
269 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
271 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
274 b_vformat_attribute_add_value (attr
, outbuf
);
278 b_vformat_attribute_add_value (attr
, str
->str
);
292 static void _read_attribute_value (b_VFormatAttribute
*attr
, char **p
, int format_encoding
, GString
*charset
)
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;
308 /* e.g. ...N=C3=BCrnberg\r\n
321 if (*(++tmplp
) == '\r' && *(++tmplp
) == '\n' && isalnum(*(++tmplp
))) {
328 /* append malformed input, and
330 str
= g_string_append_c(str
, a
);
331 str
= g_string_append_c(str
, b
);
340 if (b
== '\r' && c
== '\n' && isalnum(d
) && isalnum(e
)) {
346 /* append malformed input, and
348 str
= g_string_append_c(str
, a
);
349 str
= g_string_append_c(str
, b
);
353 /* append malformed input, and
355 str
= g_string_append_c(str
, a
);
356 str
= g_string_append_c(str
, b
);
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
);
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
380 lp
= g_utf8_next_char(lp
);
382 str
= g_string_append_c (str
, '\\');
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;
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
, "");
396 str
= g_string_append_c (str
, ',');
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;
403 osync_trace(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp
);
404 str
= g_string_append_c (str
, '\\');
405 str
= g_string_append_unichar (str
, g_utf8_get_char(lp
));
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
);
417 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
418 lp
= g_utf8_next_char(lp
);
422 _read_attribute_value_add (attr
, str
, charset
);
423 g_string_free (str
, TRUE
);
427 lp
= g_utf8_next_char (lp
); /* \n */
428 lp
= g_utf8_next_char (lp
); /* start of the next line */
434 static void _read_attribute_params(b_VFormatAttribute
*attr
, char **p
, int *format_encoding
, GString
**charset
)
438 b_VFormatParam
*param
= NULL
;
439 gboolean in_quote
= FALSE
;
440 str
= g_string_new ("");
442 while (*lp
!= '\0') {
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
== '=') {
459 param
= b_vformat_attribute_param_new (str
->str
);
460 g_string_assign (str
, "");
461 lp
= g_utf8_next_char (lp
);
464 _skip_until (&lp
, ":;");
466 lp
= g_utf8_next_char (lp
); /* \n */
467 lp
= g_utf8_next_char (lp
); /* start of the next line */
471 lp
= g_utf8_next_char (lp
);
474 else if (*lp
== ';' || *lp
== ':' || *lp
== ',') {
475 gboolean colon
= (*lp
== ':');
476 gboolean comma
= (*lp
== ',');
480 b_vformat_attribute_param_add_value (param
, str
->str
);
481 g_string_assign (str
, "");
483 lp
= g_utf8_next_char (lp
);
486 /* we've got a parameter of the form:
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
);
498 lp
= g_utf8_next_char (lp
);
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
);
508 } else if ( _helper_is_base64(param
->values
->data
)) {
509 *format_encoding
= VF_ENCODING_BASE64
;
510 // b_vformat_attribute_param_free (param);
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
);
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
,
531 param_name
= "ENCODING";
532 g_string_assign (str
, "b");
533 *format_encoding
= VF_ENCODING_BASE64
;
540 param
= b_vformat_attribute_param_new (param_name
);
541 b_vformat_attribute_param_add_value (param
, str
->str
);
543 g_string_assign (str
, "");
545 lp
= g_utf8_next_char (lp
);
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;]*:
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 ':' */
561 lp
= g_utf8_next_char (lp
);
564 if (param
&& !comma
) {
565 b_vformat_attribute_add_param (attr
, param
);
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
, ":;");
579 g_string_free (str
, TRUE
);
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
;
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
== ';') {
601 /* we've got a name, break out to the value/attribute parsing */
602 attr_name
= g_string_free (str
, FALSE
);
606 /* a line of the form:
609 * since we don't have an attribute
610 * name, skip to the end of the line
613 g_string_free (str
, TRUE
);
615 _skip_to_next_line(p
);
619 else if (*lp
== '.') {
621 osync_trace(TRACE_INTERNAL
, "extra `.' in attribute specification. ignoring extra group `%s'",
623 g_string_free (str
, TRUE
);
624 str
= g_string_new ("");
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
));
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
);
638 _skip_to_next_line(p
);
642 lp
= g_utf8_next_char(lp
);
646 _skip_to_next_line (p
);
650 attr
= b_vformat_attribute_new (attr_group
, attr_name
);
655 /* skip past the ';' */
656 lp
= g_utf8_next_char(lp
);
657 _read_attribute_params (attr
, &lp
, &is_qp
, &charset
);
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
);
674 b_vformat_attribute_free (attr
);
678 static void open_block(char **block
, const char *block_name
)
680 char *start
= *block
? *block
: "";
683 result
= g_strconcat(start
, "/", block_name
, NULL
);
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 )
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,
705 // cut off the end of the string... no need to free/realloc
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
);
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.");
727 buf
= _fold_lines (buf
);
731 attr
= _read_attribute (&p
);
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
);
741 b_vformat_add_attribute (evc
, attr
);
745 b_VFormatAttribute
*next_attr
= _read_attribute (&p
);
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);
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);
763 // apply the block to the attr
764 next_attr
->block
= g_strdup(block
);
767 b_vformat_add_attribute (evc
, next_attr
);
772 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "end")) {
773 osync_trace(TRACE_INTERNAL
, "vformat ended without END");
780 char *b_vformat_escape_string (const char *s
, b_VFormatType type
)
785 str
= g_string_new ("");
787 /* Escape a string as described in RFC2426, section 5 */
788 for (p
= s
; p
&& *p
; p
++) {
791 str
= g_string_append (str
, "\\n");
796 str
= g_string_append (str
, "\\n");
799 str
= g_string_append (str
, "\\;");
802 if (type
== VFORMAT_CARD_30
|| type
== VFORMAT_EVENT_20
|| type
== VFORMAT_TODO_20
)
803 str
= g_string_append (str
, "\\,");
805 str
= g_string_append_c (str
, *p
);
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
);
818 osync_trace(TRACE_INTERNAL
, "[%s] escape backslashes!!", __func__
);
819 str
= g_string_append (str
, "\\\\");
823 str
= g_string_append_c (str
, *p
);
828 return g_string_free (str
, FALSE
);
832 b_vformat_unescape_string (const char *s
)
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
++) {
846 str
= g_string_append_c (str
, '\\');
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;
859 osync_trace(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p
);
860 str
= g_string_append_c (str
, '\\');
861 str
= g_string_append_unichar (str
, g_utf8_get_char(p
));
867 return g_string_free (str
, FALSE
);
871 b_vformat_construct (b_VFormat
*evc
, const char *str
)
873 g_return_if_fail (str
!= NULL
);
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
);
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
);
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;
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
);
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
) ) {
949 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
953 GList *attributes = last ? last->next : 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)) {
968 char *b_vformat_to_string (b_VFormat
*evc
, b_VFormatType type
)
970 osync_trace(TRACE_ENTRY
, "%s(%p, %i)", __func__
, evc
, type
);
974 GString
*str
= g_string_new ("");
977 case VFORMAT_CARD_21
:
978 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
980 case VFORMAT_CARD_30
:
981 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
983 case VFORMAT_TODO_10
:
984 case VFORMAT_EVENT_10
:
985 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
987 case VFORMAT_TODO_20
:
988 case VFORMAT_EVENT_20
:
989 case VFORMAT_JOURNAL
:
990 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
993 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
997 for (l
= evc
->attributes
; l
; l
= l
->next
) {
999 b_VFormatAttribute
*attr
= l
->data
;
1002 int format_encoding
= VF_ENCODING_RAW
;
1004 attr_str
= g_string_new ("");
1006 /* From rfc2425, 5.8.2
1008 * contentline = [group "."] name *(";" param) ":" value CRLF
1012 attr_str
= g_string_append (attr_str
, attr
->group
);
1013 attr_str
= g_string_append_c (attr_str
, '.');
1015 attr_str
= g_string_append (attr_str
, attr
->name
);
1016 /* handle the parameters */
1017 for (p
= attr
->params
; p
; p
= p
->next
) {
1018 b_VFormatParam
*param
= p
->data
;
1020 * param = param-name "=" param-value *("," param-value)
1022 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
1023 || type
== VFORMAT_EVENT_20
|| type
== VFORMAT_JOURNAL
) {
1026 * Character set can only be specified on the CHARSET
1027 * parameter on the Content-Type MIME header field.
1029 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
1031 attr_str
= g_string_append_c (attr_str
, ';');
1032 attr_str
= g_string_append (attr_str
, param
->name
);
1033 if (param
->values
) {
1034 attr_str
= g_string_append_c (attr_str
, '=');
1036 for (v
= param
->values
; v
; v
= v
->next
) {
1037 if (_helper_is_base64((const char *) v
->data
)) {
1038 format_encoding
= VF_ENCODING_BASE64
;
1039 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1040 v
->data
=g_strdup("B");
1043 * QUOTED-PRINTABLE inline encoding has been
1046 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
1047 osync_trace(TRACE_ERROR
, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__
);
1048 format_encoding
= VF_ENCODING_QP
;
1050 attr_str
= g_string_append (attr_str
, v
->data
);
1053 attr_str
= g_string_append_c (attr_str
, ',');
1057 attr_str
= g_string_append_c (attr_str
, ';');
1059 * The "TYPE=" is optional skip it.
1060 * LOGO, PHOTO and SOUND multimedia formats MUST
1061 * have a "TYPE=" parameter
1063 gboolean must_have_type
= FALSE
;
1064 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
1065 must_have_type
= TRUE
;
1066 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
1067 attr_str
= g_string_append (attr_str
, param
->name
);
1068 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
1069 attr_str
= g_string_append_c (attr_str
, '=');
1070 for (v
= param
->values
; v
; v
= v
->next
) {
1071 // check for quoted-printable encoding
1072 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
1073 format_encoding
= VF_ENCODING_QP
;
1074 // check for base64 encoding
1075 if (_helper_is_base64((const char *) v
->data
)) {
1076 format_encoding
= VF_ENCODING_BASE64
;
1077 v
->data
=g_strdup("BASE64");
1079 attr_str
= g_string_append (attr_str
, v
->data
);
1081 attr_str
= g_string_append_c (attr_str
, ',');
1086 attr_str
= g_string_append_c (attr_str
, ':');
1088 for (v
= attr
->values
; v
; v
= v
->next
) {
1089 char *value
= v
->data
;
1090 char *escaped_value
= NULL
;
1092 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
1093 strstr (value
, "BYDAY") == v
->data
) {
1094 attr_str
= g_string_append (attr_str
, value
);
1096 escaped_value
= b_vformat_escape_string (value
, type
);
1097 attr_str
= g_string_append (attr_str
, escaped_value
);
1102 /* XXX toshok - i hate you, rfc 2426.
1103 why doesn't CATEGORIES use a ; like
1104 a normal list attribute? */
1105 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1106 attr_str
= g_string_append_c (attr_str
, ',');
1108 attr_str
= g_string_append_c (attr_str
, ';');
1111 g_free (escaped_value
);
1117 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1118 * After generating a content line,
1119 * lines longer than 75 characters SHOULD be folded according to the
1120 * folding procedure described in [MIME-DIR].
1122 * rfc 2445 (iCalendar), 4.1 Content Lines:
1123 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1124 * break. Long content lines SHOULD be split into a multiple line
1125 * representations using a line "folding" technique. That is, a long
1126 * line can be split between any two characters by inserting a CRLF
1127 * immediately followed by a single linear white space character (i.e.,
1128 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1129 * of CRLF followed immediately by a single linear white space character
1130 * is ignored (i.e., removed) when processing the content type.
1132 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1133 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1135 * Differences between encodings:
1136 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1138 * rfc 2425 [MIME-DIR], 5.8.1:
1139 * A logical line MAY be continued on the next physical line anywhere
1140 * between two characters by inserting a CRLF immediately followed by a
1141 * single <WS> (white space) character.
1143 * rfc 2045, 6.7, chapter 5:
1144 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1145 * without follwing <WS>
1150 * Note that all the line folding above is described in terms of characters
1151 * not bytes. In particular, it would be an error to put a line break
1152 * within a UTF-8 character.
1157 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1160 /* If using QP, must be sure that we do not fold within a quote sequence */
1161 if (format_encoding
== VF_ENCODING_QP
) {
1162 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1163 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1166 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1168 if (format_encoding
== VF_ENCODING_QP
)
1169 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1171 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1175 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1177 attr_str
= g_string_append (attr_str
, CRLF
);
1179 * base64= <MIME RFC 1521 base64 text>
1180 * the end of the text is marked with two CRLF sequences
1181 * this results in one blank line before the start of the
1184 if( format_encoding
== VF_ENCODING_BASE64
1185 && (type
== VFORMAT_CARD_21
))
1186 attr_str
= g_string_append (attr_str
, CRLF
);
1188 str
= g_string_append (str
, attr_str
->str
);
1189 g_string_free (attr_str
, TRUE
);
1193 case VFORMAT_CARD_21
:
1194 str
= g_string_append (str
, "END:VCARD\r\n");
1196 case VFORMAT_CARD_30
:
1197 str
= g_string_append (str
, "END:VCARD\r\n");
1199 case VFORMAT_TODO_10
:
1200 case VFORMAT_EVENT_10
:
1201 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1203 case VFORMAT_TODO_20
:
1204 case VFORMAT_EVENT_20
:
1205 case VFORMAT_JOURNAL
:
1206 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1209 str
= g_string_append (str
, "END:VNOTE\r\n");
1213 osync_trace(TRACE_EXIT
, "%s", __func__
);
1214 return g_string_free (str
, FALSE
);
1217 void b_vformat_dump_structure (b_VFormat
*evc
)
1223 printf ("b_VFormat\n");
1224 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1226 b_VFormatAttribute
*attr
= a
->data
;
1227 printf ("+-- %s\n", attr
->name
);
1229 printf (" +- params=\n");
1231 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1232 b_VFormatParam
*param
= p
->data
;
1233 printf (" | [%d] = %s", i
,param
->name
);
1235 for (v
= param
->values
; v
; v
= v
->next
) {
1236 char *value
= b_vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1237 printf ("%s", value
);
1246 printf (" +- values=\n");
1247 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1248 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1253 b_VFormatAttribute
*b_vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1255 b_VFormatAttribute
*attr
;
1257 attr
= g_new0 (b_VFormatAttribute
, 1);
1259 attr
->group
= g_strdup (attr_group
);
1260 attr
->name
= g_strdup (attr_name
);
1266 b_vformat_attribute_free (b_VFormatAttribute
*attr
)
1268 g_return_if_fail (attr
!= NULL
);
1270 g_free (attr
->block
);
1271 g_free (attr
->group
);
1272 g_free (attr
->name
);
1274 b_vformat_attribute_remove_values (attr
);
1276 b_vformat_attribute_remove_params (attr
);
1282 b_vformat_attribute_copy (b_VFormatAttribute
*attr
)
1284 b_VFormatAttribute
*a
;
1287 g_return_val_if_fail (attr
!= NULL
, NULL
);
1289 a
= b_vformat_attribute_new (b_vformat_attribute_get_group (attr
),
1290 b_vformat_attribute_get_name (attr
));
1292 for (p
= attr
->values
; p
; p
= p
->next
)
1293 b_vformat_attribute_add_value (a
, p
->data
);
1295 for (p
= attr
->params
; p
; p
= p
->next
)
1296 b_vformat_attribute_add_param (a
, b_vformat_attribute_param_copy (p
->data
));
1302 b_vformat_remove_attributes (b_VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1306 g_return_if_fail (attr_name
!= NULL
);
1308 attr
= evc
->attributes
;
1311 b_VFormatAttribute
*a
= attr
->data
;
1313 next_attr
= attr
->next
;
1315 if (((!attr_group
&& !a
->group
) ||
1316 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1317 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1319 /* matches, remove/delete the attribute */
1320 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1322 b_vformat_attribute_free (a
);
1330 b_vformat_remove_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1332 g_return_if_fail (attr
!= NULL
);
1334 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1335 b_vformat_attribute_free (attr
);
1339 b_vformat_add_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1341 g_return_if_fail (attr
!= NULL
);
1343 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1347 b_vformat_add_attribute_with_value (b_VFormat
*b_VFormat
,
1348 b_VFormatAttribute
*attr
, const char *value
)
1350 g_return_if_fail (attr
!= NULL
);
1352 b_vformat_attribute_add_value (attr
, value
);
1354 b_vformat_add_attribute (b_VFormat
, attr
);
1358 b_vformat_add_attribute_with_values (b_VFormat
*b_VFormat
, b_VFormatAttribute
*attr
, ...)
1363 g_return_if_fail (attr
!= NULL
);
1365 va_start (ap
, attr
);
1367 while ((v
= va_arg (ap
, char*))) {
1368 b_vformat_attribute_add_value (attr
, v
);
1373 b_vformat_add_attribute (b_VFormat
, attr
);
1377 b_vformat_attribute_add_value (b_VFormatAttribute
*attr
, const char *value
)
1379 g_return_if_fail (attr
!= NULL
);
1381 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1385 b_vformat_attribute_add_value_decoded (b_VFormatAttribute
*attr
, const char *value
, int len
)
1387 g_return_if_fail (attr
!= NULL
);
1389 switch (attr
->encoding
) {
1390 case VF_ENCODING_RAW
:
1391 osync_trace(TRACE_INTERNAL
, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1393 case VF_ENCODING_BASE64
: {
1394 char *b64_data
= base64_encode_simple (value
, len
);
1395 GString
*decoded
= g_string_new_len (value
, len
);
1397 /* make sure the decoded list is up to date */
1398 b_vformat_attribute_get_values_decoded (attr
);
1400 attr
->values
= g_list_append (attr
->values
, b64_data
);
1401 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1404 case VF_ENCODING_QP
: {
1405 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1406 GString
*decoded
= g_string_new (value
);
1408 /* make sure the decoded list is up to date */
1409 b_vformat_attribute_get_values_decoded (attr
);
1411 attr
->values
= g_list_append (attr
->values
, qp_data
);
1412 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1415 case VF_ENCODING_8BIT
: {
1416 char *data
= g_strdup(value
);
1417 GString
*decoded
= g_string_new (value
);
1419 /* make sure the decoded list is up to date */
1420 b_vformat_attribute_get_values_decoded (attr
);
1422 attr
->values
= g_list_append (attr
->values
, data
);
1423 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1430 b_vformat_attribute_add_values (b_VFormatAttribute
*attr
, ...)
1435 g_return_if_fail (attr
!= NULL
);
1437 va_start (ap
, attr
);
1439 while ((v
= va_arg (ap
, char*))) {
1440 b_vformat_attribute_add_value (attr
, v
);
1447 free_gstring (GString
*str
)
1449 g_string_free (str
, TRUE
);
1453 b_vformat_attribute_remove_values (b_VFormatAttribute
*attr
)
1455 g_return_if_fail (attr
!= NULL
);
1457 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1458 g_list_free (attr
->values
);
1459 attr
->values
= NULL
;
1461 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1462 g_list_free (attr
->decoded_values
);
1463 attr
->decoded_values
= NULL
;
1467 b_vformat_attribute_remove_params (b_VFormatAttribute
*attr
)
1469 g_return_if_fail (attr
!= NULL
);
1471 g_list_foreach (attr
->params
, (GFunc
)b_vformat_attribute_param_free
, NULL
);
1472 g_list_free (attr
->params
);
1473 attr
->params
= NULL
;
1475 /* also remove the cached encoding on this attribute */
1476 attr
->encoding_set
= FALSE
;
1477 attr
->encoding
= VF_ENCODING_RAW
;
1481 b_vformat_attribute_param_new (const char *name
)
1483 b_VFormatParam
*param
= g_new0 (b_VFormatParam
, 1);
1484 param
->name
= g_strdup (name
);
1490 b_vformat_attribute_param_free (b_VFormatParam
*param
)
1492 g_return_if_fail (param
!= NULL
);
1494 g_free (param
->name
);
1496 b_vformat_attribute_param_remove_values (param
);
1502 b_vformat_attribute_param_copy (b_VFormatParam
*param
)
1507 g_return_val_if_fail (param
!= NULL
, NULL
);
1509 p
= b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param
));
1511 for (l
= param
->values
; l
; l
= l
->next
) {
1512 b_vformat_attribute_param_add_value (p
, l
->data
);
1519 b_vformat_attribute_add_param (b_VFormatAttribute
*attr
,
1520 b_VFormatParam
*param
)
1522 g_return_if_fail (attr
!= NULL
);
1523 g_return_if_fail (param
!= NULL
);
1525 attr
->params
= g_list_append (attr
->params
, param
);
1527 /* we handle our special encoding stuff here */
1529 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1530 if (attr
->encoding_set
) {
1531 osync_trace(TRACE_INTERNAL
, "ENCODING specified twice");
1535 if (param
->values
&& param
->values
->data
) {
1536 if (_helper_is_base64((const char*)param
->values
->data
))
1537 attr
->encoding
= VF_ENCODING_BASE64
;
1538 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1539 attr
->encoding
= VF_ENCODING_QP
;
1540 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1541 attr
->encoding
= VF_ENCODING_8BIT
;
1543 osync_trace(TRACE_INTERNAL
, "Unknown value `%s' for ENCODING parameter. values will be treated as raw",
1544 (char*)param
->values
->data
);
1547 attr
->encoding_set
= TRUE
;
1550 osync_trace(TRACE_INTERNAL
, "ENCODING parameter added with no value");
1555 b_VFormatParam
*b_vformat_attribute_find_param(b_VFormatAttribute
*attr
, const char *name
, int level
)
1557 g_return_val_if_fail (attr
!= NULL
, NULL
);
1559 for (p
= attr
->params
; p
; p
= p
->next
) {
1560 b_VFormatParam
*param
= p
->data
;
1561 if (!g_ascii_strcasecmp (param
->name
, name
)) {
1572 b_vformat_attribute_set_value (b_VFormatAttribute
*attr
,
1573 int nth
, const char *value
)
1575 GList
*param
= g_list_nth(attr
->values
, nth
);
1576 g_free(param
->data
);
1577 param
->data
= g_strdup(value
);
1581 b_vformat_attribute_param_add_value (b_VFormatParam
*param
,
1584 g_return_if_fail (param
!= NULL
);
1586 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1590 b_vformat_attribute_param_add_values (b_VFormatParam
*param
,
1596 g_return_if_fail (param
!= NULL
);
1598 va_start (ap
, param
);
1600 while ((v
= va_arg (ap
, char*))) {
1601 b_vformat_attribute_param_add_value (param
, v
);
1608 b_vformat_attribute_add_param_with_value (b_VFormatAttribute
*attr
, const char *name
, const char *value
)
1610 g_return_if_fail (attr
!= NULL
);
1611 g_return_if_fail (name
!= NULL
);
1616 b_VFormatParam
*param
= b_vformat_attribute_param_new(name
);
1618 b_vformat_attribute_param_add_value (param
, value
);
1620 b_vformat_attribute_add_param (attr
, param
);
1624 b_vformat_attribute_add_param_with_values (b_VFormatAttribute
*attr
,
1625 b_VFormatParam
*param
, ...)
1630 g_return_if_fail (attr
!= NULL
);
1631 g_return_if_fail (param
!= NULL
);
1633 va_start (ap
, param
);
1635 while ((v
= va_arg (ap
, char*))) {
1636 b_vformat_attribute_param_add_value (param
, v
);
1641 b_vformat_attribute_add_param (attr
, param
);
1645 b_vformat_attribute_param_remove_values (b_VFormatParam
*param
)
1647 g_return_if_fail (param
!= NULL
);
1649 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1650 g_list_free (param
->values
);
1651 param
->values
= NULL
;
1655 b_vformat_get_attributes (b_VFormat
*format
)
1657 return format
->attributes
;
1661 b_vformat_attribute_get_group (b_VFormatAttribute
*attr
)
1663 g_return_val_if_fail (attr
!= NULL
, NULL
);
1669 b_vformat_attribute_get_name (b_VFormatAttribute
*attr
)
1671 g_return_val_if_fail (attr
!= NULL
, NULL
);
1677 b_vformat_attribute_get_block (b_VFormatAttribute
*attr
)
1679 g_return_val_if_fail (attr
!= NULL
, NULL
);
1685 b_vformat_attribute_get_values (b_VFormatAttribute
*attr
)
1687 g_return_val_if_fail (attr
!= NULL
, NULL
);
1689 return attr
->values
;
1693 b_vformat_attribute_get_values_decoded (b_VFormatAttribute
*attr
)
1695 g_return_val_if_fail (attr
!= NULL
, NULL
);
1697 if (!attr
->decoded_values
) {
1699 switch (attr
->encoding
) {
1700 case VF_ENCODING_RAW
:
1701 case VF_ENCODING_8BIT
:
1702 for (l
= attr
->values
; l
; l
= l
->next
)
1703 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1705 case VF_ENCODING_BASE64
:
1706 for (l
= attr
->values
; l
; l
= l
->next
) {
1707 char *decoded
= g_strdup ((char*)l
->data
);
1708 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1709 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1713 case VF_ENCODING_QP
:
1714 for (l
= attr
->values
; l
; l
= l
->next
) {
1717 char *decoded
= g_strdup ((char*)l
->data
);
1718 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1719 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1726 return attr
->decoded_values
;
1730 b_vformat_attribute_is_single_valued (b_VFormatAttribute
*attr
)
1732 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1734 if (attr
->values
== NULL
1735 || attr
->values
->next
!= NULL
)
1742 b_vformat_attribute_get_value (b_VFormatAttribute
*attr
)
1746 g_return_val_if_fail (attr
!= NULL
, NULL
);
1748 values
= b_vformat_attribute_get_values (attr
);
1750 if (!b_vformat_attribute_is_single_valued (attr
))
1751 osync_trace(TRACE_INTERNAL
, "b_vformat_attribute_get_value called on multivalued attribute");
1753 return values
? g_strdup ((char*)values
->data
) : NULL
;
1757 b_vformat_attribute_get_value_decoded (b_VFormatAttribute
*attr
)
1760 GString
*str
= NULL
;
1762 g_return_val_if_fail (attr
!= NULL
, NULL
);
1764 values
= b_vformat_attribute_get_values_decoded (attr
);
1766 if (!b_vformat_attribute_is_single_valued (attr
))
1767 osync_trace(TRACE_INTERNAL
, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1772 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1775 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute
*attr
, int nth
)
1777 GList
*values
= b_vformat_attribute_get_values_decoded(attr
);
1780 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1784 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1785 values
= b_vformat_attribute_get_values(attr
);
1788 return g_list_nth_data(values
, nth
);
1795 b_vformat_attribute_has_type (b_VFormatAttribute
*attr
, const char *typestr
)
1800 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1801 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1803 params
= b_vformat_attribute_get_params (attr
);
1805 for (p
= params
; p
; p
= p
->next
) {
1806 b_VFormatParam
*param
= p
->data
;
1808 if (!strcasecmp (b_vformat_attribute_param_get_name (param
), "TYPE")) {
1809 GList
*values
= b_vformat_attribute_param_get_values (param
);
1812 for (v
= values
; v
; v
= v
->next
) {
1813 if (!strcasecmp ((char*)v
->data
, typestr
))
1823 gboolean
b_vformat_attribute_has_param(b_VFormatAttribute
*attr
, const char *name
)
1825 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1826 g_return_val_if_fail (name
!= NULL
, FALSE
);
1828 GList
*params
= b_vformat_attribute_get_params(attr
);
1830 for (p
= params
; p
; p
= p
->next
) {
1831 b_VFormatParam
*param
= p
->data
;
1832 if (!strcasecmp(name
, b_vformat_attribute_param_get_name(param
)))
1839 b_vformat_attribute_get_params (b_VFormatAttribute
*attr
)
1841 g_return_val_if_fail (attr
!= NULL
, NULL
);
1843 return attr
->params
;
1847 b_vformat_attribute_param_get_name (b_VFormatParam
*param
)
1849 g_return_val_if_fail (param
!= NULL
, NULL
);
1855 b_vformat_attribute_param_get_values (b_VFormatParam
*param
)
1857 g_return_val_if_fail (param
!= NULL
, NULL
);
1859 return param
->values
;
1862 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam
*param
, int nth
)
1864 const char *ret
= NULL
;
1865 GList
*values
= b_vformat_attribute_param_get_values(param
);
1868 ret
= g_list_nth_data(values
, nth
);
1872 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1874 //static unsigned char _evc_base64_rank[256];
1876 static void base64_init(char *rank
)
1880 memset(rank
, 0xff, sizeof(rank
));
1881 for (i
=0;i
<64;i
++) {
1882 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1887 /* call this when finished encoding everything, to
1888 flush off the last little bit */
1889 static size_t base64_encode_close(const unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1892 unsigned char *outptr
= out
;
1895 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1897 c1
= ((unsigned char *)save
)[1];
1898 c2
= ((unsigned char *)save
)[2];
1900 switch (((char *)save
)[0]) {
1902 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1903 g_assert(outptr
[2] != 0);
1908 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1909 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1924 performs an 'encode step', only encodes blocks of 3 characters to the
1925 output at a time, saves left-over state in state and save (initialise to
1926 0 on first invocation).
1928 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1930 register const unsigned char *inptr
;
1931 register unsigned char *outptr
;
1939 if (len
+ ((char *)save
)[0] > 2) {
1940 const unsigned char *inend
= in
+len
-2;
1941 register int c1
, c2
, c3
;
1942 register int already
;
1946 switch (((char *)save
)[0]) {
1947 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1948 case 2: c1
= ((unsigned char *)save
)[1];
1949 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1952 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1953 while (inptr
< inend
) {
1959 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1960 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1961 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1962 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1963 /* this is a bit ugly ... */
1964 if (break_lines
&& (++already
)>=19) {
1970 ((char *)save
)[0] = 0;
1971 len
= 2-(inptr
-inend
);
1976 register char *saveout
;
1978 /* points to the slot for the next char to save */
1979 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1981 /* len can only be 0 1 or 2 */
1983 case 2: *saveout
++ = *inptr
++;
1984 case 1: *saveout
++ = *inptr
++;
1986 ((char *)save
)[0]+=len
;
1994 * base64_decode_step: decode a chunk of base64 encoded data
1996 * @len: max length of data to decode
1997 * @out: output stream
1998 * @state: holds the number of bits that are stored in @save
1999 * @save: leftover bits that have not yet been decoded
2001 * Decodes a chunk of base64 encoded data
2003 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
2005 unsigned char base64_rank
[256];
2006 base64_init((char*)base64_rank
);
2008 register const unsigned char *inptr
;
2009 register unsigned char *outptr
;
2010 const unsigned char *inend
;
2012 register unsigned int v
;
2018 /* convert 4 base64 bytes to 3 normal bytes */
2022 while (inptr
<inend
) {
2023 c
= base64_rank
[*inptr
++];
2039 /* quick scan back for '=' on the end somewhere */
2040 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2042 while (inptr
>in
&& i
) {
2044 if (base64_rank
[*inptr
] != 0xff) {
2045 if (*inptr
== '=' && outptr
>out
)
2051 /* if i!= 0 then there is a truncation error! */
2055 static char *base64_encode_simple (const char *data
, size_t len
)
2058 int state
= 0, outlen
;
2059 unsigned int save
= 0;
2061 g_return_val_if_fail (data
!= NULL
, NULL
);
2063 out
= g_malloc (len
* 4 / 3 + 5);
2064 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
2065 out
, &state
, (int*)&save
);
2070 static size_t base64_decode_simple (char *data
, size_t len
)
2073 unsigned int save
= 0;
2075 g_return_val_if_fail (data
!= NULL
, 0);
2077 return base64_decode_step ((unsigned char *)data
, len
,
2078 (unsigned char *)data
, &state
, &save
);
2081 static char *quoted_encode_simple(const unsigned char *string
, int len
)
2083 GString
*tmp
= g_string_new("");
2086 while(string
[i
] != 0) {
2087 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
2088 g_string_append_printf(tmp
, "=%02X", string
[i
]);
2090 g_string_append_c(tmp
, string
[i
]);
2095 char *ret
= tmp
->str
;
2096 g_string_free(tmp
, FALSE
);
2101 static size_t quoted_decode_simple (char *data
, size_t len
)
2103 g_return_val_if_fail (data
!= NULL
, 0);
2105 GString
*string
= g_string_new(data
);
2113 //Get the index of the next encoded char
2114 int i
= strcspn(string
->str
, "=");
2115 if (i
>= strlen(string
->str
))
2119 strncat(hex
, &string
->str
[i
+ 1], 2);
2120 char rep
= ((int)(strtod(hex
, NULL
)));
2121 g_string_erase(string
, i
, 2);
2122 g_string_insert_c(string
, i
, rep
);
2125 memset(data
, 0, strlen(data
));
2126 strcpy(data
, string
->str
);
2127 g_string_free(string
, 1);
2129 return strlen(data
);