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)
26 //#ifdef HAVE_CONFIG_H
35 //#include <opensync/opensync.h>
37 #define TRACE_INTERNAL 1
42 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
);
43 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
);
44 static size_t base64_decode_simple (char *data
, size_t len
);
45 static char *base64_encode_simple (const char *data
, size_t len
);
47 static size_t quoted_decode_simple (char *data
, size_t len
);
48 static char *quoted_encode_simple (const unsigned char *string
, int len
);
52 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
53 * @param check_string string that should be compared with "b" or "base64"
54 * @return 0 if check_string is not base64 and 1 if it is
56 static int _helper_is_base64(const char *check_string
)
58 if(!g_ascii_strcasecmp ((char *) check_string
, "BASE64") ||
59 !g_ascii_strcasecmp ((char *) check_string
, "b") )
64 time_t b_vformat_time_to_unix(const char *inptime
)
69 if ((ftime
= g_strrstr(inptime
, "T"))) {
71 date
= g_strndup(inptime
, ftime
- inptime
);
73 time
= g_strndup(ftime
+ 1, 8);
75 time
= g_strndup(ftime
+ 1, 6);
77 date
= g_strdup(inptime
);
81 memset(&btime
, 0, sizeof(struct tm
));
84 if (strlen(date
) == 10) {
85 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111 - 1900;
86 btime
.tm_mon
= date
[5] * 10 + date
[6] - '0' * 11 - 1;
87 btime
.tm_mday
= date
[8] * 10 + date
[9] - '0' * 11;
89 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111- 1900;
90 btime
.tm_mon
= date
[4] * 10 + date
[5] - '0' * 11 - 1;
91 btime
.tm_mday
= date
[6] * 10 + date
[7] - '0' * 11;
94 if (time
&& strlen(time
) == 8) {
96 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
97 btime
.tm_min
= time
[3] * 10 + time
[4] - '0' * 11;
98 btime
.tm_sec
= time
[6] * 10 + time
[7] - '0' * 11;
99 } else if (time
&& strlen(time
) == 6) {
100 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
101 btime
.tm_min
= time
[2] * 10 + time
[3] - '0' * 11;
102 btime
.tm_sec
= time
[4] * 10 + time
[5] - '0' * 11;
105 time_t utime
= mktime(&btime
);
109 static char *_fold_lines (char *buf
)
111 GString
*str
= g_string_new ("");
112 GString
*line
= g_string_new ("");
114 char *next
, *next2
, *q
;
115 gboolean newline
= TRUE
;
116 gboolean quotedprintable
= FALSE
;
119 * We're pretty liberal with line folding here. We handle
120 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
121 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
126 /* search new lines for quoted printable encoding */
128 for (q
=p
; *q
!= '\n' && *q
!= '\0'; q
++)
129 line
= g_string_append_unichar (line
, g_utf8_get_char (q
));
131 if (strstr(line
->str
, "ENCODING=QUOTED-PRINTABLE"))
132 quotedprintable
= TRUE
;
134 g_string_free(line
, TRUE
);
135 line
= g_string_new ("");
141 if ((quotedprintable
&& *p
== '=') || *p
== '\r' || *p
== '\n') {
142 next
= g_utf8_next_char (p
);
143 if (*next
== '\n' || *next
== '\r') {
144 next2
= g_utf8_next_char (next
);
145 if (*next2
== '\n' || *next2
== '\r' || *next2
== ' ' || *next2
== '\t') {
146 p
= g_utf8_next_char (next2
);
148 else if(quotedprintable
) {
149 p
= g_utf8_next_char (next
);
152 str
= g_string_append (str
, CRLF
);
153 p
= g_utf8_next_char (next
);
155 quotedprintable
= FALSE
;
158 else if (*p
== '=') {
159 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
160 p
= g_utf8_next_char (p
);
162 else if (*next
== ' ' || *next
== '\t') {
163 p
= g_utf8_next_char (next
);
166 str
= g_string_append (str
, CRLF
);
167 p
= g_utf8_next_char (p
);
169 quotedprintable
= FALSE
;
173 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
174 p
= g_utf8_next_char (p
);
179 g_string_free(line
, TRUE
);
181 return g_string_free (str
, FALSE
);
184 /* skip forward until we hit the CRLF, or \0 */
185 static void _skip_to_next_line (char **p
)
190 while (*lp
!= '\r' && *lp
!= '\0')
191 lp
= g_utf8_next_char (lp
);
194 lp
= g_utf8_next_char (lp
); /* \n */
195 lp
= g_utf8_next_char (lp
); /* start of the next line */
201 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
202 pointing at the character that causes us to stop */
203 static void _skip_until (char **p
, char *s
)
209 while (*lp
!= '\r' && *lp
!= '\0') {
210 gboolean s_matches
= FALSE
;
212 for (ls
= s
; *ls
; ls
= g_utf8_next_char (ls
)) {
213 if (g_utf8_get_char (ls
) == g_utf8_get_char (lp
)) {
227 static void _read_attribute_value_add (b_VFormatAttribute
*attr
, GString
*str
, GString
*charset
)
229 /* don't convert empty strings */
231 b_vformat_attribute_add_value(attr
, str
->str
);
235 char *inbuf
, *outbuf
, *p
;
236 size_t inbytesleft
, outbytesleft
;
239 p
= outbuf
= malloc(str
->len
*2);
240 inbytesleft
= str
->len
;
241 outbytesleft
= str
->len
*2;
245 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
248 cd
= iconv_open("UTF-8", charset
->str
);
250 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
252 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
255 b_vformat_attribute_add_value(attr
, outbuf
);
259 /* hmm, should not happen */
260 b_vformat_attribute_add_value(attr
, str
->str
);
268 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
269 if (g_utf8_validate (inbuf
, -1, NULL
)) {
271 b_vformat_attribute_add_value (attr
, str
->str
);
275 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
276 cd
= iconv_open("UTF-8", "ISO-8859-1");
278 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
280 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
283 b_vformat_attribute_add_value (attr
, outbuf
);
287 b_vformat_attribute_add_value (attr
, str
->str
);
301 static void _read_attribute_value (b_VFormatAttribute
*attr
, char **p
, int format_encoding
, GString
*charset
)
306 /* read in the value */
307 str
= g_string_new ("");
308 while (*lp
!= '\r' && *lp
!= '\0') {
309 if (*lp
== '=' && format_encoding
== VF_ENCODING_QP
) {
310 char a
, b
, x1
=0, x2
=0;
312 if ((a
= *(++lp
)) == '\0') break;
313 if ((b
= *(++lp
)) == '\0') break;
317 /* e.g. ...N=C3=BCrnberg\r\n
330 if (*(++tmplp
) == '\r' && *(++tmplp
) == '\n' && isalnum(*(++tmplp
))) {
337 /* append malformed input, and
339 str
= g_string_append_c(str
, a
);
340 str
= g_string_append_c(str
, b
);
349 if (b
== '\r' && c
== '\n' && isalnum(d
) && isalnum(e
)) {
355 /* append malformed input, and
357 str
= g_string_append_c(str
, a
);
358 str
= g_string_append_c(str
, b
);
362 /* append malformed input, and
364 str
= g_string_append_c(str
, a
);
365 str
= g_string_append_c(str
, b
);
373 c
= (((a
>='a'?a
-'a'+10:a
-'0')&0x0f) << 4)
374 | ((b
>='a'?b
-'a'+10:b
-'0')&0x0f);
376 str
= g_string_append_c (str
, c
);
381 else if (format_encoding
== VF_ENCODING_BASE64
) {
382 if((*lp
!= ' ') && (*lp
!= '\t') )
383 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
384 lp
= g_utf8_next_char(lp
);
386 else if (*lp
== '\\') {
387 /* convert back to the non-escaped version of
389 lp
= g_utf8_next_char(lp
);
391 str
= g_string_append_c (str
, '\\');
395 case 'n': str
= g_string_append_c (str
, '\n'); break;
396 case 'r': str
= g_string_append_c (str
, '\r'); break;
397 case ';': str
= g_string_append_c (str
, ';'); break;
399 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES")) {
400 //We need to handle categories here to work
401 //aroung a bug in evo2
402 _read_attribute_value_add (attr
, str
, charset
);
403 g_string_assign (str
, "");
405 str
= g_string_append_c (str
, ',');
407 case '\\': str
= g_string_append_c (str
, '\\'); break;
408 case '"': str
= g_string_append_c (str
, '"'); break;
409 /* \t is (incorrectly) used by kOrganizer, so handle it here */
410 case 't': str
= g_string_append_c (str
, '\t'); break;
412 BarryLogf(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp
);
413 str
= g_string_append_c (str
, '\\');
414 str
= g_string_append_unichar (str
, g_utf8_get_char(lp
));
417 lp
= g_utf8_next_char(lp
);
419 else if ((*lp
== ';') ||
420 (*lp
== ',' && !g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))) {
421 _read_attribute_value_add (attr
, str
, charset
);
422 g_string_assign (str
, "");
423 lp
= g_utf8_next_char(lp
);
426 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
427 lp
= g_utf8_next_char(lp
);
431 _read_attribute_value_add (attr
, str
, charset
);
432 g_string_free (str
, TRUE
);
436 lp
= g_utf8_next_char (lp
); /* \n */
437 lp
= g_utf8_next_char (lp
); /* start of the next line */
443 static void _read_attribute_params(b_VFormatAttribute
*attr
, char **p
, int *format_encoding
, GString
**charset
)
447 b_VFormatParam
*param
= NULL
;
448 gboolean in_quote
= FALSE
;
449 str
= g_string_new ("");
451 while (*lp
!= '\0') {
453 in_quote
= !in_quote
;
454 lp
= g_utf8_next_char (lp
);
456 else if (in_quote
|| g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/' || *lp
== '.' || *lp
== ' ') {
457 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
458 lp
= g_utf8_next_char (lp
);
460 /* accumulate until we hit the '=' or ';'. If we hit
461 * a '=' the string contains the parameter name. if
462 * we hit a ';' the string contains the parameter
463 * value and the name is either ENCODING (if value ==
464 * QUOTED-PRINTABLE) or TYPE (in any other case.)
466 else if (*lp
== '=') {
468 param
= b_vformat_attribute_param_new (str
->str
);
469 g_string_assign (str
, "");
470 lp
= g_utf8_next_char (lp
);
473 _skip_until (&lp
, ":;");
475 lp
= g_utf8_next_char (lp
); /* \n */
476 lp
= g_utf8_next_char (lp
); /* start of the next line */
480 lp
= g_utf8_next_char (lp
);
483 else if (*lp
== ';' || *lp
== ':' || *lp
== ',') {
484 gboolean colon
= (*lp
== ':');
485 gboolean comma
= (*lp
== ',');
489 b_vformat_attribute_param_add_value (param
, str
->str
);
490 g_string_assign (str
, "");
492 lp
= g_utf8_next_char (lp
);
495 /* we've got a parameter of the form:
497 * so what we do depends on if there are already values
498 * for the parameter. If there are, we just finish
499 * this parameter and skip past the offending character
500 * (unless it's the ':'). If there aren't values, we free
501 * the parameter then skip past the character.
503 if (!param
->values
) {
504 b_vformat_attribute_param_free (param
);
507 lp
= g_utf8_next_char (lp
);
512 && !g_ascii_strcasecmp (param
->name
, "encoding")) {
513 if (!g_ascii_strcasecmp (param
->values
->data
, "quoted-printable")) {
514 *format_encoding
= VF_ENCODING_QP
;
515 b_vformat_attribute_param_free (param
);
517 } else if ( _helper_is_base64(param
->values
->data
)) {
518 *format_encoding
= VF_ENCODING_BASE64
;
519 // b_vformat_attribute_param_free (param);
522 } else if (param
&& !g_ascii_strcasecmp(param
->name
, "charset")) {
523 *charset
= g_string_new(param
->values
->data
);
524 b_vformat_attribute_param_free (param
);
531 if (!g_ascii_strcasecmp (str
->str
,
532 "quoted-printable")) {
533 param_name
= "ENCODING";
534 *format_encoding
= VF_ENCODING_QP
;
536 /* apple's broken addressbook app outputs naked BASE64
537 parameters, which aren't even vcard 3.0 compliant. */
538 else if (!g_ascii_strcasecmp (str
->str
,
540 param_name
= "ENCODING";
541 g_string_assign (str
, "b");
542 *format_encoding
= VF_ENCODING_BASE64
;
549 param
= b_vformat_attribute_param_new (param_name
);
550 b_vformat_attribute_param_add_value (param
, str
->str
);
552 g_string_assign (str
, "");
554 lp
= g_utf8_next_char (lp
);
557 /* we've got an attribute with a truly empty
558 attribute parameter. So it's of the form:
560 ATTR;[PARAM=value;]*;[PARAM=value;]*:
564 the only thing to do here is, well.. nothing.
565 we skip over the character if it's not a colon,
566 and the rest is handled for us: We'll either
567 continue through the loop again if we hit a ';',
568 or we'll break out correct below if it was a ':' */
570 lp
= g_utf8_next_char (lp
);
573 if (param
&& !comma
) {
574 b_vformat_attribute_add_param (attr
, param
);
581 BarryLogf(TRACE_INTERNAL
, "invalid character found in parameter spec: \"%i\" String so far: %s", lp
[0], str
->str
);
582 g_string_assign (str
, "");
583 _skip_until (&lp
, ":;");
588 g_string_free (str
, TRUE
);
593 /* reads an entire attribute from the input buffer, leaving p pointing
594 at the start of the next line (past the \r\n) */
595 static b_VFormatAttribute
*_read_attribute (char **p
)
597 char *attr_group
= NULL
;
598 char *attr_name
= NULL
;
599 b_VFormatAttribute
*attr
= NULL
;
600 GString
*str
, *charset
= NULL
;
603 gboolean is_qp
= FALSE
;
605 /* first read in the group/name */
606 str
= g_string_new ("");
607 while (*lp
!= '\r' && *lp
!= '\0') {
608 if (*lp
== ':' || *lp
== ';') {
610 /* we've got a name, break out to the value/attribute parsing */
611 attr_name
= g_string_free (str
, FALSE
);
615 /* a line of the form:
618 * since we don't have an attribute
619 * name, skip to the end of the line
622 g_string_free (str
, TRUE
);
624 _skip_to_next_line(p
);
628 else if (*lp
== '.') {
630 BarryLogf(TRACE_INTERNAL
, "extra `.' in attribute specification. ignoring extra group `%s'", str
->str
);
631 g_string_free (str
, TRUE
);
632 str
= g_string_new ("");
635 attr_group
= g_string_free (str
, FALSE
);
636 str
= g_string_new ("");
639 else if (g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/') {
640 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
643 BarryLogf(TRACE_INTERNAL
, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp
[0], str
->str
);
644 g_string_free (str
, TRUE
);
646 _skip_to_next_line(p
);
650 lp
= g_utf8_next_char(lp
);
654 _skip_to_next_line (p
);
658 attr
= b_vformat_attribute_new (attr_group
, attr_name
);
663 /* skip past the ';' */
664 lp
= g_utf8_next_char(lp
);
665 _read_attribute_params (attr
, &lp
, &is_qp
, &charset
);
668 /* skip past the ':' */
669 lp
= g_utf8_next_char(lp
);
670 _read_attribute_value (attr
, &lp
, is_qp
, charset
);
673 if (charset
) g_string_free(charset
, TRUE
);
682 b_vformat_attribute_free (attr
);
686 static void open_block(char **block
, const char *block_name
)
688 char *start
= *block
? *block
: "";
691 result
= g_strconcat(start
, "/", block_name
, NULL
);
697 static void close_block(char **block
, const char *block_name
)
699 int name_len
= strlen(block_name
);
700 int block_len
= *block
? strlen(*block
) : 0;
701 char *cmp_start
= NULL
;
703 if( block_len
< name_len
+ 1 )
706 cmp_start
= *block
+ (block_len
- name_len
- 1);
707 if( cmp_start
[0] == '/' &&
708 g_ascii_strcasecmp(cmp_start
+1, block_name
) == 0 )
710 // end of block hierarchy contains block name,
713 // cut off the end of the string... no need to free/realloc
718 /* we try to be as forgiving as we possibly can here - this isn't a
719 * validator. Almost nothing is considered a fatal error. We always
720 * try to return *something*.
722 static void _parse(b_VFormat
*evc
, const char *str
)
724 char *buf
= g_strdup (str
);
726 b_VFormatAttribute
*attr
;
728 /* first validate the string is valid utf8 */
729 if (!g_utf8_validate (buf
, -1, (const char **)&end
)) {
730 /* if the string isn't valid, we parse as much as we can from it */
731 BarryLogf(TRACE_INTERNAL
, "invalid utf8 passed to b_VFormat. Limping along.");
735 buf
= _fold_lines (buf
);
739 attr
= _read_attribute (&p
);
741 attr
= _read_attribute (&p
);
743 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "begin")) {
744 BarryLogf(TRACE_INTERNAL
, "vformat began without a BEGIN\n");
746 if (attr
&& !g_ascii_strcasecmp (attr
->name
, "begin"))
747 b_vformat_attribute_free (attr
);
749 b_vformat_add_attribute (evc
, attr
);
753 b_VFormatAttribute
*next_attr
= _read_attribute (&p
);
756 if( g_ascii_strcasecmp(next_attr
->name
, "begin") == 0 ) {
757 // add to block hierarchy string
758 char *value
= b_vformat_attribute_get_value(next_attr
);
759 open_block(&block
, value
);
760 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
763 else if( g_ascii_strcasecmp(next_attr
->name
, "end") == 0 ) {
764 // close off the block
765 char *value
= b_vformat_attribute_get_value(next_attr
);
766 close_block(&block
, value
);
767 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
771 // apply the block to the attr
772 next_attr
->block
= g_strdup(block
);
775 b_vformat_add_attribute (evc
, next_attr
);
780 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "end")) {
781 BarryLogf(TRACE_INTERNAL
, "vformat ended without END");
788 char *b_vformat_escape_string (const char *s
, b_VFormatType type
)
793 str
= g_string_new ("");
795 /* Escape a string as described in RFC2426, section 5 */
796 for (p
= s
; p
&& *p
; p
++) {
799 str
= g_string_append (str
, "\\n");
804 str
= g_string_append (str
, "\\n");
807 str
= g_string_append (str
, "\\;");
810 if (type
== VFORMAT_CARD_30
|| type
== VFORMAT_EVENT_20
|| type
== VFORMAT_TODO_20
)
811 str
= g_string_append (str
, "\\,");
813 str
= g_string_append_c (str
, *p
);
817 * We won't escape backslashes
818 * on vcard 2.1, unless it is in the end of a value.
819 * See comments above for a better explanation
821 if (*p
!= '\0' && type
== VFORMAT_CARD_21
) {
822 BarryLogf(TRACE_INTERNAL
, "[%s]We won't escape backslashes", __func__
);
823 str
= g_string_append_c(str
, *p
);
826 BarryLogf(TRACE_INTERNAL
, "[%s] escape backslashes!!", __func__
);
827 str
= g_string_append (str
, "\\\\");
831 str
= g_string_append_c (str
, *p
);
836 return g_string_free (str
, FALSE
);
840 b_vformat_unescape_string (const char *s
)
845 g_return_val_if_fail (s
!= NULL
, NULL
);
847 str
= g_string_new ("");
849 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
850 for (p
= s
; *p
; p
++) {
854 str
= g_string_append_c (str
, '\\');
858 case 'n': str
= g_string_append_c (str
, '\n'); break;
859 case 'r': str
= g_string_append_c (str
, '\r'); break;
860 case ';': str
= g_string_append_c (str
, ';'); break;
861 case ',': str
= g_string_append_c (str
, ','); break;
862 case '\\': str
= g_string_append_c (str
, '\\'); break;
863 case '"': str
= g_string_append_c (str
, '"'); break;
864 /* \t is (incorrectly) used by kOrganizer, so handle it here */
865 case 't': str
= g_string_append_c (str
, '\t'); break;
867 BarryLogf(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p
);
868 str
= g_string_append_c (str
, '\\');
869 str
= g_string_append_unichar (str
, g_utf8_get_char(p
));
875 return g_string_free (str
, FALSE
);
879 b_vformat_construct (b_VFormat
*evc
, const char *str
)
881 g_return_if_fail (str
!= NULL
);
887 void b_vformat_free(b_VFormat
*format
)
889 g_list_foreach (format
->attributes
, (GFunc
)b_vformat_attribute_free
, NULL
);
890 g_list_free (format
->attributes
);
894 b_VFormat
*b_vformat_new_from_string (const char *str
)
896 g_return_val_if_fail (str
!= NULL
, NULL
);
897 b_VFormat
*evc
= g_malloc0(sizeof(b_VFormat
));
899 b_vformat_construct (evc
, str
);
904 b_VFormat
*b_vformat_new(void)
906 return b_vformat_new_from_string ("");
909 static int _block_match(b_VFormatAttribute
*attr
, const char *block
)
911 // a block matches if the end of the attribute's block
912 // string matches a case insensitive compare with block
914 // for example, a calendar may or may not start with a
915 // BEGIN: VCALENDAR, so DTSTART's block string could be
916 // "/vcalendar/vevent" or just "/vevent". By passing
917 // "/vevent" or even "vevent" as the block argument above,
918 // we should get a match for any of the above.
920 int attr_len
= attr
->block
? strlen(attr
->block
) : 0;
921 int block_len
= block
? strlen(block
) : 0;
924 return 1; // if block is null, match everything
926 if( attr_len
< block_len
)
927 return 0; // not enough string to compare
929 if( attr_len
== 0 && block_len
== 0 )
930 return 1; // empty and null strings match
932 if( attr
->block
== NULL
)
933 return 0; // don't compare if one side is null
935 return g_ascii_strcasecmp(&attr
->block
[attr_len
- block_len
], block
) == 0;
938 b_VFormatAttribute
*b_vformat_find_attribute(b_VFormat
*vcard
, const char *name
, int nth
, const char *block
)
940 GList
*attributes
= b_vformat_get_attributes(vcard
);
943 for (a
= attributes
; a
; a
= a
->next
) {
944 b_VFormatAttribute
*attr
= a
->data
;
945 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr
), name
)) {
946 if( block
== NULL
|| _block_match(attr
, block
) ) {
957 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
961 GList *attributes = last ? last->next : 0;
964 for (a = attributes; a; a = a->next) {
965 b_VFormatAttribute *attr = a->data;
966 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
976 char *b_vformat_to_string (b_VFormat
*evc
, b_VFormatType type
)
978 BarryLogf(TRACE_ENTRY
, "%s(%p, %i)", __func__
, evc
, type
);
982 GString
*str
= g_string_new ("");
985 case VFORMAT_CARD_21
:
986 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
988 case VFORMAT_CARD_30
:
989 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
991 case VFORMAT_TODO_10
:
992 case VFORMAT_EVENT_10
:
993 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
995 case VFORMAT_TODO_20
:
996 case VFORMAT_EVENT_20
:
997 case VFORMAT_JOURNAL
:
998 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
1001 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
1005 for (l
= evc
->attributes
; l
; l
= l
->next
) {
1007 b_VFormatAttribute
*attr
= l
->data
;
1010 int format_encoding
= VF_ENCODING_RAW
;
1012 attr_str
= g_string_new ("");
1014 /* From rfc2425, 5.8.2
1016 * contentline = [group "."] name *(";" param) ":" value CRLF
1020 attr_str
= g_string_append (attr_str
, attr
->group
);
1021 attr_str
= g_string_append_c (attr_str
, '.');
1023 attr_str
= g_string_append (attr_str
, attr
->name
);
1024 /* handle the parameters */
1025 for (p
= attr
->params
; p
; p
= p
->next
) {
1026 b_VFormatParam
*param
= p
->data
;
1028 * param = param-name "=" param-value *("," param-value)
1030 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
1031 || type
== VFORMAT_EVENT_20
|| type
== VFORMAT_JOURNAL
) {
1034 * Character set can only be specified on the CHARSET
1035 * parameter on the Content-Type MIME header field.
1037 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
1039 attr_str
= g_string_append_c (attr_str
, ';');
1040 attr_str
= g_string_append (attr_str
, param
->name
);
1041 if (param
->values
) {
1042 attr_str
= g_string_append_c (attr_str
, '=');
1044 for (v
= param
->values
; v
; v
= v
->next
) {
1045 if (_helper_is_base64((const char *) v
->data
)) {
1046 format_encoding
= VF_ENCODING_BASE64
;
1047 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1048 v
->data
=g_strdup("B");
1051 * QUOTED-PRINTABLE inline encoding has been
1054 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
1055 BarryLogf(TRACE_ERROR
, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__
);
1056 format_encoding
= VF_ENCODING_QP
;
1058 attr_str
= g_string_append (attr_str
, v
->data
);
1061 attr_str
= g_string_append_c (attr_str
, ',');
1065 attr_str
= g_string_append_c (attr_str
, ';');
1067 * The "TYPE=" is optional skip it.
1068 * LOGO, PHOTO and SOUND multimedia formats MUST
1069 * have a "TYPE=" parameter
1071 gboolean must_have_type
= FALSE
;
1072 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
1073 must_have_type
= TRUE
;
1074 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
1075 attr_str
= g_string_append (attr_str
, param
->name
);
1076 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
1077 attr_str
= g_string_append_c (attr_str
, '=');
1078 for (v
= param
->values
; v
; v
= v
->next
) {
1079 // check for quoted-printable encoding
1080 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
1081 format_encoding
= VF_ENCODING_QP
;
1082 // check for base64 encoding
1083 if (_helper_is_base64((const char *) v
->data
)) {
1084 format_encoding
= VF_ENCODING_BASE64
;
1085 v
->data
=g_strdup("BASE64");
1087 attr_str
= g_string_append (attr_str
, v
->data
);
1089 attr_str
= g_string_append_c (attr_str
, ',');
1094 attr_str
= g_string_append_c (attr_str
, ':');
1096 for (v
= attr
->values
; v
; v
= v
->next
) {
1097 char *value
= v
->data
;
1098 char *escaped_value
= NULL
;
1100 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
1101 strstr (value
, "BYDAY") == v
->data
) {
1102 attr_str
= g_string_append (attr_str
, value
);
1104 escaped_value
= b_vformat_escape_string (value
, type
);
1105 attr_str
= g_string_append (attr_str
, escaped_value
);
1110 /* XXX toshok - i hate you, rfc 2426.
1111 why doesn't CATEGORIES use a ; like
1112 a normal list attribute? */
1113 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1114 attr_str
= g_string_append_c (attr_str
, ',');
1116 attr_str
= g_string_append_c (attr_str
, ';');
1119 g_free (escaped_value
);
1125 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1126 * After generating a content line,
1127 * lines longer than 75 characters SHOULD be folded according to the
1128 * folding procedure described in [MIME-DIR].
1130 * rfc 2445 (iCalendar), 4.1 Content Lines:
1131 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1132 * break. Long content lines SHOULD be split into a multiple line
1133 * representations using a line "folding" technique. That is, a long
1134 * line can be split between any two characters by inserting a CRLF
1135 * immediately followed by a single linear white space character (i.e.,
1136 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1137 * of CRLF followed immediately by a single linear white space character
1138 * is ignored (i.e., removed) when processing the content type.
1140 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1141 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1143 * Differences between encodings:
1144 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1146 * rfc 2425 [MIME-DIR], 5.8.1:
1147 * A logical line MAY be continued on the next physical line anywhere
1148 * between two characters by inserting a CRLF immediately followed by a
1149 * single <WS> (white space) character.
1151 * rfc 2045, 6.7, chapter 5:
1152 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1153 * without follwing <WS>
1158 * Note that all the line folding above is described in terms of characters
1159 * not bytes. In particular, it would be an error to put a line break
1160 * within a UTF-8 character.
1165 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1168 /* If using QP, must be sure that we do not fold within a quote sequence */
1169 if (format_encoding
== VF_ENCODING_QP
) {
1170 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1171 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1174 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1176 if (format_encoding
== VF_ENCODING_QP
)
1177 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1179 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1183 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1185 attr_str
= g_string_append (attr_str
, CRLF
);
1187 * base64= <MIME RFC 1521 base64 text>
1188 * the end of the text is marked with two CRLF sequences
1189 * this results in one blank line before the start of the
1192 if( format_encoding
== VF_ENCODING_BASE64
1193 && (type
== VFORMAT_CARD_21
))
1194 attr_str
= g_string_append (attr_str
, CRLF
);
1196 str
= g_string_append (str
, attr_str
->str
);
1197 g_string_free (attr_str
, TRUE
);
1201 case VFORMAT_CARD_21
:
1202 str
= g_string_append (str
, "END:VCARD\r\n");
1204 case VFORMAT_CARD_30
:
1205 str
= g_string_append (str
, "END:VCARD\r\n");
1207 case VFORMAT_TODO_10
:
1208 case VFORMAT_EVENT_10
:
1209 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1211 case VFORMAT_TODO_20
:
1212 case VFORMAT_EVENT_20
:
1213 case VFORMAT_JOURNAL
:
1214 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1217 str
= g_string_append (str
, "END:VNOTE\r\n");
1221 BarryLogf(TRACE_EXIT
, "%s", __func__
);
1222 return g_string_free (str
, FALSE
);
1225 void b_vformat_dump_structure (b_VFormat
*evc
)
1231 printf ("b_VFormat\n");
1232 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1234 b_VFormatAttribute
*attr
= a
->data
;
1235 printf ("+-- %s\n", attr
->name
);
1237 printf (" +- params=\n");
1239 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1240 b_VFormatParam
*param
= p
->data
;
1241 printf (" | [%d] = %s", i
,param
->name
);
1243 for (v
= param
->values
; v
; v
= v
->next
) {
1244 char *value
= b_vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1245 printf ("%s", value
);
1254 printf (" +- values=\n");
1255 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1256 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1261 b_VFormatAttribute
*b_vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1263 b_VFormatAttribute
*attr
;
1265 attr
= g_new0 (b_VFormatAttribute
, 1);
1267 attr
->group
= g_strdup (attr_group
);
1268 attr
->name
= g_strdup (attr_name
);
1274 b_vformat_attribute_free (b_VFormatAttribute
*attr
)
1276 g_return_if_fail (attr
!= NULL
);
1278 g_free (attr
->block
);
1279 g_free (attr
->group
);
1280 g_free (attr
->name
);
1282 b_vformat_attribute_remove_values (attr
);
1284 b_vformat_attribute_remove_params (attr
);
1290 b_vformat_attribute_copy (b_VFormatAttribute
*attr
)
1292 b_VFormatAttribute
*a
;
1295 g_return_val_if_fail (attr
!= NULL
, NULL
);
1297 a
= b_vformat_attribute_new (b_vformat_attribute_get_group (attr
),
1298 b_vformat_attribute_get_name (attr
));
1300 for (p
= attr
->values
; p
; p
= p
->next
)
1301 b_vformat_attribute_add_value (a
, p
->data
);
1303 for (p
= attr
->params
; p
; p
= p
->next
)
1304 b_vformat_attribute_add_param (a
, b_vformat_attribute_param_copy (p
->data
));
1310 b_vformat_remove_attributes (b_VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1314 g_return_if_fail (attr_name
!= NULL
);
1316 attr
= evc
->attributes
;
1319 b_VFormatAttribute
*a
= attr
->data
;
1321 next_attr
= attr
->next
;
1323 if (((!attr_group
&& !a
->group
) ||
1324 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1325 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1327 /* matches, remove/delete the attribute */
1328 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1330 b_vformat_attribute_free (a
);
1338 b_vformat_remove_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1340 g_return_if_fail (attr
!= NULL
);
1342 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1343 b_vformat_attribute_free (attr
);
1347 b_vformat_add_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1349 g_return_if_fail (attr
!= NULL
);
1351 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1355 b_vformat_add_attribute_with_value (b_VFormat
*b_VFormat
,
1356 b_VFormatAttribute
*attr
, const char *value
)
1358 g_return_if_fail (attr
!= NULL
);
1360 b_vformat_attribute_add_value (attr
, value
);
1362 b_vformat_add_attribute (b_VFormat
, attr
);
1366 b_vformat_add_attribute_with_values (b_VFormat
*b_VFormat
, b_VFormatAttribute
*attr
, ...)
1371 g_return_if_fail (attr
!= NULL
);
1373 va_start (ap
, attr
);
1375 while ((v
= va_arg (ap
, char*))) {
1376 b_vformat_attribute_add_value (attr
, v
);
1381 b_vformat_add_attribute (b_VFormat
, attr
);
1385 b_vformat_attribute_add_value (b_VFormatAttribute
*attr
, const char *value
)
1387 g_return_if_fail (attr
!= NULL
);
1389 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1393 b_vformat_attribute_add_value_decoded (b_VFormatAttribute
*attr
, const char *value
, int len
)
1395 g_return_if_fail (attr
!= NULL
);
1397 switch (attr
->encoding
) {
1398 case VF_ENCODING_RAW
:
1399 BarryLogf(TRACE_INTERNAL
, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1401 case VF_ENCODING_BASE64
: {
1402 char *b64_data
= base64_encode_simple (value
, len
);
1403 GString
*decoded
= g_string_new_len (value
, len
);
1405 /* make sure the decoded list is up to date */
1406 b_vformat_attribute_get_values_decoded (attr
);
1408 attr
->values
= g_list_append (attr
->values
, b64_data
);
1409 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1412 case VF_ENCODING_QP
: {
1413 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1414 GString
*decoded
= g_string_new (value
);
1416 /* make sure the decoded list is up to date */
1417 b_vformat_attribute_get_values_decoded (attr
);
1419 attr
->values
= g_list_append (attr
->values
, qp_data
);
1420 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1423 case VF_ENCODING_8BIT
: {
1424 char *data
= g_strdup(value
);
1425 GString
*decoded
= g_string_new (value
);
1427 /* make sure the decoded list is up to date */
1428 b_vformat_attribute_get_values_decoded (attr
);
1430 attr
->values
= g_list_append (attr
->values
, data
);
1431 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1438 b_vformat_attribute_add_values (b_VFormatAttribute
*attr
, ...)
1443 g_return_if_fail (attr
!= NULL
);
1445 va_start (ap
, attr
);
1447 while ((v
= va_arg (ap
, char*))) {
1448 b_vformat_attribute_add_value (attr
, v
);
1455 free_gstring (GString
*str
)
1457 g_string_free (str
, TRUE
);
1461 b_vformat_attribute_remove_values (b_VFormatAttribute
*attr
)
1463 g_return_if_fail (attr
!= NULL
);
1465 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1466 g_list_free (attr
->values
);
1467 attr
->values
= NULL
;
1469 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1470 g_list_free (attr
->decoded_values
);
1471 attr
->decoded_values
= NULL
;
1475 b_vformat_attribute_remove_params (b_VFormatAttribute
*attr
)
1477 g_return_if_fail (attr
!= NULL
);
1479 g_list_foreach (attr
->params
, (GFunc
)b_vformat_attribute_param_free
, NULL
);
1480 g_list_free (attr
->params
);
1481 attr
->params
= NULL
;
1483 /* also remove the cached encoding on this attribute */
1484 attr
->encoding_set
= FALSE
;
1485 attr
->encoding
= VF_ENCODING_RAW
;
1489 b_vformat_attribute_param_new (const char *name
)
1491 b_VFormatParam
*param
= g_new0 (b_VFormatParam
, 1);
1492 param
->name
= g_strdup (name
);
1498 b_vformat_attribute_param_free (b_VFormatParam
*param
)
1500 g_return_if_fail (param
!= NULL
);
1502 g_free (param
->name
);
1504 b_vformat_attribute_param_remove_values (param
);
1510 b_vformat_attribute_param_copy (b_VFormatParam
*param
)
1515 g_return_val_if_fail (param
!= NULL
, NULL
);
1517 p
= b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param
));
1519 for (l
= param
->values
; l
; l
= l
->next
) {
1520 b_vformat_attribute_param_add_value (p
, l
->data
);
1527 b_vformat_attribute_add_param (b_VFormatAttribute
*attr
,
1528 b_VFormatParam
*param
)
1530 g_return_if_fail (attr
!= NULL
);
1531 g_return_if_fail (param
!= NULL
);
1533 attr
->params
= g_list_append (attr
->params
, param
);
1535 /* we handle our special encoding stuff here */
1537 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1538 if (attr
->encoding_set
) {
1539 BarryLogf(TRACE_INTERNAL
, "ENCODING specified twice");
1543 if (param
->values
&& param
->values
->data
) {
1544 if (_helper_is_base64((const char*)param
->values
->data
))
1545 attr
->encoding
= VF_ENCODING_BASE64
;
1546 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1547 attr
->encoding
= VF_ENCODING_QP
;
1548 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1549 attr
->encoding
= VF_ENCODING_8BIT
;
1551 BarryLogf(TRACE_INTERNAL
, "Unknown value `%s' for ENCODING parameter. values will be treated as raw", (char*)param
->values
->data
);
1554 attr
->encoding_set
= TRUE
;
1557 BarryLogf(TRACE_INTERNAL
, "ENCODING parameter added with no value");
1562 b_VFormatParam
*b_vformat_attribute_find_param(b_VFormatAttribute
*attr
, const char *name
, int level
)
1564 g_return_val_if_fail (attr
!= NULL
, NULL
);
1566 for (p
= attr
->params
; p
; p
= p
->next
) {
1567 b_VFormatParam
*param
= p
->data
;
1568 if (!g_ascii_strcasecmp (param
->name
, name
)) {
1579 b_vformat_attribute_set_value (b_VFormatAttribute
*attr
,
1580 int nth
, const char *value
)
1582 GList
*param
= g_list_nth(attr
->values
, nth
);
1583 g_free(param
->data
);
1584 param
->data
= g_strdup(value
);
1588 b_vformat_attribute_param_add_value (b_VFormatParam
*param
,
1591 g_return_if_fail (param
!= NULL
);
1593 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1597 b_vformat_attribute_param_add_values (b_VFormatParam
*param
,
1603 g_return_if_fail (param
!= NULL
);
1605 va_start (ap
, param
);
1607 while ((v
= va_arg (ap
, char*))) {
1608 b_vformat_attribute_param_add_value (param
, v
);
1615 b_vformat_attribute_add_param_with_value (b_VFormatAttribute
*attr
, const char *name
, const char *value
)
1617 g_return_if_fail (attr
!= NULL
);
1618 g_return_if_fail (name
!= NULL
);
1623 b_VFormatParam
*param
= b_vformat_attribute_param_new(name
);
1625 b_vformat_attribute_param_add_value (param
, value
);
1627 b_vformat_attribute_add_param (attr
, param
);
1631 b_vformat_attribute_add_param_with_values (b_VFormatAttribute
*attr
,
1632 b_VFormatParam
*param
, ...)
1637 g_return_if_fail (attr
!= NULL
);
1638 g_return_if_fail (param
!= NULL
);
1640 va_start (ap
, param
);
1642 while ((v
= va_arg (ap
, char*))) {
1643 b_vformat_attribute_param_add_value (param
, v
);
1648 b_vformat_attribute_add_param (attr
, param
);
1652 b_vformat_attribute_param_remove_values (b_VFormatParam
*param
)
1654 g_return_if_fail (param
!= NULL
);
1656 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1657 g_list_free (param
->values
);
1658 param
->values
= NULL
;
1662 b_vformat_get_attributes (b_VFormat
*format
)
1664 return format
->attributes
;
1668 b_vformat_attribute_get_group (b_VFormatAttribute
*attr
)
1670 g_return_val_if_fail (attr
!= NULL
, NULL
);
1676 b_vformat_attribute_get_name (b_VFormatAttribute
*attr
)
1678 g_return_val_if_fail (attr
!= NULL
, NULL
);
1684 b_vformat_attribute_get_block (b_VFormatAttribute
*attr
)
1686 g_return_val_if_fail (attr
!= NULL
, NULL
);
1692 b_vformat_attribute_get_values (b_VFormatAttribute
*attr
)
1694 g_return_val_if_fail (attr
!= NULL
, NULL
);
1696 return attr
->values
;
1700 b_vformat_attribute_get_values_decoded (b_VFormatAttribute
*attr
)
1702 g_return_val_if_fail (attr
!= NULL
, NULL
);
1704 if (!attr
->decoded_values
) {
1706 switch (attr
->encoding
) {
1707 case VF_ENCODING_RAW
:
1708 case VF_ENCODING_8BIT
:
1709 for (l
= attr
->values
; l
; l
= l
->next
)
1710 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1712 case VF_ENCODING_BASE64
:
1713 for (l
= attr
->values
; l
; l
= l
->next
) {
1714 char *decoded
= g_strdup ((char*)l
->data
);
1715 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1716 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1720 case VF_ENCODING_QP
:
1721 for (l
= attr
->values
; l
; l
= l
->next
) {
1724 char *decoded
= g_strdup ((char*)l
->data
);
1725 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1726 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1733 return attr
->decoded_values
;
1737 b_vformat_attribute_is_single_valued (b_VFormatAttribute
*attr
)
1739 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1741 if (attr
->values
== NULL
1742 || attr
->values
->next
!= NULL
)
1749 b_vformat_attribute_get_value (b_VFormatAttribute
*attr
)
1753 g_return_val_if_fail (attr
!= NULL
, NULL
);
1755 values
= b_vformat_attribute_get_values (attr
);
1757 if (!b_vformat_attribute_is_single_valued (attr
))
1758 BarryLogf(TRACE_INTERNAL
, "b_vformat_attribute_get_value called on multivalued attribute");
1760 return values
? g_strdup ((char*)values
->data
) : NULL
;
1764 b_vformat_attribute_get_value_decoded (b_VFormatAttribute
*attr
)
1767 GString
*str
= NULL
;
1769 g_return_val_if_fail (attr
!= NULL
, NULL
);
1771 values
= b_vformat_attribute_get_values_decoded (attr
);
1773 if (!b_vformat_attribute_is_single_valued (attr
))
1774 BarryLogf(TRACE_INTERNAL
, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1779 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1782 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute
*attr
, int nth
)
1784 GList
*values
= b_vformat_attribute_get_values_decoded(attr
);
1787 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1791 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1792 values
= b_vformat_attribute_get_values(attr
);
1795 return g_list_nth_data(values
, nth
);
1802 b_vformat_attribute_has_type (b_VFormatAttribute
*attr
, const char *typestr
)
1807 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1808 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1810 params
= b_vformat_attribute_get_params (attr
);
1812 for (p
= params
; p
; p
= p
->next
) {
1813 b_VFormatParam
*param
= p
->data
;
1815 if (!strcasecmp (b_vformat_attribute_param_get_name (param
), "TYPE")) {
1816 GList
*values
= b_vformat_attribute_param_get_values (param
);
1819 for (v
= values
; v
; v
= v
->next
) {
1820 if (!strcasecmp ((char*)v
->data
, typestr
))
1830 gboolean
b_vformat_attribute_has_param(b_VFormatAttribute
*attr
, const char *name
)
1832 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1833 g_return_val_if_fail (name
!= NULL
, FALSE
);
1835 GList
*params
= b_vformat_attribute_get_params(attr
);
1837 for (p
= params
; p
; p
= p
->next
) {
1838 b_VFormatParam
*param
= p
->data
;
1839 if (!strcasecmp(name
, b_vformat_attribute_param_get_name(param
)))
1846 b_vformat_attribute_get_params (b_VFormatAttribute
*attr
)
1848 g_return_val_if_fail (attr
!= NULL
, NULL
);
1850 return attr
->params
;
1854 b_vformat_attribute_param_get_name (b_VFormatParam
*param
)
1856 g_return_val_if_fail (param
!= NULL
, NULL
);
1862 b_vformat_attribute_param_get_values (b_VFormatParam
*param
)
1864 g_return_val_if_fail (param
!= NULL
, NULL
);
1866 return param
->values
;
1869 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam
*param
, int nth
)
1871 const char *ret
= NULL
;
1872 GList
*values
= b_vformat_attribute_param_get_values(param
);
1875 ret
= g_list_nth_data(values
, nth
);
1879 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1881 //static unsigned char _evc_base64_rank[256];
1883 static void base64_init(char *rank
)
1887 memset(rank
, 0xff, sizeof(rank
));
1888 for (i
=0;i
<64;i
++) {
1889 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1894 /* call this when finished encoding everything, to
1895 flush off the last little bit */
1896 static size_t base64_encode_close(const unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1899 unsigned char *outptr
= out
;
1902 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1904 c1
= ((unsigned char *)save
)[1];
1905 c2
= ((unsigned char *)save
)[2];
1907 switch (((char *)save
)[0]) {
1909 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1910 g_assert(outptr
[2] != 0);
1915 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1916 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1931 performs an 'encode step', only encodes blocks of 3 characters to the
1932 output at a time, saves left-over state in state and save (initialise to
1933 0 on first invocation).
1935 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1937 register const unsigned char *inptr
;
1938 register unsigned char *outptr
;
1946 if (len
+ ((char *)save
)[0] > 2) {
1947 const unsigned char *inend
= in
+len
-2;
1948 register int c1
, c2
, c3
;
1949 register int already
;
1953 switch (((char *)save
)[0]) {
1954 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1955 case 2: c1
= ((unsigned char *)save
)[1];
1956 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1959 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1960 while (inptr
< inend
) {
1966 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1967 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1968 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1969 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1970 /* this is a bit ugly ... */
1971 if (break_lines
&& (++already
)>=19) {
1977 ((char *)save
)[0] = 0;
1978 len
= 2-(inptr
-inend
);
1983 register char *saveout
;
1985 /* points to the slot for the next char to save */
1986 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1988 /* len can only be 0 1 or 2 */
1990 case 2: *saveout
++ = *inptr
++;
1991 case 1: *saveout
++ = *inptr
++;
1993 ((char *)save
)[0]+=len
;
2001 * base64_decode_step: decode a chunk of base64 encoded data
2003 * @len: max length of data to decode
2004 * @out: output stream
2005 * @state: holds the number of bits that are stored in @save
2006 * @save: leftover bits that have not yet been decoded
2008 * Decodes a chunk of base64 encoded data
2010 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
2012 unsigned char base64_rank
[256];
2013 base64_init((char*)base64_rank
);
2015 register const unsigned char *inptr
;
2016 register unsigned char *outptr
;
2017 const unsigned char *inend
;
2019 register unsigned int v
;
2025 /* convert 4 base64 bytes to 3 normal bytes */
2029 while (inptr
<inend
) {
2030 c
= base64_rank
[*inptr
++];
2046 /* quick scan back for '=' on the end somewhere */
2047 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2049 while (inptr
>in
&& i
) {
2051 if (base64_rank
[*inptr
] != 0xff) {
2052 if (*inptr
== '=' && outptr
>out
)
2058 /* if i!= 0 then there is a truncation error! */
2062 static char *base64_encode_simple (const char *data
, size_t len
)
2065 int state
= 0, outlen
;
2066 unsigned int save
= 0;
2068 g_return_val_if_fail (data
!= NULL
, NULL
);
2070 out
= g_malloc (len
* 4 / 3 + 5);
2071 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
2072 out
, &state
, (int*)&save
);
2077 static size_t base64_decode_simple (char *data
, size_t len
)
2080 unsigned int save
= 0;
2082 g_return_val_if_fail (data
!= NULL
, 0);
2084 return base64_decode_step ((unsigned char *)data
, len
,
2085 (unsigned char *)data
, &state
, &save
);
2088 static char *quoted_encode_simple(const unsigned char *string
, int len
)
2090 GString
*tmp
= g_string_new("");
2093 while(string
[i
] != 0) {
2094 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
2095 g_string_append_printf(tmp
, "=%02X", string
[i
]);
2097 g_string_append_c(tmp
, string
[i
]);
2102 char *ret
= tmp
->str
;
2103 g_string_free(tmp
, FALSE
);
2108 static size_t quoted_decode_simple (char *data
, size_t len
)
2110 g_return_val_if_fail (data
!= NULL
, 0);
2112 GString
*string
= g_string_new(data
);
2120 //Get the index of the next encoded char
2121 int i
= strcspn(string
->str
, "=");
2122 if (i
>= strlen(string
->str
))
2126 strncat(hex
, &string
->str
[i
+ 1], 2);
2127 char rep
= ((int)(strtod(hex
, NULL
)));
2128 g_string_erase(string
, i
, 2);
2129 g_string_insert_c(string
, i
, rep
);
2132 memset(data
, 0, strlen(data
));
2133 strcpy(data
, string
->str
);
2134 g_string_free(string
, 1);
2136 return strlen(data
);