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(unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
);
37 static size_t base64_decode_step(unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
);
38 static size_t base64_decode_simple (char *data
, size_t len
);
39 static char *base64_encode_simple (const char *data
, size_t len
);
41 static size_t quoted_decode_simple (char *data
, size_t len
);
42 static char *quoted_encode_simple (const unsigned char *string
, int len
);
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 %i", *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 %s", *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__
, 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 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
992 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
996 for (l
= evc
->attributes
; l
; l
= l
->next
) {
998 b_VFormatAttribute
*attr
= l
->data
;
1001 int format_encoding
= VF_ENCODING_RAW
;
1003 attr_str
= g_string_new ("");
1005 /* From rfc2425, 5.8.2
1007 * contentline = [group "."] name *(";" param) ":" value CRLF
1011 attr_str
= g_string_append (attr_str
, attr
->group
);
1012 attr_str
= g_string_append_c (attr_str
, '.');
1014 attr_str
= g_string_append (attr_str
, attr
->name
);
1015 /* handle the parameters */
1016 for (p
= attr
->params
; p
; p
= p
->next
) {
1017 b_VFormatParam
*param
= p
->data
;
1019 * param = param-name "=" param-value *("," param-value)
1021 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
1022 || type
== VFORMAT_EVENT_20
) {
1025 * Character set can only be specified on the CHARSET
1026 * parameter on the Content-Type MIME header field.
1028 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
1030 attr_str
= g_string_append_c (attr_str
, ';');
1031 attr_str
= g_string_append (attr_str
, param
->name
);
1032 if (param
->values
) {
1033 attr_str
= g_string_append_c (attr_str
, '=');
1035 for (v
= param
->values
; v
; v
= v
->next
) {
1036 if (_helper_is_base64((const char *) v
->data
)) {
1037 format_encoding
= VF_ENCODING_BASE64
;
1038 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1042 * QUOTED-PRINTABLE inline encoding has been
1045 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
1046 osync_trace(TRACE_ERROR
, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__
);
1047 format_encoding
= VF_ENCODING_QP
;
1049 attr_str
= g_string_append (attr_str
, v
->data
);
1052 attr_str
= g_string_append_c (attr_str
, ',');
1056 attr_str
= g_string_append_c (attr_str
, ';');
1058 * The "TYPE=" is optional skip it.
1059 * LOGO, PHOTO and SOUND multimedia formats MUST
1060 * have a "TYPE=" parameter
1062 gboolean must_have_type
= FALSE
;
1063 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
1064 must_have_type
= TRUE
;
1065 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
1066 attr_str
= g_string_append (attr_str
, param
->name
);
1067 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
1068 attr_str
= g_string_append_c (attr_str
, '=');
1069 for (v
= param
->values
; v
; v
= v
->next
) {
1070 // check for quoted-printable encoding
1071 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
1072 format_encoding
= VF_ENCODING_QP
;
1073 // check for base64 encoding
1074 if (_helper_is_base64((const char *) v
->data
)) {
1075 format_encoding
= VF_ENCODING_BASE64
;
1078 attr_str
= g_string_append (attr_str
, v
->data
);
1080 attr_str
= g_string_append_c (attr_str
, ',');
1085 attr_str
= g_string_append_c (attr_str
, ':');
1087 for (v
= attr
->values
; v
; v
= v
->next
) {
1088 char *value
= v
->data
;
1089 char *escaped_value
= NULL
;
1091 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
1092 strstr (value
, "BYDAY") == v
->data
) {
1093 attr_str
= g_string_append (attr_str
, value
);
1095 escaped_value
= b_vformat_escape_string (value
, type
);
1096 attr_str
= g_string_append (attr_str
, escaped_value
);
1101 /* XXX toshok - i hate you, rfc 2426.
1102 why doesn't CATEGORIES use a ; like
1103 a normal list attribute? */
1104 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1105 attr_str
= g_string_append_c (attr_str
, ',');
1107 attr_str
= g_string_append_c (attr_str
, ';');
1110 g_free (escaped_value
);
1116 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1117 * After generating a content line,
1118 * lines longer than 75 characters SHOULD be folded according to the
1119 * folding procedure described in [MIME-DIR].
1121 * rfc 2445 (iCalendar), 4.1 Content Lines:
1122 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1123 * break. Long content lines SHOULD be split into a multiple line
1124 * representations using a line "folding" technique. That is, a long
1125 * line can be split between any two characters by inserting a CRLF
1126 * immediately followed by a single linear white space character (i.e.,
1127 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1128 * of CRLF followed immediately by a single linear white space character
1129 * is ignored (i.e., removed) when processing the content type.
1131 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1132 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1134 * Differences between encodings:
1135 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1137 * rfc 2425 [MIME-DIR], 5.8.1:
1138 * A logical line MAY be continued on the next physical line anywhere
1139 * between two characters by inserting a CRLF immediately followed by a
1140 * single <WS> (white space) character.
1142 * rfc 2045, 6.7, chapter 5:
1143 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1144 * without follwing <WS>
1149 * Note that all the line folding above is described in terms of characters
1150 * not bytes. In particular, it would be an error to put a line break
1151 * within a UTF-8 character.
1156 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1159 /* If using QP, must be sure that we do not fold within a quote sequence */
1160 if (format_encoding
== VF_ENCODING_QP
) {
1161 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1162 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1165 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1167 if (format_encoding
== VF_ENCODING_QP
)
1168 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1170 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1174 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1176 attr_str
= g_string_append (attr_str
, CRLF
);
1178 * base64= <MIME RFC 1521 base64 text>
1179 * the end of the text is marked with two CRLF sequences
1180 * this results in one blank line before the start of the
1183 if( format_encoding
== VF_ENCODING_BASE64
1184 && (type
== VFORMAT_CARD_21
))
1185 attr_str
= g_string_append (attr_str
, CRLF
);
1187 str
= g_string_append (str
, attr_str
->str
);
1188 g_string_free (attr_str
, TRUE
);
1192 case VFORMAT_CARD_21
:
1193 str
= g_string_append (str
, "END:VCARD\r\n");
1195 case VFORMAT_CARD_30
:
1196 str
= g_string_append (str
, "END:VCARD\r\n");
1198 case VFORMAT_TODO_10
:
1199 case VFORMAT_EVENT_10
:
1200 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1202 case VFORMAT_TODO_20
:
1203 case VFORMAT_EVENT_20
:
1204 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1207 str
= g_string_append (str
, "END:VNOTE\r\n");
1211 osync_trace(TRACE_EXIT
, "%s(%p, %i)", __func__
, type
);
1212 return g_string_free (str
, FALSE
);
1215 void b_vformat_dump_structure (b_VFormat
*evc
)
1221 printf ("b_VFormat\n");
1222 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1224 b_VFormatAttribute
*attr
= a
->data
;
1225 printf ("+-- %s\n", attr
->name
);
1227 printf (" +- params=\n");
1229 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1230 b_VFormatParam
*param
= p
->data
;
1231 printf (" | [%d] = %s", i
,param
->name
);
1233 for (v
= param
->values
; v
; v
= v
->next
) {
1234 char *value
= b_vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1235 printf ("%s", value
);
1244 printf (" +- values=\n");
1245 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1246 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1251 b_VFormatAttribute
*b_vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1253 b_VFormatAttribute
*attr
;
1255 attr
= g_new0 (b_VFormatAttribute
, 1);
1257 attr
->group
= g_strdup (attr_group
);
1258 attr
->name
= g_strdup (attr_name
);
1264 b_vformat_attribute_free (b_VFormatAttribute
*attr
)
1266 g_return_if_fail (attr
!= NULL
);
1268 g_free (attr
->block
);
1269 g_free (attr
->group
);
1270 g_free (attr
->name
);
1272 b_vformat_attribute_remove_values (attr
);
1274 b_vformat_attribute_remove_params (attr
);
1280 b_vformat_attribute_copy (b_VFormatAttribute
*attr
)
1282 b_VFormatAttribute
*a
;
1285 g_return_val_if_fail (attr
!= NULL
, NULL
);
1287 a
= b_vformat_attribute_new (b_vformat_attribute_get_group (attr
),
1288 b_vformat_attribute_get_name (attr
));
1290 for (p
= attr
->values
; p
; p
= p
->next
)
1291 b_vformat_attribute_add_value (a
, p
->data
);
1293 for (p
= attr
->params
; p
; p
= p
->next
)
1294 b_vformat_attribute_add_param (a
, b_vformat_attribute_param_copy (p
->data
));
1300 b_vformat_remove_attributes (b_VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1304 g_return_if_fail (attr_name
!= NULL
);
1306 attr
= evc
->attributes
;
1309 b_VFormatAttribute
*a
= attr
->data
;
1311 next_attr
= attr
->next
;
1313 if (((!attr_group
&& !a
->group
) ||
1314 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1315 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1317 /* matches, remove/delete the attribute */
1318 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1320 b_vformat_attribute_free (a
);
1328 b_vformat_remove_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1330 g_return_if_fail (attr
!= NULL
);
1332 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1333 b_vformat_attribute_free (attr
);
1337 b_vformat_add_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1339 g_return_if_fail (attr
!= NULL
);
1341 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1345 b_vformat_add_attribute_with_value (b_VFormat
*b_VFormat
,
1346 b_VFormatAttribute
*attr
, const char *value
)
1348 g_return_if_fail (attr
!= NULL
);
1350 b_vformat_attribute_add_value (attr
, value
);
1352 b_vformat_add_attribute (b_VFormat
, attr
);
1356 b_vformat_add_attribute_with_values (b_VFormat
*b_VFormat
, b_VFormatAttribute
*attr
, ...)
1361 g_return_if_fail (attr
!= NULL
);
1363 va_start (ap
, attr
);
1365 while ((v
= va_arg (ap
, char*))) {
1366 b_vformat_attribute_add_value (attr
, v
);
1371 b_vformat_add_attribute (b_VFormat
, attr
);
1375 b_vformat_attribute_add_value (b_VFormatAttribute
*attr
, const char *value
)
1377 g_return_if_fail (attr
!= NULL
);
1379 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1383 b_vformat_attribute_add_value_decoded (b_VFormatAttribute
*attr
, const char *value
, int len
)
1385 g_return_if_fail (attr
!= NULL
);
1387 switch (attr
->encoding
) {
1388 case VF_ENCODING_RAW
:
1389 osync_trace(TRACE_INTERNAL
, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1391 case VF_ENCODING_BASE64
: {
1392 char *b64_data
= base64_encode_simple (value
, len
);
1393 GString
*decoded
= g_string_new_len (value
, len
);
1395 /* make sure the decoded list is up to date */
1396 b_vformat_attribute_get_values_decoded (attr
);
1398 attr
->values
= g_list_append (attr
->values
, b64_data
);
1399 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1402 case VF_ENCODING_QP
: {
1403 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1404 GString
*decoded
= g_string_new (value
);
1406 /* make sure the decoded list is up to date */
1407 b_vformat_attribute_get_values_decoded (attr
);
1409 attr
->values
= g_list_append (attr
->values
, qp_data
);
1410 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1413 case VF_ENCODING_8BIT
: {
1414 char *data
= g_strdup(value
);
1415 GString
*decoded
= g_string_new (value
);
1417 /* make sure the decoded list is up to date */
1418 b_vformat_attribute_get_values_decoded (attr
);
1420 attr
->values
= g_list_append (attr
->values
, data
);
1421 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1428 b_vformat_attribute_add_values (b_VFormatAttribute
*attr
, ...)
1433 g_return_if_fail (attr
!= NULL
);
1435 va_start (ap
, attr
);
1437 while ((v
= va_arg (ap
, char*))) {
1438 b_vformat_attribute_add_value (attr
, v
);
1445 free_gstring (GString
*str
)
1447 g_string_free (str
, TRUE
);
1451 b_vformat_attribute_remove_values (b_VFormatAttribute
*attr
)
1453 g_return_if_fail (attr
!= NULL
);
1455 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1456 g_list_free (attr
->values
);
1457 attr
->values
= NULL
;
1459 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1460 g_list_free (attr
->decoded_values
);
1461 attr
->decoded_values
= NULL
;
1465 b_vformat_attribute_remove_params (b_VFormatAttribute
*attr
)
1467 g_return_if_fail (attr
!= NULL
);
1469 g_list_foreach (attr
->params
, (GFunc
)b_vformat_attribute_param_free
, NULL
);
1470 g_list_free (attr
->params
);
1471 attr
->params
= NULL
;
1473 /* also remove the cached encoding on this attribute */
1474 attr
->encoding_set
= FALSE
;
1475 attr
->encoding
= VF_ENCODING_RAW
;
1479 b_vformat_attribute_param_new (const char *name
)
1481 b_VFormatParam
*param
= g_new0 (b_VFormatParam
, 1);
1482 param
->name
= g_strdup (name
);
1488 b_vformat_attribute_param_free (b_VFormatParam
*param
)
1490 g_return_if_fail (param
!= NULL
);
1492 g_free (param
->name
);
1494 b_vformat_attribute_param_remove_values (param
);
1500 b_vformat_attribute_param_copy (b_VFormatParam
*param
)
1505 g_return_val_if_fail (param
!= NULL
, NULL
);
1507 p
= b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param
));
1509 for (l
= param
->values
; l
; l
= l
->next
) {
1510 b_vformat_attribute_param_add_value (p
, l
->data
);
1517 b_vformat_attribute_add_param (b_VFormatAttribute
*attr
,
1518 b_VFormatParam
*param
)
1520 g_return_if_fail (attr
!= NULL
);
1521 g_return_if_fail (param
!= NULL
);
1523 attr
->params
= g_list_append (attr
->params
, param
);
1525 /* we handle our special encoding stuff here */
1527 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1528 if (attr
->encoding_set
) {
1529 osync_trace(TRACE_INTERNAL
, "ENCODING specified twice");
1533 if (param
->values
&& param
->values
->data
) {
1534 if (_helper_is_base64((const char*)param
->values
->data
))
1535 attr
->encoding
= VF_ENCODING_BASE64
;
1536 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1537 attr
->encoding
= VF_ENCODING_QP
;
1538 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1539 attr
->encoding
= VF_ENCODING_8BIT
;
1541 osync_trace(TRACE_INTERNAL
, "Unknown value `%s' for ENCODING parameter. values will be treated as raw",
1542 (char*)param
->values
->data
);
1545 attr
->encoding_set
= TRUE
;
1548 osync_trace(TRACE_INTERNAL
, "ENCODING parameter added with no value");
1553 b_VFormatParam
*b_vformat_attribute_find_param(b_VFormatAttribute
*attr
, const char *name
, int level
)
1555 g_return_val_if_fail (attr
!= NULL
, NULL
);
1557 for (p
= attr
->params
; p
; p
= p
->next
) {
1558 b_VFormatParam
*param
= p
->data
;
1559 if (!g_ascii_strcasecmp (param
->name
, name
)) {
1570 b_vformat_attribute_set_value (b_VFormatAttribute
*attr
,
1571 int nth
, const char *value
)
1573 GList
*param
= g_list_nth(attr
->values
, nth
);
1574 g_free(param
->data
);
1575 param
->data
= g_strdup(value
);
1579 b_vformat_attribute_param_add_value (b_VFormatParam
*param
,
1582 g_return_if_fail (param
!= NULL
);
1584 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1588 b_vformat_attribute_param_add_values (b_VFormatParam
*param
,
1594 g_return_if_fail (param
!= NULL
);
1596 va_start (ap
, param
);
1598 while ((v
= va_arg (ap
, char*))) {
1599 b_vformat_attribute_param_add_value (param
, v
);
1606 b_vformat_attribute_add_param_with_value (b_VFormatAttribute
*attr
, const char *name
, const char *value
)
1608 g_return_if_fail (attr
!= NULL
);
1609 g_return_if_fail (name
!= NULL
);
1614 b_VFormatParam
*param
= b_vformat_attribute_param_new(name
);
1616 b_vformat_attribute_param_add_value (param
, value
);
1618 b_vformat_attribute_add_param (attr
, param
);
1622 b_vformat_attribute_add_param_with_values (b_VFormatAttribute
*attr
,
1623 b_VFormatParam
*param
, ...)
1628 g_return_if_fail (attr
!= NULL
);
1629 g_return_if_fail (param
!= NULL
);
1631 va_start (ap
, param
);
1633 while ((v
= va_arg (ap
, char*))) {
1634 b_vformat_attribute_param_add_value (param
, v
);
1639 b_vformat_attribute_add_param (attr
, param
);
1643 b_vformat_attribute_param_remove_values (b_VFormatParam
*param
)
1645 g_return_if_fail (param
!= NULL
);
1647 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1648 g_list_free (param
->values
);
1649 param
->values
= NULL
;
1653 b_vformat_get_attributes (b_VFormat
*format
)
1655 return format
->attributes
;
1659 b_vformat_attribute_get_group (b_VFormatAttribute
*attr
)
1661 g_return_val_if_fail (attr
!= NULL
, NULL
);
1667 b_vformat_attribute_get_name (b_VFormatAttribute
*attr
)
1669 g_return_val_if_fail (attr
!= NULL
, NULL
);
1675 b_vformat_attribute_get_block (b_VFormatAttribute
*attr
)
1677 g_return_val_if_fail (attr
!= NULL
, NULL
);
1683 b_vformat_attribute_get_values (b_VFormatAttribute
*attr
)
1685 g_return_val_if_fail (attr
!= NULL
, NULL
);
1687 return attr
->values
;
1691 b_vformat_attribute_get_values_decoded (b_VFormatAttribute
*attr
)
1693 g_return_val_if_fail (attr
!= NULL
, NULL
);
1695 if (!attr
->decoded_values
) {
1697 switch (attr
->encoding
) {
1698 case VF_ENCODING_RAW
:
1699 case VF_ENCODING_8BIT
:
1700 for (l
= attr
->values
; l
; l
= l
->next
)
1701 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1703 case VF_ENCODING_BASE64
:
1704 for (l
= attr
->values
; l
; l
= l
->next
) {
1705 char *decoded
= g_strdup ((char*)l
->data
);
1706 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1707 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1711 case VF_ENCODING_QP
:
1712 for (l
= attr
->values
; l
; l
= l
->next
) {
1715 char *decoded
= g_strdup ((char*)l
->data
);
1716 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1717 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1724 return attr
->decoded_values
;
1728 b_vformat_attribute_is_single_valued (b_VFormatAttribute
*attr
)
1730 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1732 if (attr
->values
== NULL
1733 || attr
->values
->next
!= NULL
)
1740 b_vformat_attribute_get_value (b_VFormatAttribute
*attr
)
1744 g_return_val_if_fail (attr
!= NULL
, NULL
);
1746 values
= b_vformat_attribute_get_values (attr
);
1748 if (!b_vformat_attribute_is_single_valued (attr
))
1749 osync_trace(TRACE_INTERNAL
, "b_vformat_attribute_get_value called on multivalued attribute");
1751 return values
? g_strdup ((char*)values
->data
) : NULL
;
1755 b_vformat_attribute_get_value_decoded (b_VFormatAttribute
*attr
)
1758 GString
*str
= NULL
;
1760 g_return_val_if_fail (attr
!= NULL
, NULL
);
1762 values
= b_vformat_attribute_get_values_decoded (attr
);
1764 if (!b_vformat_attribute_is_single_valued (attr
))
1765 osync_trace(TRACE_INTERNAL
, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1770 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1773 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute
*attr
, int nth
)
1775 GList
*values
= b_vformat_attribute_get_values_decoded(attr
);
1778 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1782 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1783 values
= b_vformat_attribute_get_values(attr
);
1786 return g_list_nth_data(values
, nth
);
1793 b_vformat_attribute_has_type (b_VFormatAttribute
*attr
, const char *typestr
)
1798 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1799 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1801 params
= b_vformat_attribute_get_params (attr
);
1803 for (p
= params
; p
; p
= p
->next
) {
1804 b_VFormatParam
*param
= p
->data
;
1806 if (!strcasecmp (b_vformat_attribute_param_get_name (param
), "TYPE")) {
1807 GList
*values
= b_vformat_attribute_param_get_values (param
);
1810 for (v
= values
; v
; v
= v
->next
) {
1811 if (!strcasecmp ((char*)v
->data
, typestr
))
1821 gboolean
b_vformat_attribute_has_param(b_VFormatAttribute
*attr
, const char *name
)
1823 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1824 g_return_val_if_fail (name
!= NULL
, FALSE
);
1826 GList
*params
= b_vformat_attribute_get_params(attr
);
1828 for (p
= params
; p
; p
= p
->next
) {
1829 b_VFormatParam
*param
= p
->data
;
1830 if (!strcasecmp(name
, b_vformat_attribute_param_get_name(param
)))
1837 b_vformat_attribute_get_params (b_VFormatAttribute
*attr
)
1839 g_return_val_if_fail (attr
!= NULL
, NULL
);
1841 return attr
->params
;
1845 b_vformat_attribute_param_get_name (b_VFormatParam
*param
)
1847 g_return_val_if_fail (param
!= NULL
, NULL
);
1853 b_vformat_attribute_param_get_values (b_VFormatParam
*param
)
1855 g_return_val_if_fail (param
!= NULL
, NULL
);
1857 return param
->values
;
1860 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam
*param
, int nth
)
1862 const char *ret
= NULL
;
1863 GList
*values
= b_vformat_attribute_param_get_values(param
);
1866 ret
= g_list_nth_data(values
, nth
);
1870 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1872 //static unsigned char _evc_base64_rank[256];
1874 static void base64_init(char *rank
)
1878 memset(rank
, 0xff, sizeof(rank
));
1879 for (i
=0;i
<64;i
++) {
1880 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1885 /* call this when finished encoding everything, to
1886 flush off the last little bit */
1887 static size_t base64_encode_close(unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1890 unsigned char *outptr
= out
;
1893 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1895 c1
= ((unsigned char *)save
)[1];
1896 c2
= ((unsigned char *)save
)[2];
1898 switch (((char *)save
)[0]) {
1900 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1901 g_assert(outptr
[2] != 0);
1906 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1907 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1922 performs an 'encode step', only encodes blocks of 3 characters to the
1923 output at a time, saves left-over state in state and save (initialise to
1924 0 on first invocation).
1926 static size_t base64_encode_step(unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1928 register unsigned char *inptr
, *outptr
;
1936 if (len
+ ((char *)save
)[0] > 2) {
1937 unsigned char *inend
= in
+len
-2;
1938 register int c1
, c2
, c3
;
1939 register int already
;
1943 switch (((char *)save
)[0]) {
1944 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1945 case 2: c1
= ((unsigned char *)save
)[1];
1946 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1949 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1950 while (inptr
< inend
) {
1956 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1957 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1958 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1959 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1960 /* this is a bit ugly ... */
1961 if (break_lines
&& (++already
)>=19) {
1967 ((char *)save
)[0] = 0;
1968 len
= 2-(inptr
-inend
);
1973 register char *saveout
;
1975 /* points to the slot for the next char to save */
1976 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1978 /* len can only be 0 1 or 2 */
1980 case 2: *saveout
++ = *inptr
++;
1981 case 1: *saveout
++ = *inptr
++;
1983 ((char *)save
)[0]+=len
;
1991 * base64_decode_step: decode a chunk of base64 encoded data
1993 * @len: max length of data to decode
1994 * @out: output stream
1995 * @state: holds the number of bits that are stored in @save
1996 * @save: leftover bits that have not yet been decoded
1998 * Decodes a chunk of base64 encoded data
2000 static size_t base64_decode_step(unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
2002 unsigned char base64_rank
[256];
2003 base64_init((char*)base64_rank
);
2005 register unsigned char *inptr
, *outptr
;
2006 unsigned char *inend
, c
;
2007 register unsigned int v
;
2013 /* convert 4 base64 bytes to 3 normal bytes */
2017 while (inptr
<inend
) {
2018 c
= base64_rank
[*inptr
++];
2034 /* quick scan back for '=' on the end somewhere */
2035 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2037 while (inptr
>in
&& i
) {
2039 if (base64_rank
[*inptr
] != 0xff) {
2040 if (*inptr
== '=' && outptr
>out
)
2046 /* if i!= 0 then there is a truncation error! */
2050 static char *base64_encode_simple (const char *data
, size_t len
)
2053 int state
= 0, outlen
;
2054 unsigned int save
= 0;
2056 g_return_val_if_fail (data
!= NULL
, NULL
);
2058 out
= g_malloc (len
* 4 / 3 + 5);
2059 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
2060 out
, &state
, (int*)&save
);
2065 static size_t base64_decode_simple (char *data
, size_t len
)
2068 unsigned int save
= 0;
2070 g_return_val_if_fail (data
!= NULL
, 0);
2072 return base64_decode_step ((unsigned char *)data
, len
,
2073 (unsigned char *)data
, &state
, &save
);
2076 static char *quoted_encode_simple(const unsigned char *string
, int len
)
2078 GString
*tmp
= g_string_new("");
2081 while(string
[i
] != 0) {
2082 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
2083 g_string_append_printf(tmp
, "=%02X", string
[i
]);
2085 g_string_append_c(tmp
, string
[i
]);
2090 char *ret
= tmp
->str
;
2091 g_string_free(tmp
, FALSE
);
2096 static size_t quoted_decode_simple (char *data
, size_t len
)
2098 g_return_val_if_fail (data
!= NULL
, 0);
2100 GString
*string
= g_string_new(data
);
2108 //Get the index of the next encoded char
2109 int i
= strcspn(string
->str
, "=");
2110 if (i
>= strlen(string
->str
))
2114 strncat(hex
, &string
->str
[i
+ 1], 2);
2115 char rep
= ((int)(strtod(hex
, NULL
)));
2116 g_string_erase(string
, i
, 2);
2117 g_string_insert_c(string
, i
, rep
);
2120 memset(data
, 0, strlen(data
));
2121 strcpy(data
, string
->str
);
2122 g_string_free(string
, 1);
2124 return strlen(data
);