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
);
149 str
= g_string_append (str
, CRLF
);
150 p
= g_utf8_next_char (next
);
152 quotedprintable
= FALSE
;
155 else if (*p
== '=') {
156 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
157 p
= g_utf8_next_char (p
);
159 else if (*next
== ' ' || *next
== '\t') {
160 p
= g_utf8_next_char (next
);
163 str
= g_string_append (str
, CRLF
);
164 p
= g_utf8_next_char (p
);
166 quotedprintable
= FALSE
;
170 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
171 p
= g_utf8_next_char (p
);
176 g_string_free(line
, TRUE
);
178 return g_string_free (str
, FALSE
);
181 /* skip forward until we hit the CRLF, or \0 */
182 static void _skip_to_next_line (char **p
)
187 while (*lp
!= '\r' && *lp
!= '\0')
188 lp
= g_utf8_next_char (lp
);
191 lp
= g_utf8_next_char (lp
); /* \n */
192 lp
= g_utf8_next_char (lp
); /* start of the next line */
198 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
199 pointing at the character that causes us to stop */
200 static void _skip_until (char **p
, char *s
)
206 while (*lp
!= '\r' && *lp
!= '\0') {
207 gboolean s_matches
= FALSE
;
209 for (ls
= s
; *ls
; ls
= g_utf8_next_char (ls
)) {
210 if (g_utf8_get_char (ls
) == g_utf8_get_char (lp
)) {
224 static void _read_attribute_value_add (b_VFormatAttribute
*attr
, GString
*str
, GString
*charset
)
226 /* don't convert empty strings */
228 b_vformat_attribute_add_value(attr
, str
->str
);
232 char *inbuf
, *outbuf
, *p
;
233 size_t inbytesleft
, outbytesleft
;
236 p
= outbuf
= malloc(str
->len
*2);
237 inbytesleft
= str
->len
;
238 outbytesleft
= str
->len
*2;
242 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
245 cd
= iconv_open("UTF-8", charset
->str
);
247 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
249 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
252 b_vformat_attribute_add_value(attr
, outbuf
);
256 /* hmm, should not happen */
257 b_vformat_attribute_add_value(attr
, str
->str
);
265 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
266 if (g_utf8_validate (inbuf
, -1, NULL
)) {
268 b_vformat_attribute_add_value (attr
, str
->str
);
272 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
273 cd
= iconv_open("UTF-8", "ISO-8859-1");
275 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
277 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
280 b_vformat_attribute_add_value (attr
, outbuf
);
284 b_vformat_attribute_add_value (attr
, str
->str
);
298 static void _read_attribute_value (b_VFormatAttribute
*attr
, char **p
, int format_encoding
, GString
*charset
)
303 /* read in the value */
304 str
= g_string_new ("");
305 while (*lp
!= '\r' && *lp
!= '\0') {
306 if (*lp
== '=' && format_encoding
== VF_ENCODING_QP
) {
307 char a
, b
, x1
=0, x2
=0;
309 if ((a
= *(++lp
)) == '\0') break;
310 if ((b
= *(++lp
)) == '\0') break;
314 /* e.g. ...N=C3=BCrnberg\r\n
327 if (*(++tmplp
) == '\r' && *(++tmplp
) == '\n' && isalnum(*(++tmplp
))) {
334 /* append malformed input, and
336 str
= g_string_append_c(str
, a
);
337 str
= g_string_append_c(str
, b
);
346 if (b
== '\r' && c
== '\n' && isalnum(d
) && isalnum(e
)) {
352 /* append malformed input, and
354 str
= g_string_append_c(str
, a
);
355 str
= g_string_append_c(str
, b
);
359 /* append malformed input, and
361 str
= g_string_append_c(str
, a
);
362 str
= g_string_append_c(str
, b
);
370 c
= (((a
>='a'?a
-'a'+10:a
-'0')&0x0f) << 4)
371 | ((b
>='a'?b
-'a'+10:b
-'0')&0x0f);
373 str
= g_string_append_c (str
, c
);
378 else if (format_encoding
== VF_ENCODING_BASE64
) {
379 if((*lp
!= ' ') && (*lp
!= '\t') )
380 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
381 lp
= g_utf8_next_char(lp
);
383 else if (*lp
== '\\') {
384 /* convert back to the non-escaped version of
386 lp
= g_utf8_next_char(lp
);
388 str
= g_string_append_c (str
, '\\');
392 case 'n': str
= g_string_append_c (str
, '\n'); break;
393 case 'r': str
= g_string_append_c (str
, '\r'); break;
394 case ';': str
= g_string_append_c (str
, ';'); break;
396 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES")) {
397 //We need to handle categories here to work
398 //aroung a bug in evo2
399 _read_attribute_value_add (attr
, str
, charset
);
400 g_string_assign (str
, "");
402 str
= g_string_append_c (str
, ',');
404 case '\\': str
= g_string_append_c (str
, '\\'); break;
405 case '"': str
= g_string_append_c (str
, '"'); break;
406 /* \t is (incorrectly) used by kOrganizer, so handle it here */
407 case 't': str
= g_string_append_c (str
, '\t'); break;
409 BarryLogf(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp
);
410 str
= g_string_append_c (str
, '\\');
411 str
= g_string_append_unichar (str
, g_utf8_get_char(lp
));
414 lp
= g_utf8_next_char(lp
);
416 else if ((*lp
== ';') ||
417 (*lp
== ',' && !g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))) {
418 _read_attribute_value_add (attr
, str
, charset
);
419 g_string_assign (str
, "");
420 lp
= g_utf8_next_char(lp
);
423 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
424 lp
= g_utf8_next_char(lp
);
428 _read_attribute_value_add (attr
, str
, charset
);
429 g_string_free (str
, TRUE
);
433 lp
= g_utf8_next_char (lp
); /* \n */
434 lp
= g_utf8_next_char (lp
); /* start of the next line */
440 static void _read_attribute_params(b_VFormatAttribute
*attr
, char **p
, int *format_encoding
, GString
**charset
)
444 b_VFormatParam
*param
= NULL
;
445 gboolean in_quote
= FALSE
;
446 str
= g_string_new ("");
448 while (*lp
!= '\0') {
450 in_quote
= !in_quote
;
451 lp
= g_utf8_next_char (lp
);
453 else if (in_quote
|| g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/' || *lp
== '.' || *lp
== ' ') {
454 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
455 lp
= g_utf8_next_char (lp
);
457 /* accumulate until we hit the '=' or ';'. If we hit
458 * a '=' the string contains the parameter name. if
459 * we hit a ';' the string contains the parameter
460 * value and the name is either ENCODING (if value ==
461 * QUOTED-PRINTABLE) or TYPE (in any other case.)
463 else if (*lp
== '=') {
465 param
= b_vformat_attribute_param_new (str
->str
);
466 g_string_assign (str
, "");
467 lp
= g_utf8_next_char (lp
);
470 _skip_until (&lp
, ":;");
472 lp
= g_utf8_next_char (lp
); /* \n */
473 lp
= g_utf8_next_char (lp
); /* start of the next line */
477 lp
= g_utf8_next_char (lp
);
480 else if (*lp
== ';' || *lp
== ':' || *lp
== ',') {
481 gboolean colon
= (*lp
== ':');
482 gboolean comma
= (*lp
== ',');
486 b_vformat_attribute_param_add_value (param
, str
->str
);
487 g_string_assign (str
, "");
489 lp
= g_utf8_next_char (lp
);
492 /* we've got a parameter of the form:
494 * so what we do depends on if there are already values
495 * for the parameter. If there are, we just finish
496 * this parameter and skip past the offending character
497 * (unless it's the ':'). If there aren't values, we free
498 * the parameter then skip past the character.
500 if (!param
->values
) {
501 b_vformat_attribute_param_free (param
);
504 lp
= g_utf8_next_char (lp
);
509 && !g_ascii_strcasecmp (param
->name
, "encoding")) {
510 if (!g_ascii_strcasecmp (param
->values
->data
, "quoted-printable")) {
511 *format_encoding
= VF_ENCODING_QP
;
512 b_vformat_attribute_param_free (param
);
514 } else if ( _helper_is_base64(param
->values
->data
)) {
515 *format_encoding
= VF_ENCODING_BASE64
;
516 // b_vformat_attribute_param_free (param);
519 } else if (param
&& !g_ascii_strcasecmp(param
->name
, "charset")) {
520 *charset
= g_string_new(param
->values
->data
);
521 b_vformat_attribute_param_free (param
);
528 if (!g_ascii_strcasecmp (str
->str
,
529 "quoted-printable")) {
530 param_name
= "ENCODING";
531 *format_encoding
= VF_ENCODING_QP
;
533 /* apple's broken addressbook app outputs naked BASE64
534 parameters, which aren't even vcard 3.0 compliant. */
535 else if (!g_ascii_strcasecmp (str
->str
,
537 param_name
= "ENCODING";
538 g_string_assign (str
, "b");
539 *format_encoding
= VF_ENCODING_BASE64
;
546 param
= b_vformat_attribute_param_new (param_name
);
547 b_vformat_attribute_param_add_value (param
, str
->str
);
549 g_string_assign (str
, "");
551 lp
= g_utf8_next_char (lp
);
554 /* we've got an attribute with a truly empty
555 attribute parameter. So it's of the form:
557 ATTR;[PARAM=value;]*;[PARAM=value;]*:
561 the only thing to do here is, well.. nothing.
562 we skip over the character if it's not a colon,
563 and the rest is handled for us: We'll either
564 continue through the loop again if we hit a ';',
565 or we'll break out correct below if it was a ':' */
567 lp
= g_utf8_next_char (lp
);
570 if (param
&& !comma
) {
571 b_vformat_attribute_add_param (attr
, param
);
578 BarryLogf(TRACE_INTERNAL
, "invalid character found in parameter spec: \"%i\" String so far: %s", lp
[0], str
->str
);
579 g_string_assign (str
, "");
580 _skip_until (&lp
, ":;");
585 g_string_free (str
, TRUE
);
590 /* reads an entire attribute from the input buffer, leaving p pointing
591 at the start of the next line (past the \r\n) */
592 static b_VFormatAttribute
*_read_attribute (char **p
)
594 char *attr_group
= NULL
;
595 char *attr_name
= NULL
;
596 b_VFormatAttribute
*attr
= NULL
;
597 GString
*str
, *charset
= NULL
;
600 gboolean is_qp
= FALSE
;
602 /* first read in the group/name */
603 str
= g_string_new ("");
604 while (*lp
!= '\r' && *lp
!= '\0') {
605 if (*lp
== ':' || *lp
== ';') {
607 /* we've got a name, break out to the value/attribute parsing */
608 attr_name
= g_string_free (str
, FALSE
);
612 /* a line of the form:
615 * since we don't have an attribute
616 * name, skip to the end of the line
619 g_string_free (str
, TRUE
);
621 _skip_to_next_line(p
);
625 else if (*lp
== '.') {
627 BarryLogf(TRACE_INTERNAL
, "extra `.' in attribute specification. ignoring extra group `%s'", str
->str
);
628 g_string_free (str
, TRUE
);
629 str
= g_string_new ("");
632 attr_group
= g_string_free (str
, FALSE
);
633 str
= g_string_new ("");
636 else if (g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/') {
637 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
640 BarryLogf(TRACE_INTERNAL
, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp
[0], str
->str
);
641 g_string_free (str
, TRUE
);
643 _skip_to_next_line(p
);
647 lp
= g_utf8_next_char(lp
);
651 _skip_to_next_line (p
);
655 attr
= b_vformat_attribute_new (attr_group
, attr_name
);
660 /* skip past the ';' */
661 lp
= g_utf8_next_char(lp
);
662 _read_attribute_params (attr
, &lp
, &is_qp
, &charset
);
665 /* skip past the ':' */
666 lp
= g_utf8_next_char(lp
);
667 _read_attribute_value (attr
, &lp
, is_qp
, charset
);
670 if (charset
) g_string_free(charset
, TRUE
);
679 b_vformat_attribute_free (attr
);
683 static void open_block(char **block
, const char *block_name
)
685 char *start
= *block
? *block
: "";
688 result
= g_strconcat(start
, "/", block_name
, NULL
);
694 static void close_block(char **block
, const char *block_name
)
696 int name_len
= strlen(block_name
);
697 int block_len
= *block
? strlen(*block
) : 0;
698 char *cmp_start
= NULL
;
700 if( block_len
< name_len
+ 1 )
703 cmp_start
= *block
+ (block_len
- name_len
- 1);
704 if( cmp_start
[0] == '/' &&
705 g_ascii_strcasecmp(cmp_start
+1, block_name
) == 0 )
707 // end of block hierarchy contains block name,
710 // cut off the end of the string... no need to free/realloc
715 /* we try to be as forgiving as we possibly can here - this isn't a
716 * validator. Almost nothing is considered a fatal error. We always
717 * try to return *something*.
719 static void _parse(b_VFormat
*evc
, const char *str
)
721 char *buf
= g_strdup (str
);
723 b_VFormatAttribute
*attr
;
725 /* first validate the string is valid utf8 */
726 if (!g_utf8_validate (buf
, -1, (const char **)&end
)) {
727 /* if the string isn't valid, we parse as much as we can from it */
728 BarryLogf(TRACE_INTERNAL
, "invalid utf8 passed to b_VFormat. Limping along.");
732 buf
= _fold_lines (buf
);
736 attr
= _read_attribute (&p
);
738 attr
= _read_attribute (&p
);
740 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "begin")) {
741 BarryLogf(TRACE_INTERNAL
, "vformat began without a BEGIN\n");
743 if (attr
&& !g_ascii_strcasecmp (attr
->name
, "begin"))
744 b_vformat_attribute_free (attr
);
746 b_vformat_add_attribute (evc
, attr
);
750 b_VFormatAttribute
*next_attr
= _read_attribute (&p
);
753 if( g_ascii_strcasecmp(next_attr
->name
, "begin") == 0 ) {
754 // add to block hierarchy string
755 char *value
= b_vformat_attribute_get_value(next_attr
);
756 open_block(&block
, value
);
757 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
760 else if( g_ascii_strcasecmp(next_attr
->name
, "end") == 0 ) {
761 // close off the block
762 char *value
= b_vformat_attribute_get_value(next_attr
);
763 close_block(&block
, value
);
764 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
768 // apply the block to the attr
769 next_attr
->block
= g_strdup(block
);
772 b_vformat_add_attribute (evc
, next_attr
);
777 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "end")) {
778 BarryLogf(TRACE_INTERNAL
, "vformat ended without END");
785 char *b_vformat_escape_string (const char *s
, b_VFormatType type
)
790 str
= g_string_new ("");
792 /* Escape a string as described in RFC2426, section 5 */
793 for (p
= s
; p
&& *p
; p
++) {
796 str
= g_string_append (str
, "\\n");
801 str
= g_string_append (str
, "\\n");
804 str
= g_string_append (str
, "\\;");
807 if (type
== VFORMAT_CARD_30
|| type
== VFORMAT_EVENT_20
|| type
== VFORMAT_TODO_20
)
808 str
= g_string_append (str
, "\\,");
810 str
= g_string_append_c (str
, *p
);
814 * We won't escape backslashes
815 * on vcard 2.1, unless it is in the end of a value.
816 * See comments above for a better explanation
818 if (*p
!= '\0' && type
== VFORMAT_CARD_21
) {
819 BarryLogf(TRACE_INTERNAL
, "[%s]We won't escape backslashes", __func__
);
820 str
= g_string_append_c(str
, *p
);
823 BarryLogf(TRACE_INTERNAL
, "[%s] escape backslashes!!", __func__
);
824 str
= g_string_append (str
, "\\\\");
828 str
= g_string_append_c (str
, *p
);
833 return g_string_free (str
, FALSE
);
837 b_vformat_unescape_string (const char *s
)
842 g_return_val_if_fail (s
!= NULL
, NULL
);
844 str
= g_string_new ("");
846 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
847 for (p
= s
; *p
; p
++) {
851 str
= g_string_append_c (str
, '\\');
855 case 'n': str
= g_string_append_c (str
, '\n'); break;
856 case 'r': str
= g_string_append_c (str
, '\r'); break;
857 case ';': str
= g_string_append_c (str
, ';'); break;
858 case ',': str
= g_string_append_c (str
, ','); break;
859 case '\\': str
= g_string_append_c (str
, '\\'); break;
860 case '"': str
= g_string_append_c (str
, '"'); break;
861 /* \t is (incorrectly) used by kOrganizer, so handle it here */
862 case 't': str
= g_string_append_c (str
, '\t'); break;
864 BarryLogf(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p
);
865 str
= g_string_append_c (str
, '\\');
866 str
= g_string_append_unichar (str
, g_utf8_get_char(p
));
872 return g_string_free (str
, FALSE
);
876 b_vformat_construct (b_VFormat
*evc
, const char *str
)
878 g_return_if_fail (str
!= NULL
);
884 void b_vformat_free(b_VFormat
*format
)
886 g_list_foreach (format
->attributes
, (GFunc
)b_vformat_attribute_free
, NULL
);
887 g_list_free (format
->attributes
);
891 b_VFormat
*b_vformat_new_from_string (const char *str
)
893 g_return_val_if_fail (str
!= NULL
, NULL
);
894 b_VFormat
*evc
= g_malloc0(sizeof(b_VFormat
));
896 b_vformat_construct (evc
, str
);
901 b_VFormat
*b_vformat_new(void)
903 return b_vformat_new_from_string ("");
906 static int _block_match(b_VFormatAttribute
*attr
, const char *block
)
908 // a block matches if the end of the attribute's block
909 // string matches a case insensitive compare with block
911 // for example, a calendar may or may not start with a
912 // BEGIN: VCALENDAR, so DTSTART's block string could be
913 // "/vcalendar/vevent" or just "/vevent". By passing
914 // "/vevent" or even "vevent" as the block argument above,
915 // we should get a match for any of the above.
917 int attr_len
= attr
->block
? strlen(attr
->block
) : 0;
918 int block_len
= block
? strlen(block
) : 0;
921 return 1; // if block is null, match everything
923 if( attr_len
< block_len
)
924 return 0; // not enough string to compare
926 if( attr_len
== 0 && block_len
== 0 )
927 return 1; // empty and null strings match
929 if( attr
->block
== NULL
)
930 return 0; // don't compare if one side is null
932 return g_ascii_strcasecmp(&attr
->block
[attr_len
- block_len
], block
) == 0;
935 b_VFormatAttribute
*b_vformat_find_attribute(b_VFormat
*vcard
, const char *name
, int nth
, const char *block
)
937 GList
*attributes
= b_vformat_get_attributes(vcard
);
940 for (a
= attributes
; a
; a
= a
->next
) {
941 b_VFormatAttribute
*attr
= a
->data
;
942 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr
), name
)) {
943 if( block
== NULL
|| _block_match(attr
, block
) ) {
954 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
958 GList *attributes = last ? last->next : 0;
961 for (a = attributes; a; a = a->next) {
962 b_VFormatAttribute *attr = a->data;
963 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
973 char *b_vformat_to_string (b_VFormat
*evc
, b_VFormatType type
)
975 BarryLogf(TRACE_ENTRY
, "%s(%p, %i)", __func__
, evc
, type
);
979 GString
*str
= g_string_new ("");
982 case VFORMAT_CARD_21
:
983 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
985 case VFORMAT_CARD_30
:
986 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
988 case VFORMAT_TODO_10
:
989 case VFORMAT_EVENT_10
:
990 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
992 case VFORMAT_TODO_20
:
993 case VFORMAT_EVENT_20
:
994 case VFORMAT_JOURNAL
:
995 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
998 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
1002 for (l
= evc
->attributes
; l
; l
= l
->next
) {
1004 b_VFormatAttribute
*attr
= l
->data
;
1007 int format_encoding
= VF_ENCODING_RAW
;
1009 attr_str
= g_string_new ("");
1011 /* From rfc2425, 5.8.2
1013 * contentline = [group "."] name *(";" param) ":" value CRLF
1017 attr_str
= g_string_append (attr_str
, attr
->group
);
1018 attr_str
= g_string_append_c (attr_str
, '.');
1020 attr_str
= g_string_append (attr_str
, attr
->name
);
1021 /* handle the parameters */
1022 for (p
= attr
->params
; p
; p
= p
->next
) {
1023 b_VFormatParam
*param
= p
->data
;
1025 * param = param-name "=" param-value *("," param-value)
1027 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
1028 || type
== VFORMAT_EVENT_20
|| type
== VFORMAT_JOURNAL
) {
1031 * Character set can only be specified on the CHARSET
1032 * parameter on the Content-Type MIME header field.
1034 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
1036 attr_str
= g_string_append_c (attr_str
, ';');
1037 attr_str
= g_string_append (attr_str
, param
->name
);
1038 if (param
->values
) {
1039 attr_str
= g_string_append_c (attr_str
, '=');
1041 for (v
= param
->values
; v
; v
= v
->next
) {
1042 if (_helper_is_base64((const char *) v
->data
)) {
1043 format_encoding
= VF_ENCODING_BASE64
;
1044 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1045 v
->data
=g_strdup("B");
1048 * QUOTED-PRINTABLE inline encoding has been
1051 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
1052 BarryLogf(TRACE_ERROR
, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__
);
1053 format_encoding
= VF_ENCODING_QP
;
1055 attr_str
= g_string_append (attr_str
, v
->data
);
1058 attr_str
= g_string_append_c (attr_str
, ',');
1062 attr_str
= g_string_append_c (attr_str
, ';');
1064 * The "TYPE=" is optional skip it.
1065 * LOGO, PHOTO and SOUND multimedia formats MUST
1066 * have a "TYPE=" parameter
1068 gboolean must_have_type
= FALSE
;
1069 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
1070 must_have_type
= TRUE
;
1071 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
1072 attr_str
= g_string_append (attr_str
, param
->name
);
1073 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
1074 attr_str
= g_string_append_c (attr_str
, '=');
1075 for (v
= param
->values
; v
; v
= v
->next
) {
1076 // check for quoted-printable encoding
1077 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
1078 format_encoding
= VF_ENCODING_QP
;
1079 // check for base64 encoding
1080 if (_helper_is_base64((const char *) v
->data
)) {
1081 format_encoding
= VF_ENCODING_BASE64
;
1082 v
->data
=g_strdup("BASE64");
1084 attr_str
= g_string_append (attr_str
, v
->data
);
1086 attr_str
= g_string_append_c (attr_str
, ',');
1091 attr_str
= g_string_append_c (attr_str
, ':');
1093 for (v
= attr
->values
; v
; v
= v
->next
) {
1094 char *value
= v
->data
;
1095 char *escaped_value
= NULL
;
1097 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
1098 strstr (value
, "BYDAY") == v
->data
) {
1099 attr_str
= g_string_append (attr_str
, value
);
1101 escaped_value
= b_vformat_escape_string (value
, type
);
1102 attr_str
= g_string_append (attr_str
, escaped_value
);
1107 /* XXX toshok - i hate you, rfc 2426.
1108 why doesn't CATEGORIES use a ; like
1109 a normal list attribute? */
1110 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1111 attr_str
= g_string_append_c (attr_str
, ',');
1113 attr_str
= g_string_append_c (attr_str
, ';');
1116 g_free (escaped_value
);
1122 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1123 * After generating a content line,
1124 * lines longer than 75 characters SHOULD be folded according to the
1125 * folding procedure described in [MIME-DIR].
1127 * rfc 2445 (iCalendar), 4.1 Content Lines:
1128 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1129 * break. Long content lines SHOULD be split into a multiple line
1130 * representations using a line "folding" technique. That is, a long
1131 * line can be split between any two characters by inserting a CRLF
1132 * immediately followed by a single linear white space character (i.e.,
1133 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1134 * of CRLF followed immediately by a single linear white space character
1135 * is ignored (i.e., removed) when processing the content type.
1137 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1138 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1140 * Differences between encodings:
1141 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1143 * rfc 2425 [MIME-DIR], 5.8.1:
1144 * A logical line MAY be continued on the next physical line anywhere
1145 * between two characters by inserting a CRLF immediately followed by a
1146 * single <WS> (white space) character.
1148 * rfc 2045, 6.7, chapter 5:
1149 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1150 * without follwing <WS>
1155 * Note that all the line folding above is described in terms of characters
1156 * not bytes. In particular, it would be an error to put a line break
1157 * within a UTF-8 character.
1162 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1165 /* If using QP, must be sure that we do not fold within a quote sequence */
1166 if (format_encoding
== VF_ENCODING_QP
) {
1167 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1168 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1171 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1173 if (format_encoding
== VF_ENCODING_QP
)
1174 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1176 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1180 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1182 attr_str
= g_string_append (attr_str
, CRLF
);
1184 * base64= <MIME RFC 1521 base64 text>
1185 * the end of the text is marked with two CRLF sequences
1186 * this results in one blank line before the start of the
1189 if( format_encoding
== VF_ENCODING_BASE64
1190 && (type
== VFORMAT_CARD_21
))
1191 attr_str
= g_string_append (attr_str
, CRLF
);
1193 str
= g_string_append (str
, attr_str
->str
);
1194 g_string_free (attr_str
, TRUE
);
1198 case VFORMAT_CARD_21
:
1199 str
= g_string_append (str
, "END:VCARD\r\n");
1201 case VFORMAT_CARD_30
:
1202 str
= g_string_append (str
, "END:VCARD\r\n");
1204 case VFORMAT_TODO_10
:
1205 case VFORMAT_EVENT_10
:
1206 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1208 case VFORMAT_TODO_20
:
1209 case VFORMAT_EVENT_20
:
1210 case VFORMAT_JOURNAL
:
1211 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1214 str
= g_string_append (str
, "END:VNOTE\r\n");
1218 BarryLogf(TRACE_EXIT
, "%s", __func__
);
1219 return g_string_free (str
, FALSE
);
1222 void b_vformat_dump_structure (b_VFormat
*evc
)
1228 printf ("b_VFormat\n");
1229 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1231 b_VFormatAttribute
*attr
= a
->data
;
1232 printf ("+-- %s\n", attr
->name
);
1234 printf (" +- params=\n");
1236 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1237 b_VFormatParam
*param
= p
->data
;
1238 printf (" | [%d] = %s", i
,param
->name
);
1240 for (v
= param
->values
; v
; v
= v
->next
) {
1241 char *value
= b_vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1242 printf ("%s", value
);
1251 printf (" +- values=\n");
1252 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1253 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1258 b_VFormatAttribute
*b_vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1260 b_VFormatAttribute
*attr
;
1262 attr
= g_new0 (b_VFormatAttribute
, 1);
1264 attr
->group
= g_strdup (attr_group
);
1265 attr
->name
= g_strdup (attr_name
);
1271 b_vformat_attribute_free (b_VFormatAttribute
*attr
)
1273 g_return_if_fail (attr
!= NULL
);
1275 g_free (attr
->block
);
1276 g_free (attr
->group
);
1277 g_free (attr
->name
);
1279 b_vformat_attribute_remove_values (attr
);
1281 b_vformat_attribute_remove_params (attr
);
1287 b_vformat_attribute_copy (b_VFormatAttribute
*attr
)
1289 b_VFormatAttribute
*a
;
1292 g_return_val_if_fail (attr
!= NULL
, NULL
);
1294 a
= b_vformat_attribute_new (b_vformat_attribute_get_group (attr
),
1295 b_vformat_attribute_get_name (attr
));
1297 for (p
= attr
->values
; p
; p
= p
->next
)
1298 b_vformat_attribute_add_value (a
, p
->data
);
1300 for (p
= attr
->params
; p
; p
= p
->next
)
1301 b_vformat_attribute_add_param (a
, b_vformat_attribute_param_copy (p
->data
));
1307 b_vformat_remove_attributes (b_VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1311 g_return_if_fail (attr_name
!= NULL
);
1313 attr
= evc
->attributes
;
1316 b_VFormatAttribute
*a
= attr
->data
;
1318 next_attr
= attr
->next
;
1320 if (((!attr_group
&& !a
->group
) ||
1321 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1322 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1324 /* matches, remove/delete the attribute */
1325 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1327 b_vformat_attribute_free (a
);
1335 b_vformat_remove_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1337 g_return_if_fail (attr
!= NULL
);
1339 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1340 b_vformat_attribute_free (attr
);
1344 b_vformat_add_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1346 g_return_if_fail (attr
!= NULL
);
1348 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1352 b_vformat_add_attribute_with_value (b_VFormat
*b_VFormat
,
1353 b_VFormatAttribute
*attr
, const char *value
)
1355 g_return_if_fail (attr
!= NULL
);
1357 b_vformat_attribute_add_value (attr
, value
);
1359 b_vformat_add_attribute (b_VFormat
, attr
);
1363 b_vformat_add_attribute_with_values (b_VFormat
*b_VFormat
, b_VFormatAttribute
*attr
, ...)
1368 g_return_if_fail (attr
!= NULL
);
1370 va_start (ap
, attr
);
1372 while ((v
= va_arg (ap
, char*))) {
1373 b_vformat_attribute_add_value (attr
, v
);
1378 b_vformat_add_attribute (b_VFormat
, attr
);
1382 b_vformat_attribute_add_value (b_VFormatAttribute
*attr
, const char *value
)
1384 g_return_if_fail (attr
!= NULL
);
1386 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1390 b_vformat_attribute_add_value_decoded (b_VFormatAttribute
*attr
, const char *value
, int len
)
1392 g_return_if_fail (attr
!= NULL
);
1394 switch (attr
->encoding
) {
1395 case VF_ENCODING_RAW
:
1396 BarryLogf(TRACE_INTERNAL
, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1398 case VF_ENCODING_BASE64
: {
1399 char *b64_data
= base64_encode_simple (value
, len
);
1400 GString
*decoded
= g_string_new_len (value
, len
);
1402 /* make sure the decoded list is up to date */
1403 b_vformat_attribute_get_values_decoded (attr
);
1405 attr
->values
= g_list_append (attr
->values
, b64_data
);
1406 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1409 case VF_ENCODING_QP
: {
1410 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1411 GString
*decoded
= g_string_new (value
);
1413 /* make sure the decoded list is up to date */
1414 b_vformat_attribute_get_values_decoded (attr
);
1416 attr
->values
= g_list_append (attr
->values
, qp_data
);
1417 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1420 case VF_ENCODING_8BIT
: {
1421 char *data
= g_strdup(value
);
1422 GString
*decoded
= g_string_new (value
);
1424 /* make sure the decoded list is up to date */
1425 b_vformat_attribute_get_values_decoded (attr
);
1427 attr
->values
= g_list_append (attr
->values
, data
);
1428 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1435 b_vformat_attribute_add_values (b_VFormatAttribute
*attr
, ...)
1440 g_return_if_fail (attr
!= NULL
);
1442 va_start (ap
, attr
);
1444 while ((v
= va_arg (ap
, char*))) {
1445 b_vformat_attribute_add_value (attr
, v
);
1452 free_gstring (GString
*str
)
1454 g_string_free (str
, TRUE
);
1458 b_vformat_attribute_remove_values (b_VFormatAttribute
*attr
)
1460 g_return_if_fail (attr
!= NULL
);
1462 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1463 g_list_free (attr
->values
);
1464 attr
->values
= NULL
;
1466 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1467 g_list_free (attr
->decoded_values
);
1468 attr
->decoded_values
= NULL
;
1472 b_vformat_attribute_remove_params (b_VFormatAttribute
*attr
)
1474 g_return_if_fail (attr
!= NULL
);
1476 g_list_foreach (attr
->params
, (GFunc
)b_vformat_attribute_param_free
, NULL
);
1477 g_list_free (attr
->params
);
1478 attr
->params
= NULL
;
1480 /* also remove the cached encoding on this attribute */
1481 attr
->encoding_set
= FALSE
;
1482 attr
->encoding
= VF_ENCODING_RAW
;
1486 b_vformat_attribute_param_new (const char *name
)
1488 b_VFormatParam
*param
= g_new0 (b_VFormatParam
, 1);
1489 param
->name
= g_strdup (name
);
1495 b_vformat_attribute_param_free (b_VFormatParam
*param
)
1497 g_return_if_fail (param
!= NULL
);
1499 g_free (param
->name
);
1501 b_vformat_attribute_param_remove_values (param
);
1507 b_vformat_attribute_param_copy (b_VFormatParam
*param
)
1512 g_return_val_if_fail (param
!= NULL
, NULL
);
1514 p
= b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param
));
1516 for (l
= param
->values
; l
; l
= l
->next
) {
1517 b_vformat_attribute_param_add_value (p
, l
->data
);
1524 b_vformat_attribute_add_param (b_VFormatAttribute
*attr
,
1525 b_VFormatParam
*param
)
1527 g_return_if_fail (attr
!= NULL
);
1528 g_return_if_fail (param
!= NULL
);
1530 attr
->params
= g_list_append (attr
->params
, param
);
1532 /* we handle our special encoding stuff here */
1534 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1535 if (attr
->encoding_set
) {
1536 BarryLogf(TRACE_INTERNAL
, "ENCODING specified twice");
1540 if (param
->values
&& param
->values
->data
) {
1541 if (_helper_is_base64((const char*)param
->values
->data
))
1542 attr
->encoding
= VF_ENCODING_BASE64
;
1543 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1544 attr
->encoding
= VF_ENCODING_QP
;
1545 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1546 attr
->encoding
= VF_ENCODING_8BIT
;
1548 BarryLogf(TRACE_INTERNAL
, "Unknown value `%s' for ENCODING parameter. values will be treated as raw", (char*)param
->values
->data
);
1551 attr
->encoding_set
= TRUE
;
1554 BarryLogf(TRACE_INTERNAL
, "ENCODING parameter added with no value");
1559 b_VFormatParam
*b_vformat_attribute_find_param(b_VFormatAttribute
*attr
, const char *name
, int level
)
1561 g_return_val_if_fail (attr
!= NULL
, NULL
);
1563 for (p
= attr
->params
; p
; p
= p
->next
) {
1564 b_VFormatParam
*param
= p
->data
;
1565 if (!g_ascii_strcasecmp (param
->name
, name
)) {
1576 b_vformat_attribute_set_value (b_VFormatAttribute
*attr
,
1577 int nth
, const char *value
)
1579 GList
*param
= g_list_nth(attr
->values
, nth
);
1580 g_free(param
->data
);
1581 param
->data
= g_strdup(value
);
1585 b_vformat_attribute_param_add_value (b_VFormatParam
*param
,
1588 g_return_if_fail (param
!= NULL
);
1590 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1594 b_vformat_attribute_param_add_values (b_VFormatParam
*param
,
1600 g_return_if_fail (param
!= NULL
);
1602 va_start (ap
, param
);
1604 while ((v
= va_arg (ap
, char*))) {
1605 b_vformat_attribute_param_add_value (param
, v
);
1612 b_vformat_attribute_add_param_with_value (b_VFormatAttribute
*attr
, const char *name
, const char *value
)
1614 g_return_if_fail (attr
!= NULL
);
1615 g_return_if_fail (name
!= NULL
);
1620 b_VFormatParam
*param
= b_vformat_attribute_param_new(name
);
1622 b_vformat_attribute_param_add_value (param
, value
);
1624 b_vformat_attribute_add_param (attr
, param
);
1628 b_vformat_attribute_add_param_with_values (b_VFormatAttribute
*attr
,
1629 b_VFormatParam
*param
, ...)
1634 g_return_if_fail (attr
!= NULL
);
1635 g_return_if_fail (param
!= NULL
);
1637 va_start (ap
, param
);
1639 while ((v
= va_arg (ap
, char*))) {
1640 b_vformat_attribute_param_add_value (param
, v
);
1645 b_vformat_attribute_add_param (attr
, param
);
1649 b_vformat_attribute_param_remove_values (b_VFormatParam
*param
)
1651 g_return_if_fail (param
!= NULL
);
1653 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1654 g_list_free (param
->values
);
1655 param
->values
= NULL
;
1659 b_vformat_get_attributes (b_VFormat
*format
)
1661 return format
->attributes
;
1665 b_vformat_attribute_get_group (b_VFormatAttribute
*attr
)
1667 g_return_val_if_fail (attr
!= NULL
, NULL
);
1673 b_vformat_attribute_get_name (b_VFormatAttribute
*attr
)
1675 g_return_val_if_fail (attr
!= NULL
, NULL
);
1681 b_vformat_attribute_get_block (b_VFormatAttribute
*attr
)
1683 g_return_val_if_fail (attr
!= NULL
, NULL
);
1689 b_vformat_attribute_get_values (b_VFormatAttribute
*attr
)
1691 g_return_val_if_fail (attr
!= NULL
, NULL
);
1693 return attr
->values
;
1697 b_vformat_attribute_get_values_decoded (b_VFormatAttribute
*attr
)
1699 g_return_val_if_fail (attr
!= NULL
, NULL
);
1701 if (!attr
->decoded_values
) {
1703 switch (attr
->encoding
) {
1704 case VF_ENCODING_RAW
:
1705 case VF_ENCODING_8BIT
:
1706 for (l
= attr
->values
; l
; l
= l
->next
)
1707 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1709 case VF_ENCODING_BASE64
:
1710 for (l
= attr
->values
; l
; l
= l
->next
) {
1711 char *decoded
= g_strdup ((char*)l
->data
);
1712 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1713 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1717 case VF_ENCODING_QP
:
1718 for (l
= attr
->values
; l
; l
= l
->next
) {
1721 char *decoded
= g_strdup ((char*)l
->data
);
1722 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1723 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1730 return attr
->decoded_values
;
1734 b_vformat_attribute_is_single_valued (b_VFormatAttribute
*attr
)
1736 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1738 if (attr
->values
== NULL
1739 || attr
->values
->next
!= NULL
)
1746 b_vformat_attribute_get_value (b_VFormatAttribute
*attr
)
1750 g_return_val_if_fail (attr
!= NULL
, NULL
);
1752 values
= b_vformat_attribute_get_values (attr
);
1754 if (!b_vformat_attribute_is_single_valued (attr
))
1755 BarryLogf(TRACE_INTERNAL
, "b_vformat_attribute_get_value called on multivalued attribute");
1757 return values
? g_strdup ((char*)values
->data
) : NULL
;
1761 b_vformat_attribute_get_value_decoded (b_VFormatAttribute
*attr
)
1764 GString
*str
= NULL
;
1766 g_return_val_if_fail (attr
!= NULL
, NULL
);
1768 values
= b_vformat_attribute_get_values_decoded (attr
);
1770 if (!b_vformat_attribute_is_single_valued (attr
))
1771 BarryLogf(TRACE_INTERNAL
, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
1776 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1779 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute
*attr
, int nth
)
1781 GList
*values
= b_vformat_attribute_get_values_decoded(attr
);
1784 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1788 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1789 values
= b_vformat_attribute_get_values(attr
);
1792 return g_list_nth_data(values
, nth
);
1799 b_vformat_attribute_has_type (b_VFormatAttribute
*attr
, const char *typestr
)
1804 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1805 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1807 params
= b_vformat_attribute_get_params (attr
);
1809 for (p
= params
; p
; p
= p
->next
) {
1810 b_VFormatParam
*param
= p
->data
;
1812 if (!strcasecmp (b_vformat_attribute_param_get_name (param
), "TYPE")) {
1813 GList
*values
= b_vformat_attribute_param_get_values (param
);
1816 for (v
= values
; v
; v
= v
->next
) {
1817 if (!strcasecmp ((char*)v
->data
, typestr
))
1827 gboolean
b_vformat_attribute_has_param(b_VFormatAttribute
*attr
, const char *name
)
1829 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1830 g_return_val_if_fail (name
!= NULL
, FALSE
);
1832 GList
*params
= b_vformat_attribute_get_params(attr
);
1834 for (p
= params
; p
; p
= p
->next
) {
1835 b_VFormatParam
*param
= p
->data
;
1836 if (!strcasecmp(name
, b_vformat_attribute_param_get_name(param
)))
1843 b_vformat_attribute_get_params (b_VFormatAttribute
*attr
)
1845 g_return_val_if_fail (attr
!= NULL
, NULL
);
1847 return attr
->params
;
1851 b_vformat_attribute_param_get_name (b_VFormatParam
*param
)
1853 g_return_val_if_fail (param
!= NULL
, NULL
);
1859 b_vformat_attribute_param_get_values (b_VFormatParam
*param
)
1861 g_return_val_if_fail (param
!= NULL
, NULL
);
1863 return param
->values
;
1866 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam
*param
, int nth
)
1868 const char *ret
= NULL
;
1869 GList
*values
= b_vformat_attribute_param_get_values(param
);
1872 ret
= g_list_nth_data(values
, nth
);
1876 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1878 //static unsigned char _evc_base64_rank[256];
1880 static void base64_init(char *rank
)
1884 memset(rank
, 0xff, sizeof(rank
));
1885 for (i
=0;i
<64;i
++) {
1886 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1891 /* call this when finished encoding everything, to
1892 flush off the last little bit */
1893 static size_t base64_encode_close(const unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1896 unsigned char *outptr
= out
;
1899 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1901 c1
= ((unsigned char *)save
)[1];
1902 c2
= ((unsigned char *)save
)[2];
1904 switch (((char *)save
)[0]) {
1906 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1907 g_assert(outptr
[2] != 0);
1912 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1913 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1928 performs an 'encode step', only encodes blocks of 3 characters to the
1929 output at a time, saves left-over state in state and save (initialise to
1930 0 on first invocation).
1932 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1934 register const unsigned char *inptr
;
1935 register unsigned char *outptr
;
1943 if (len
+ ((char *)save
)[0] > 2) {
1944 const unsigned char *inend
= in
+len
-2;
1945 register int c1
, c2
, c3
;
1946 register int already
;
1950 switch (((char *)save
)[0]) {
1951 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1952 case 2: c1
= ((unsigned char *)save
)[1];
1953 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1956 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1957 while (inptr
< inend
) {
1963 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1964 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1965 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1966 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1967 /* this is a bit ugly ... */
1968 if (break_lines
&& (++already
)>=19) {
1974 ((char *)save
)[0] = 0;
1975 len
= 2-(inptr
-inend
);
1980 register char *saveout
;
1982 /* points to the slot for the next char to save */
1983 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1985 /* len can only be 0 1 or 2 */
1987 case 2: *saveout
++ = *inptr
++;
1988 case 1: *saveout
++ = *inptr
++;
1990 ((char *)save
)[0]+=len
;
1998 * base64_decode_step: decode a chunk of base64 encoded data
2000 * @len: max length of data to decode
2001 * @out: output stream
2002 * @state: holds the number of bits that are stored in @save
2003 * @save: leftover bits that have not yet been decoded
2005 * Decodes a chunk of base64 encoded data
2007 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
2009 unsigned char base64_rank
[256];
2010 base64_init((char*)base64_rank
);
2012 register const unsigned char *inptr
;
2013 register unsigned char *outptr
;
2014 const unsigned char *inend
;
2016 register unsigned int v
;
2022 /* convert 4 base64 bytes to 3 normal bytes */
2026 while (inptr
<inend
) {
2027 c
= base64_rank
[*inptr
++];
2043 /* quick scan back for '=' on the end somewhere */
2044 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2046 while (inptr
>in
&& i
) {
2048 if (base64_rank
[*inptr
] != 0xff) {
2049 if (*inptr
== '=' && outptr
>out
)
2055 /* if i!= 0 then there is a truncation error! */
2059 static char *base64_encode_simple (const char *data
, size_t len
)
2062 int state
= 0, outlen
;
2063 unsigned int save
= 0;
2065 g_return_val_if_fail (data
!= NULL
, NULL
);
2067 out
= g_malloc (len
* 4 / 3 + 5);
2068 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
2069 out
, &state
, (int*)&save
);
2074 static size_t base64_decode_simple (char *data
, size_t len
)
2077 unsigned int save
= 0;
2079 g_return_val_if_fail (data
!= NULL
, 0);
2081 return base64_decode_step ((unsigned char *)data
, len
,
2082 (unsigned char *)data
, &state
, &save
);
2085 static char *quoted_encode_simple(const unsigned char *string
, int len
)
2087 GString
*tmp
= g_string_new("");
2090 while(string
[i
] != 0) {
2091 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
2092 g_string_append_printf(tmp
, "=%02X", string
[i
]);
2094 g_string_append_c(tmp
, string
[i
]);
2099 char *ret
= tmp
->str
;
2100 g_string_free(tmp
, FALSE
);
2105 static size_t quoted_decode_simple (char *data
, size_t len
)
2107 g_return_val_if_fail (data
!= NULL
, 0);
2109 GString
*string
= g_string_new(data
);
2117 //Get the index of the next encoded char
2118 int i
= strcspn(string
->str
, "=");
2119 if (i
>= strlen(string
->str
))
2123 strncat(hex
, &string
->str
[i
+ 1], 2);
2124 char rep
= ((int)(strtod(hex
, NULL
)));
2125 g_string_erase(string
, i
, 2);
2126 g_string_insert_c(string
, i
, rep
);
2129 memset(data
, 0, strlen(data
));
2130 strcpy(data
, string
->str
);
2131 g_string_free(string
, 1);
2133 return strlen(data
);