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)
27 //#ifdef HAVE_CONFIG_H
36 //#include <opensync/opensync.h>
38 #define TRACE_INTERNAL 1
43 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
);
44 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
);
45 static size_t base64_decode_simple (char *data
, size_t len
);
46 static char *base64_encode_simple (const char *data
, size_t len
);
48 static size_t quoted_decode_simple (char *data
, size_t len
);
49 static char *quoted_encode_simple (const unsigned char *string
, int len
);
53 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
54 * @param check_string string that should be compared with "b" or "base64"
55 * @return 0 if check_string is not base64 and 1 if it is
57 static int _helper_is_base64(const char *check_string
)
59 if(!g_ascii_strcasecmp ((char *) check_string
, "BASE64") ||
60 !g_ascii_strcasecmp ((char *) check_string
, "b") )
65 time_t b_vformat_time_to_unix(const char *inptime
)
70 if ((ftime
= g_strrstr(inptime
, "T"))) {
72 date
= g_strndup(inptime
, ftime
- inptime
);
74 time
= g_strndup(ftime
+ 1, 8);
76 time
= g_strndup(ftime
+ 1, 6);
78 date
= g_strdup(inptime
);
82 memset(&btime
, 0, sizeof(struct tm
));
85 if (strlen(date
) == 10) {
86 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111 - 1900;
87 btime
.tm_mon
= date
[5] * 10 + date
[6] - '0' * 11 - 1;
88 btime
.tm_mday
= date
[8] * 10 + date
[9] - '0' * 11;
90 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111- 1900;
91 btime
.tm_mon
= date
[4] * 10 + date
[5] - '0' * 11 - 1;
92 btime
.tm_mday
= date
[6] * 10 + date
[7] - '0' * 11;
95 if (time
&& strlen(time
) == 8) {
97 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
98 btime
.tm_min
= time
[3] * 10 + time
[4] - '0' * 11;
99 btime
.tm_sec
= time
[6] * 10 + time
[7] - '0' * 11;
100 } else if (time
&& strlen(time
) == 6) {
101 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
102 btime
.tm_min
= time
[2] * 10 + time
[3] - '0' * 11;
103 btime
.tm_sec
= time
[4] * 10 + time
[5] - '0' * 11;
106 time_t utime
= mktime(&btime
);
110 static char *_unfold_lines (char *buf
)
112 GString
*str
= g_string_new ("");
113 GString
*line
= g_string_new ("");
115 char *next
, *next2
, *q
;
116 gboolean newline
= TRUE
;
117 gboolean quotedprintable
= FALSE
;
120 * We're pretty liberal with line folding here. We handle
121 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
122 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
127 /* search new lines for quoted printable encoding */
129 for (q
=p
; *q
!= '\n' && *q
!= '\0'; q
++)
130 line
= g_string_append_unichar (line
, g_utf8_get_char (q
));
132 if (strstr(line
->str
, "ENCODING=QUOTED-PRINTABLE"))
133 quotedprintable
= TRUE
;
135 g_string_free(line
, TRUE
);
136 line
= g_string_new ("");
142 if ((quotedprintable
&& *p
== '=') || *p
== '\r' || *p
== '\n') {
143 next
= g_utf8_next_char (p
);
144 if (*next
== '\n' || *next
== '\r') {
145 next2
= g_utf8_next_char (next
);
146 if (*next2
== '\n' || *next2
== '\r' || *next2
== ' ' || *next2
== '\t') {
147 p
= g_utf8_next_char (next2
);
149 else if(quotedprintable
&& (*p
== '=')) {
150 p
= g_utf8_next_char (next
);
153 str
= g_string_append (str
, CRLF
);
154 p
= g_utf8_next_char (next
);
156 quotedprintable
= FALSE
;
159 else if (*p
== '=') {
160 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
161 p
= g_utf8_next_char (p
);
163 else if (*next
== ' ' || *next
== '\t') {
164 p
= g_utf8_next_char (next
);
167 str
= g_string_append (str
, CRLF
);
168 p
= g_utf8_next_char (p
);
170 quotedprintable
= FALSE
;
174 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
175 p
= g_utf8_next_char (p
);
180 g_string_free(line
, TRUE
);
182 return g_string_free (str
, FALSE
);
185 /* skip forward until we hit the CRLF, or \0 */
186 static void _skip_to_next_line (char **p
)
191 while (*lp
!= '\r' && *lp
!= '\0')
192 lp
= g_utf8_next_char (lp
);
195 lp
= g_utf8_next_char (lp
); /* \n */
196 lp
= g_utf8_next_char (lp
); /* start of the next line */
202 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
203 pointing at the character that causes us to stop */
204 static void _skip_until (char **p
, char *s
)
210 while (*lp
!= '\r' && *lp
!= '\0') {
211 gboolean s_matches
= FALSE
;
213 for (ls
= s
; *ls
; ls
= g_utf8_next_char (ls
)) {
214 if (g_utf8_get_char (ls
) == g_utf8_get_char (lp
)) {
228 static void _read_attribute_value_add (b_VFormatAttribute
*attr
, GString
*str
, GString
*charset
)
230 /* don't convert empty strings */
232 b_vformat_attribute_add_value(attr
, str
->str
);
236 char *inbuf
, *outbuf
, *p
;
237 size_t inbytesleft
, outbytesleft
;
240 p
= outbuf
= malloc(str
->len
*2);
241 inbytesleft
= str
->len
;
242 outbytesleft
= str
->len
*2;
246 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
249 cd
= iconv_open("UTF-8", charset
->str
);
251 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
253 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
256 b_vformat_attribute_add_value(attr
, outbuf
);
260 /* hmm, should not happen */
261 b_vformat_attribute_add_value(attr
, str
->str
);
269 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
270 if (g_utf8_validate (inbuf
, -1, NULL
)) {
272 b_vformat_attribute_add_value (attr
, str
->str
);
276 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
277 cd
= iconv_open("UTF-8", "ISO-8859-1");
279 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
281 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
284 b_vformat_attribute_add_value (attr
, outbuf
);
288 b_vformat_attribute_add_value (attr
, str
->str
);
302 static void _read_attribute_value (b_VFormatAttribute
*attr
, char **p
, int format_encoding
, GString
*charset
)
307 /* read in the value */
308 str
= g_string_new ("");
309 while (*lp
!= '\r' && *lp
!= '\0') {
310 if (*lp
== '=' && format_encoding
== VF_ENCODING_QP
) {
311 char a
, b
, x1
=0, x2
=0;
313 if ((a
= *(++lp
)) == '\0') break;
314 if ((b
= *(++lp
)) == '\0') break;
318 /* e.g. ...N=C3=BCrnberg\r\n
331 if (*(++tmplp
) == '\r' && *(++tmplp
) == '\n' && isalnum(*(++tmplp
))) {
338 /* append malformed input, and
340 str
= g_string_append_c(str
, a
);
341 str
= g_string_append_c(str
, b
);
350 if (b
== '\r' && c
== '\n' && isalnum(d
) && isalnum(e
)) {
356 /* append malformed input, and
358 str
= g_string_append_c(str
, a
);
359 str
= g_string_append_c(str
, b
);
363 /* append malformed input, and
365 str
= g_string_append_c(str
, a
);
366 str
= g_string_append_c(str
, b
);
374 c
= (((a
>='a'?a
-'a'+10:a
-'0')&0x0f) << 4)
375 | ((b
>='a'?b
-'a'+10:b
-'0')&0x0f);
377 str
= g_string_append_c (str
, c
);
382 else if (format_encoding
== VF_ENCODING_BASE64
) {
383 if((*lp
!= ' ') && (*lp
!= '\t') )
384 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
385 lp
= g_utf8_next_char(lp
);
387 else if (*lp
== '\\') {
388 /* convert back to the non-escaped version of
390 lp
= g_utf8_next_char(lp
);
392 str
= g_string_append_c (str
, '\\');
396 case 'n': str
= g_string_append_c (str
, '\n'); break;
397 case 'r': str
= g_string_append_c (str
, '\r'); break;
398 case ';': str
= g_string_append_c (str
, ';'); break;
400 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES")) {
401 //We need to handle categories here to work
402 //aroung a bug in evo2
403 _read_attribute_value_add (attr
, str
, charset
);
404 g_string_assign (str
, "");
406 str
= g_string_append_c (str
, ',');
408 case '\\': str
= g_string_append_c (str
, '\\'); break;
409 case '"': str
= g_string_append_c (str
, '"'); break;
410 /* \t is (incorrectly) used by kOrganizer, so handle it here */
411 case 't': str
= g_string_append_c (str
, '\t'); break;
413 BarryLogf(TRACE_INTERNAL
, _("invalid escape, passing it through. escaped char was %u"), (unsigned int)*lp
);
414 str
= g_string_append_c (str
, '\\');
415 str
= g_string_append_unichar (str
, g_utf8_get_char(lp
));
418 lp
= g_utf8_next_char(lp
);
420 else if ((*lp
== ';') ||
421 (*lp
== ',' && !g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))) {
422 _read_attribute_value_add (attr
, str
, charset
);
423 g_string_assign (str
, "");
424 lp
= g_utf8_next_char(lp
);
427 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
428 lp
= g_utf8_next_char(lp
);
432 _read_attribute_value_add (attr
, str
, charset
);
433 g_string_free (str
, TRUE
);
437 lp
= g_utf8_next_char (lp
); /* \n */
438 lp
= g_utf8_next_char (lp
); /* start of the next line */
444 static void _read_attribute_params(b_VFormatAttribute
*attr
, char **p
, int *format_encoding
, GString
**charset
)
448 b_VFormatParam
*param
= NULL
;
449 gboolean in_quote
= FALSE
;
450 str
= g_string_new ("");
452 while (*lp
!= '\0') {
454 in_quote
= !in_quote
;
455 lp
= g_utf8_next_char (lp
);
457 else if (in_quote
|| g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/' || *lp
== '.' || *lp
== ' ') {
458 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
459 lp
= g_utf8_next_char (lp
);
461 /* accumulate until we hit the '=' or ';'. If we hit
462 * a '=' the string contains the parameter name. if
463 * we hit a ';' the string contains the parameter
464 * value and the name is either ENCODING (if value ==
465 * QUOTED-PRINTABLE) or TYPE (in any other case.)
467 else if (*lp
== '=') {
469 param
= b_vformat_attribute_param_new (str
->str
);
470 g_string_assign (str
, "");
471 lp
= g_utf8_next_char (lp
);
474 _skip_until (&lp
, ":;");
476 lp
= g_utf8_next_char (lp
); /* \n */
477 lp
= g_utf8_next_char (lp
); /* start of the next line */
481 lp
= g_utf8_next_char (lp
);
484 else if (*lp
== ';' || *lp
== ':' || *lp
== ',') {
485 gboolean colon
= (*lp
== ':');
486 gboolean comma
= (*lp
== ',');
490 b_vformat_attribute_param_add_value (param
, str
->str
);
491 g_string_assign (str
, "");
493 lp
= g_utf8_next_char (lp
);
496 /* we've got a parameter of the form:
498 * so what we do depends on if there are already values
499 * for the parameter. If there are, we just finish
500 * this parameter and skip past the offending character
501 * (unless it's the ':'). If there aren't values, we free
502 * the parameter then skip past the character.
504 if (!param
->values
) {
505 b_vformat_attribute_param_free (param
);
508 lp
= g_utf8_next_char (lp
);
513 && !g_ascii_strcasecmp (param
->name
, "encoding")) {
514 if (!g_ascii_strcasecmp (param
->values
->data
, "quoted-printable")) {
515 *format_encoding
= VF_ENCODING_QP
;
516 b_vformat_attribute_param_free (param
);
518 } else if ( _helper_is_base64(param
->values
->data
)) {
519 *format_encoding
= VF_ENCODING_BASE64
;
520 // b_vformat_attribute_param_free (param);
523 } else if (param
&& !g_ascii_strcasecmp(param
->name
, "charset")) {
524 *charset
= g_string_new(param
->values
->data
);
525 b_vformat_attribute_param_free (param
);
532 if (!g_ascii_strcasecmp (str
->str
,
533 "quoted-printable")) {
534 param_name
= "ENCODING";
535 *format_encoding
= VF_ENCODING_QP
;
537 /* apple's broken addressbook app outputs naked BASE64
538 parameters, which aren't even vcard 3.0 compliant. */
539 else if (!g_ascii_strcasecmp (str
->str
,
541 param_name
= "ENCODING";
542 g_string_assign (str
, "b");
543 *format_encoding
= VF_ENCODING_BASE64
;
550 param
= b_vformat_attribute_param_new (param_name
);
551 b_vformat_attribute_param_add_value (param
, str
->str
);
553 g_string_assign (str
, "");
555 lp
= g_utf8_next_char (lp
);
558 /* we've got an attribute with a truly empty
559 attribute parameter. So it's of the form:
561 ATTR;[PARAM=value;]*;[PARAM=value;]*:
565 the only thing to do here is, well.. nothing.
566 we skip over the character if it's not a colon,
567 and the rest is handled for us: We'll either
568 continue through the loop again if we hit a ';',
569 or we'll break out correct below if it was a ':' */
571 lp
= g_utf8_next_char (lp
);
574 if (param
&& !comma
) {
575 b_vformat_attribute_add_param (attr
, param
);
582 BarryLogf(TRACE_INTERNAL
, _("invalid character found in parameter spec: \"%i\" String so far: %s"), lp
[0], str
->str
);
583 g_string_assign (str
, "");
584 _skip_until (&lp
, ":;");
589 g_string_free (str
, TRUE
);
594 /* reads an entire attribute from the input buffer, leaving p pointing
595 at the start of the next line (past the \r\n) */
596 static b_VFormatAttribute
*_read_attribute (char **p
)
598 char *attr_group
= NULL
;
599 char *attr_name
= NULL
;
600 b_VFormatAttribute
*attr
= NULL
;
601 GString
*str
, *charset
= NULL
;
604 gboolean is_qp
= FALSE
;
606 /* first read in the group/name */
607 str
= g_string_new ("");
608 while (*lp
!= '\r' && *lp
!= '\0') {
609 if (*lp
== ':' || *lp
== ';') {
611 /* we've got a name, break out to the value/attribute parsing */
612 attr_name
= g_string_free (str
, FALSE
);
616 /* a line of the form:
619 * since we don't have an attribute
620 * name, skip to the end of the line
623 g_string_free (str
, TRUE
);
625 _skip_to_next_line(p
);
629 else if (*lp
== '.') {
631 BarryLogf(TRACE_INTERNAL
, _("extra `.' in attribute specification. ignoring extra group `%s'"), str
->str
);
632 g_string_free (str
, TRUE
);
633 str
= g_string_new ("");
636 attr_group
= g_string_free (str
, FALSE
);
637 str
= g_string_new ("");
640 else if (g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/') {
641 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
644 BarryLogf(TRACE_INTERNAL
, _("invalid character found in attribute group/name: \"%i\" String so far: %s"), lp
[0], str
->str
);
645 g_string_free (str
, TRUE
);
647 _skip_to_next_line(p
);
651 lp
= g_utf8_next_char(lp
);
655 _skip_to_next_line (p
);
659 attr
= b_vformat_attribute_new (attr_group
, attr_name
);
664 /* skip past the ';' */
665 lp
= g_utf8_next_char(lp
);
666 _read_attribute_params (attr
, &lp
, &is_qp
, &charset
);
669 /* skip past the ':' */
670 lp
= g_utf8_next_char(lp
);
671 _read_attribute_value (attr
, &lp
, is_qp
, charset
);
674 if (charset
) g_string_free(charset
, TRUE
);
683 b_vformat_attribute_free (attr
);
687 static void open_block(char **block
, const char *block_name
)
689 char *start
= *block
? *block
: "";
692 result
= g_strconcat(start
, "/", block_name
, NULL
);
698 static void close_block(char **block
, const char *block_name
)
700 int name_len
= strlen(block_name
);
701 int block_len
= *block
? strlen(*block
) : 0;
702 char *cmp_start
= NULL
;
704 if( block_len
< name_len
+ 1 )
707 cmp_start
= *block
+ (block_len
- name_len
- 1);
708 if( cmp_start
[0] == '/' &&
709 g_ascii_strcasecmp(cmp_start
+1, block_name
) == 0 )
711 // end of block hierarchy contains block name,
714 // cut off the end of the string... no need to free/realloc
719 /* we try to be as forgiving as we possibly can here - this isn't a
720 * validator. Almost nothing is considered a fatal error. We always
721 * try to return *something*.
723 static void _parse(b_VFormat
*evc
, const char *str
)
725 char *buf
= g_strdup (str
);
727 b_VFormatAttribute
*attr
;
729 /* first validate the string is valid utf8 */
730 if (!g_utf8_validate (buf
, -1, (const char **)&end
)) {
731 /* if the string isn't valid, we parse as much as we can from it */
732 BarryLogf(TRACE_INTERNAL
, _("invalid utf8 passed to b_VFormat. Limping along."));
736 buf
= _unfold_lines (buf
);
740 attr
= _read_attribute (&p
);
742 attr
= _read_attribute (&p
);
744 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "begin")) {
745 BarryLogf(TRACE_INTERNAL
, _("vformat began without a BEGIN\n"));
747 if (attr
&& !g_ascii_strcasecmp (attr
->name
, "begin"))
748 b_vformat_attribute_free (attr
);
750 b_vformat_add_attribute (evc
, attr
);
754 b_VFormatAttribute
*next_attr
= _read_attribute (&p
);
757 if( g_ascii_strcasecmp(next_attr
->name
, "begin") == 0 ) {
758 // add to block hierarchy string
759 char *value
= b_vformat_attribute_get_value(next_attr
);
760 open_block(&block
, value
);
761 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
764 else if( g_ascii_strcasecmp(next_attr
->name
, "end") == 0 ) {
765 // close off the block
766 char *value
= b_vformat_attribute_get_value(next_attr
);
767 close_block(&block
, value
);
768 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
772 // apply the block to the attr
773 next_attr
->block
= g_strdup(block
);
776 b_vformat_add_attribute (evc
, next_attr
);
781 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "end")) {
782 BarryLogf(TRACE_INTERNAL
, _("vformat ended without END"));
789 char *b_vformat_escape_string (const char *s
, b_VFormatType type
)
794 str
= g_string_new ("");
796 /* Escape a string as described in RFC2426, section 5 */
797 for (p
= s
; p
&& *p
; p
++) {
800 str
= g_string_append (str
, "\\n");
805 str
= g_string_append (str
, "\\n");
808 str
= g_string_append (str
, "\\;");
811 if (type
== VFORMAT_CARD_30
|| type
== VFORMAT_EVENT_20
|| type
== VFORMAT_TODO_20
)
812 str
= g_string_append (str
, "\\,");
814 str
= g_string_append_c (str
, *p
);
818 * We won't escape backslashes
819 * on vcard 2.1, unless it is in the end of a value.
820 * See comments above for a better explanation
822 if (*p
!= '\0' && type
== VFORMAT_CARD_21
) {
823 BarryLogf(TRACE_INTERNAL
, _("[%s]We won't escape backslashes"), __func__
);
824 str
= g_string_append_c(str
, *p
);
827 BarryLogf(TRACE_INTERNAL
, _("[%s] escape backslashes!!"), __func__
);
828 str
= g_string_append (str
, "\\\\");
832 str
= g_string_append_c (str
, *p
);
837 return g_string_free (str
, FALSE
);
841 b_vformat_unescape_string (const char *s
)
846 g_return_val_if_fail (s
!= NULL
, NULL
);
848 str
= g_string_new ("");
850 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
851 for (p
= s
; *p
; p
++) {
855 str
= g_string_append_c (str
, '\\');
859 case 'n': str
= g_string_append_c (str
, '\n'); break;
860 case 'r': str
= g_string_append_c (str
, '\r'); 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 case '"': str
= g_string_append_c (str
, '"'); break;
865 /* \t is (incorrectly) used by kOrganizer, so handle it here */
866 case 't': str
= g_string_append_c (str
, '\t'); break;
868 BarryLogf(TRACE_INTERNAL
, _("invalid escape, passing it through. escaped char was %u"), (unsigned int)*p
);
869 str
= g_string_append_c (str
, '\\');
870 str
= g_string_append_unichar (str
, g_utf8_get_char(p
));
876 return g_string_free (str
, FALSE
);
880 b_vformat_construct (b_VFormat
*evc
, const char *str
)
882 g_return_if_fail (str
!= NULL
);
888 void b_vformat_free(b_VFormat
*format
)
890 g_list_foreach (format
->attributes
, (GFunc
)b_vformat_attribute_free
, NULL
);
891 g_list_free (format
->attributes
);
895 b_VFormat
*b_vformat_new_from_string (const char *str
)
897 g_return_val_if_fail (str
!= NULL
, NULL
);
898 b_VFormat
*evc
= g_malloc0(sizeof(b_VFormat
));
900 b_vformat_construct (evc
, str
);
905 b_VFormat
*b_vformat_new(void)
907 return b_vformat_new_from_string ("");
910 static int _block_match(b_VFormatAttribute
*attr
, const char *block
)
912 // a block matches if the end of the attribute's block
913 // string matches a case insensitive compare with block
915 // for example, a calendar may or may not start with a
916 // BEGIN: VCALENDAR, so DTSTART's block string could be
917 // "/vcalendar/vevent" or just "/vevent". By passing
918 // "/vevent" or even "vevent" as the block argument above,
919 // we should get a match for any of the above.
921 int attr_len
= attr
->block
? strlen(attr
->block
) : 0;
922 int block_len
= block
? strlen(block
) : 0;
925 return 1; // if block is null, match everything
927 if( attr_len
< block_len
)
928 return 0; // not enough string to compare
930 if( attr_len
== 0 && block_len
== 0 )
931 return 1; // empty and null strings match
933 if( attr
->block
== NULL
)
934 return 0; // don't compare if one side is null
936 return g_ascii_strcasecmp(&attr
->block
[attr_len
- block_len
], block
) == 0;
939 b_VFormatAttribute
*b_vformat_find_attribute(b_VFormat
*vcard
, const char *name
, int nth
, const char *block
)
941 GList
*attributes
= b_vformat_get_attributes(vcard
);
944 for (a
= attributes
; a
; a
= a
->next
) {
945 b_VFormatAttribute
*attr
= a
->data
;
946 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr
), name
)) {
947 if( block
== NULL
|| _block_match(attr
, block
) ) {
958 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
962 GList *attributes = last ? last->next : 0;
965 for (a = attributes; a; a = a->next) {
966 b_VFormatAttribute *attr = a->data;
967 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
977 char *b_vformat_to_string (b_VFormat
*evc
, b_VFormatType type
)
979 BarryLogf(TRACE_ENTRY
, "%s(%p, %i)", __func__
, evc
, type
);
983 GString
*str
= g_string_new ("");
986 case VFORMAT_CARD_21
:
987 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
989 case VFORMAT_CARD_30
:
990 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
992 case VFORMAT_TODO_10
:
993 case VFORMAT_EVENT_10
:
994 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
996 case VFORMAT_TODO_20
:
997 case VFORMAT_EVENT_20
:
998 case VFORMAT_JOURNAL
:
999 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
1002 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
1006 for (l
= evc
->attributes
; l
; l
= l
->next
) {
1008 b_VFormatAttribute
*attr
= l
->data
;
1011 int format_encoding
= VF_ENCODING_RAW
;
1013 attr_str
= g_string_new ("");
1015 /* From rfc2425, 5.8.2
1017 * contentline = [group "."] name *(";" param) ":" value CRLF
1021 attr_str
= g_string_append (attr_str
, attr
->group
);
1022 attr_str
= g_string_append_c (attr_str
, '.');
1024 attr_str
= g_string_append (attr_str
, attr
->name
);
1025 /* handle the parameters */
1026 for (p
= attr
->params
; p
; p
= p
->next
) {
1027 b_VFormatParam
*param
= p
->data
;
1029 * param = param-name "=" param-value *("," param-value)
1031 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
1032 || type
== VFORMAT_EVENT_20
|| type
== VFORMAT_JOURNAL
) {
1035 * Character set can only be specified on the CHARSET
1036 * parameter on the Content-Type MIME header field.
1038 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
1040 attr_str
= g_string_append_c (attr_str
, ';');
1041 attr_str
= g_string_append (attr_str
, param
->name
);
1042 if (param
->values
) {
1043 attr_str
= g_string_append_c (attr_str
, '=');
1045 for (v
= param
->values
; v
; v
= v
->next
) {
1046 if (_helper_is_base64((const char *) v
->data
)) {
1047 format_encoding
= VF_ENCODING_BASE64
;
1048 /*Only the "B" encoding of [RFC 2047] is an allowed*/
1049 v
->data
=g_strdup("B");
1052 * QUOTED-PRINTABLE inline encoding has been
1055 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
1056 BarryLogf(TRACE_ERROR
, _("%s false encoding QUOTED-PRINTABLE is not allowed"), __func__
);
1057 format_encoding
= VF_ENCODING_QP
;
1059 attr_str
= g_string_append (attr_str
, v
->data
);
1062 attr_str
= g_string_append_c (attr_str
, ',');
1066 attr_str
= g_string_append_c (attr_str
, ';');
1068 * The "TYPE=" is optional skip it.
1069 * LOGO, PHOTO and SOUND multimedia formats MUST
1070 * have a "TYPE=" parameter
1072 gboolean must_have_type
= FALSE
;
1073 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
1074 must_have_type
= TRUE
;
1075 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
1076 attr_str
= g_string_append (attr_str
, param
->name
);
1077 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
1078 attr_str
= g_string_append_c (attr_str
, '=');
1079 for (v
= param
->values
; v
; v
= v
->next
) {
1080 // check for quoted-printable encoding
1081 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
1082 format_encoding
= VF_ENCODING_QP
;
1083 // check for base64 encoding
1084 if (_helper_is_base64((const char *) v
->data
)) {
1085 format_encoding
= VF_ENCODING_BASE64
;
1086 v
->data
=g_strdup("BASE64");
1088 attr_str
= g_string_append (attr_str
, v
->data
);
1090 attr_str
= g_string_append_c (attr_str
, ',');
1095 attr_str
= g_string_append_c (attr_str
, ':');
1097 for (v
= attr
->values
; v
; v
= v
->next
) {
1098 char *value
= v
->data
;
1099 char *escaped_value
= NULL
;
1101 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
1102 strstr (value
, "BYDAY") == v
->data
) {
1103 attr_str
= g_string_append (attr_str
, value
);
1105 escaped_value
= b_vformat_escape_string (value
, type
);
1106 attr_str
= g_string_append (attr_str
, escaped_value
);
1111 /* XXX toshok - i hate you, rfc 2426.
1112 why doesn't CATEGORIES use a ; like
1113 a normal list attribute? */
1114 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1115 attr_str
= g_string_append_c (attr_str
, ',');
1117 attr_str
= g_string_append_c (attr_str
, ';');
1120 g_free (escaped_value
);
1126 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1127 * After generating a content line,
1128 * lines longer than 75 characters SHOULD be folded according to the
1129 * folding procedure described in [MIME-DIR].
1131 * rfc 2445 (iCalendar), 4.1 Content Lines:
1132 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1133 * break. Long content lines SHOULD be split into a multiple line
1134 * representations using a line "folding" technique. That is, a long
1135 * line can be split between any two characters by inserting a CRLF
1136 * immediately followed by a single linear white space character (i.e.,
1137 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1138 * of CRLF followed immediately by a single linear white space character
1139 * is ignored (i.e., removed) when processing the content type.
1141 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1142 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1144 * Differences between encodings:
1145 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1147 * rfc 2425 [MIME-DIR], 5.8.1:
1148 * A logical line MAY be continued on the next physical line anywhere
1149 * between two characters by inserting a CRLF immediately followed by a
1150 * single <WS> (white space) character.
1152 * rfc 2045, 6.7, chapter 5:
1153 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1154 * without follwing <WS>
1159 * Note that all the line folding above is described in terms of characters
1160 * not bytes. In particular, it would be an error to put a line break
1161 * within a UTF-8 character.
1166 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1169 /* If using QP, must be sure that we do not fold within a quote sequence */
1170 if (format_encoding
== VF_ENCODING_QP
) {
1171 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1172 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1175 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1177 if (format_encoding
== VF_ENCODING_QP
)
1178 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1180 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1184 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1186 attr_str
= g_string_append (attr_str
, CRLF
);
1188 * base64= <MIME RFC 1521 base64 text>
1189 * the end of the text is marked with two CRLF sequences
1190 * this results in one blank line before the start of the
1193 if( format_encoding
== VF_ENCODING_BASE64
1194 && (type
== VFORMAT_CARD_21
))
1195 attr_str
= g_string_append (attr_str
, CRLF
);
1197 str
= g_string_append (str
, attr_str
->str
);
1198 g_string_free (attr_str
, TRUE
);
1202 case VFORMAT_CARD_21
:
1203 str
= g_string_append (str
, "END:VCARD\r\n");
1205 case VFORMAT_CARD_30
:
1206 str
= g_string_append (str
, "END:VCARD\r\n");
1208 case VFORMAT_TODO_10
:
1209 case VFORMAT_EVENT_10
:
1210 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1212 case VFORMAT_TODO_20
:
1213 case VFORMAT_EVENT_20
:
1214 case VFORMAT_JOURNAL
:
1215 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1218 str
= g_string_append (str
, "END:VNOTE\r\n");
1222 BarryLogf(TRACE_EXIT
, "%s", __func__
);
1223 return g_string_free (str
, FALSE
);
1226 void b_vformat_dump_structure (b_VFormat
*evc
)
1232 printf ("b_VFormat\n");
1233 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1235 b_VFormatAttribute
*attr
= a
->data
;
1236 printf ("+-- %s\n", attr
->name
);
1238 printf (" +- params=\n");
1240 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1241 b_VFormatParam
*param
= p
->data
;
1242 printf (" | [%d] = %s", i
,param
->name
);
1244 for (v
= param
->values
; v
; v
= v
->next
) {
1245 char *value
= b_vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1246 printf ("%s", value
);
1255 printf (" +- values=\n");
1256 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1257 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1262 b_VFormatAttribute
*b_vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1264 b_VFormatAttribute
*attr
;
1266 attr
= g_new0 (b_VFormatAttribute
, 1);
1268 attr
->group
= g_strdup (attr_group
);
1269 attr
->name
= g_strdup (attr_name
);
1275 b_vformat_attribute_free (b_VFormatAttribute
*attr
)
1277 g_return_if_fail (attr
!= NULL
);
1279 g_free (attr
->block
);
1280 g_free (attr
->group
);
1281 g_free (attr
->name
);
1283 b_vformat_attribute_remove_values (attr
);
1285 b_vformat_attribute_remove_params (attr
);
1291 b_vformat_attribute_copy (b_VFormatAttribute
*attr
)
1293 b_VFormatAttribute
*a
;
1296 g_return_val_if_fail (attr
!= NULL
, NULL
);
1298 a
= b_vformat_attribute_new (b_vformat_attribute_get_group (attr
),
1299 b_vformat_attribute_get_name (attr
));
1301 for (p
= attr
->values
; p
; p
= p
->next
)
1302 b_vformat_attribute_add_value (a
, p
->data
);
1304 for (p
= attr
->params
; p
; p
= p
->next
)
1305 b_vformat_attribute_add_param (a
, b_vformat_attribute_param_copy (p
->data
));
1311 b_vformat_remove_attributes (b_VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1315 g_return_if_fail (attr_name
!= NULL
);
1317 attr
= evc
->attributes
;
1320 b_VFormatAttribute
*a
= attr
->data
;
1322 next_attr
= attr
->next
;
1324 if (((!attr_group
&& !a
->group
) ||
1325 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1326 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1328 /* matches, remove/delete the attribute */
1329 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1331 b_vformat_attribute_free (a
);
1339 b_vformat_remove_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1341 g_return_if_fail (attr
!= NULL
);
1343 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1344 b_vformat_attribute_free (attr
);
1348 b_vformat_add_attribute (b_VFormat
*evc
, b_VFormatAttribute
*attr
)
1350 g_return_if_fail (attr
!= NULL
);
1352 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1356 b_vformat_add_attribute_with_value (b_VFormat
*b_VFormat
,
1357 b_VFormatAttribute
*attr
, const char *value
)
1359 g_return_if_fail (attr
!= NULL
);
1361 b_vformat_attribute_add_value (attr
, value
);
1363 b_vformat_add_attribute (b_VFormat
, attr
);
1367 b_vformat_add_attribute_with_values (b_VFormat
*b_VFormat
, b_VFormatAttribute
*attr
, ...)
1372 g_return_if_fail (attr
!= NULL
);
1374 va_start (ap
, attr
);
1376 while ((v
= va_arg (ap
, char*))) {
1377 b_vformat_attribute_add_value (attr
, v
);
1382 b_vformat_add_attribute (b_VFormat
, attr
);
1386 b_vformat_attribute_add_value (b_VFormatAttribute
*attr
, const char *value
)
1388 g_return_if_fail (attr
!= NULL
);
1390 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1394 b_vformat_attribute_add_value_decoded (b_VFormatAttribute
*attr
, const char *value
, int len
)
1396 g_return_if_fail (attr
!= NULL
);
1398 switch (attr
->encoding
) {
1399 case VF_ENCODING_RAW
:
1400 BarryLogf(TRACE_INTERNAL
, _("can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first"));
1402 case VF_ENCODING_BASE64
: {
1403 char *b64_data
= base64_encode_simple (value
, len
);
1404 GString
*decoded
= g_string_new_len (value
, len
);
1406 /* make sure the decoded list is up to date */
1407 b_vformat_attribute_get_values_decoded (attr
);
1409 attr
->values
= g_list_append (attr
->values
, b64_data
);
1410 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1413 case VF_ENCODING_QP
: {
1414 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1415 GString
*decoded
= g_string_new (value
);
1417 /* make sure the decoded list is up to date */
1418 b_vformat_attribute_get_values_decoded (attr
);
1420 attr
->values
= g_list_append (attr
->values
, qp_data
);
1421 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1424 case VF_ENCODING_8BIT
: {
1425 char *data
= g_strdup(value
);
1426 GString
*decoded
= g_string_new (value
);
1428 /* make sure the decoded list is up to date */
1429 b_vformat_attribute_get_values_decoded (attr
);
1431 attr
->values
= g_list_append (attr
->values
, data
);
1432 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1439 b_vformat_attribute_add_values (b_VFormatAttribute
*attr
, ...)
1444 g_return_if_fail (attr
!= NULL
);
1446 va_start (ap
, attr
);
1448 while ((v
= va_arg (ap
, char*))) {
1449 b_vformat_attribute_add_value (attr
, v
);
1456 free_gstring (GString
*str
)
1458 g_string_free (str
, TRUE
);
1462 b_vformat_attribute_remove_values (b_VFormatAttribute
*attr
)
1464 g_return_if_fail (attr
!= NULL
);
1466 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1467 g_list_free (attr
->values
);
1468 attr
->values
= NULL
;
1470 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1471 g_list_free (attr
->decoded_values
);
1472 attr
->decoded_values
= NULL
;
1476 b_vformat_attribute_remove_params (b_VFormatAttribute
*attr
)
1478 g_return_if_fail (attr
!= NULL
);
1480 g_list_foreach (attr
->params
, (GFunc
)b_vformat_attribute_param_free
, NULL
);
1481 g_list_free (attr
->params
);
1482 attr
->params
= NULL
;
1484 /* also remove the cached encoding on this attribute */
1485 attr
->encoding_set
= FALSE
;
1486 attr
->encoding
= VF_ENCODING_RAW
;
1490 b_vformat_attribute_param_new (const char *name
)
1492 b_VFormatParam
*param
= g_new0 (b_VFormatParam
, 1);
1493 param
->name
= g_strdup (name
);
1499 b_vformat_attribute_param_free (b_VFormatParam
*param
)
1501 g_return_if_fail (param
!= NULL
);
1503 g_free (param
->name
);
1505 b_vformat_attribute_param_remove_values (param
);
1511 b_vformat_attribute_param_copy (b_VFormatParam
*param
)
1516 g_return_val_if_fail (param
!= NULL
, NULL
);
1518 p
= b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param
));
1520 for (l
= param
->values
; l
; l
= l
->next
) {
1521 b_vformat_attribute_param_add_value (p
, l
->data
);
1528 b_vformat_attribute_add_param (b_VFormatAttribute
*attr
,
1529 b_VFormatParam
*param
)
1531 g_return_if_fail (attr
!= NULL
);
1532 g_return_if_fail (param
!= NULL
);
1534 attr
->params
= g_list_append (attr
->params
, param
);
1536 /* we handle our special encoding stuff here */
1538 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1539 if (attr
->encoding_set
) {
1540 BarryLogf(TRACE_INTERNAL
, _("ENCODING specified twice"));
1544 if (param
->values
&& param
->values
->data
) {
1545 if (_helper_is_base64((const char*)param
->values
->data
))
1546 attr
->encoding
= VF_ENCODING_BASE64
;
1547 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1548 attr
->encoding
= VF_ENCODING_QP
;
1549 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1550 attr
->encoding
= VF_ENCODING_8BIT
;
1552 BarryLogf(TRACE_INTERNAL
, _("Unknown value `%s' for ENCODING parameter. values will be treated as raw"), (char*)param
->values
->data
);
1555 attr
->encoding_set
= TRUE
;
1558 BarryLogf(TRACE_INTERNAL
, _("ENCODING parameter added with no value"));
1563 b_VFormatParam
*b_vformat_attribute_find_param(b_VFormatAttribute
*attr
, const char *name
, int level
)
1565 g_return_val_if_fail (attr
!= NULL
, NULL
);
1567 for (p
= attr
->params
; p
; p
= p
->next
) {
1568 b_VFormatParam
*param
= p
->data
;
1569 if (!g_ascii_strcasecmp (param
->name
, name
)) {
1580 b_vformat_attribute_set_value (b_VFormatAttribute
*attr
,
1581 int nth
, const char *value
)
1583 GList
*param
= g_list_nth(attr
->values
, nth
);
1584 g_free(param
->data
);
1585 param
->data
= g_strdup(value
);
1589 b_vformat_attribute_param_add_value (b_VFormatParam
*param
,
1592 g_return_if_fail (param
!= NULL
);
1594 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1598 b_vformat_attribute_param_add_values (b_VFormatParam
*param
,
1604 g_return_if_fail (param
!= NULL
);
1606 va_start (ap
, param
);
1608 while ((v
= va_arg (ap
, char*))) {
1609 b_vformat_attribute_param_add_value (param
, v
);
1616 b_vformat_attribute_add_param_with_value (b_VFormatAttribute
*attr
, const char *name
, const char *value
)
1618 g_return_if_fail (attr
!= NULL
);
1619 g_return_if_fail (name
!= NULL
);
1624 b_VFormatParam
*param
= b_vformat_attribute_param_new(name
);
1626 b_vformat_attribute_param_add_value (param
, value
);
1628 b_vformat_attribute_add_param (attr
, param
);
1632 b_vformat_attribute_add_param_with_values (b_VFormatAttribute
*attr
,
1633 b_VFormatParam
*param
, ...)
1638 g_return_if_fail (attr
!= NULL
);
1639 g_return_if_fail (param
!= NULL
);
1641 va_start (ap
, param
);
1643 while ((v
= va_arg (ap
, char*))) {
1644 b_vformat_attribute_param_add_value (param
, v
);
1649 b_vformat_attribute_add_param (attr
, param
);
1653 b_vformat_attribute_param_remove_values (b_VFormatParam
*param
)
1655 g_return_if_fail (param
!= NULL
);
1657 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1658 g_list_free (param
->values
);
1659 param
->values
= NULL
;
1663 b_vformat_get_attributes (b_VFormat
*format
)
1665 return format
->attributes
;
1669 b_vformat_attribute_get_group (b_VFormatAttribute
*attr
)
1671 g_return_val_if_fail (attr
!= NULL
, NULL
);
1677 b_vformat_attribute_get_name (b_VFormatAttribute
*attr
)
1679 g_return_val_if_fail (attr
!= NULL
, NULL
);
1685 b_vformat_attribute_get_block (b_VFormatAttribute
*attr
)
1687 g_return_val_if_fail (attr
!= NULL
, NULL
);
1693 b_vformat_attribute_get_values (b_VFormatAttribute
*attr
)
1695 g_return_val_if_fail (attr
!= NULL
, NULL
);
1697 return attr
->values
;
1701 b_vformat_attribute_get_values_decoded (b_VFormatAttribute
*attr
)
1703 g_return_val_if_fail (attr
!= NULL
, NULL
);
1705 if (!attr
->decoded_values
) {
1707 switch (attr
->encoding
) {
1708 case VF_ENCODING_RAW
:
1709 case VF_ENCODING_8BIT
:
1710 for (l
= attr
->values
; l
; l
= l
->next
)
1711 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1713 case VF_ENCODING_BASE64
:
1714 for (l
= attr
->values
; l
; l
= l
->next
) {
1715 char *decoded
= g_strdup ((char*)l
->data
);
1716 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1717 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1721 case VF_ENCODING_QP
:
1722 for (l
= attr
->values
; l
; l
= l
->next
) {
1725 char *decoded
= g_strdup ((char*)l
->data
);
1726 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1727 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1734 return attr
->decoded_values
;
1738 b_vformat_attribute_is_single_valued (b_VFormatAttribute
*attr
)
1740 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1742 if (attr
->values
== NULL
1743 || attr
->values
->next
!= NULL
)
1750 b_vformat_attribute_get_value (b_VFormatAttribute
*attr
)
1754 g_return_val_if_fail (attr
!= NULL
, NULL
);
1756 values
= b_vformat_attribute_get_values (attr
);
1758 if (!b_vformat_attribute_is_single_valued (attr
))
1759 BarryLogf(TRACE_INTERNAL
, _("b_vformat_attribute_get_value called on multivalued attribute"));
1761 return values
? g_strdup ((char*)values
->data
) : NULL
;
1765 b_vformat_attribute_get_value_decoded (b_VFormatAttribute
*attr
)
1768 GString
*str
= NULL
;
1770 g_return_val_if_fail (attr
!= NULL
, NULL
);
1772 values
= b_vformat_attribute_get_values_decoded (attr
);
1774 if (!b_vformat_attribute_is_single_valued (attr
))
1775 BarryLogf(TRACE_INTERNAL
, _("b_vformat_attribute_get_value_decoded called on multivalued attribute"));
1780 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1783 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute
*attr
, int nth
)
1785 GList
*values
= b_vformat_attribute_get_values_decoded(attr
);
1788 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1792 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1793 values
= b_vformat_attribute_get_values(attr
);
1796 return g_list_nth_data(values
, nth
);
1803 b_vformat_attribute_has_type (b_VFormatAttribute
*attr
, const char *typestr
)
1808 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1809 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1811 params
= b_vformat_attribute_get_params (attr
);
1813 for (p
= params
; p
; p
= p
->next
) {
1814 b_VFormatParam
*param
= p
->data
;
1816 if (!strcasecmp (b_vformat_attribute_param_get_name (param
), "TYPE")) {
1817 GList
*values
= b_vformat_attribute_param_get_values (param
);
1820 for (v
= values
; v
; v
= v
->next
) {
1821 if (!strcasecmp ((char*)v
->data
, typestr
))
1831 gboolean
b_vformat_attribute_has_param(b_VFormatAttribute
*attr
, const char *name
)
1833 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1834 g_return_val_if_fail (name
!= NULL
, FALSE
);
1836 GList
*params
= b_vformat_attribute_get_params(attr
);
1838 for (p
= params
; p
; p
= p
->next
) {
1839 b_VFormatParam
*param
= p
->data
;
1840 if (!strcasecmp(name
, b_vformat_attribute_param_get_name(param
)))
1847 b_vformat_attribute_get_params (b_VFormatAttribute
*attr
)
1849 g_return_val_if_fail (attr
!= NULL
, NULL
);
1851 return attr
->params
;
1855 b_vformat_attribute_param_get_name (b_VFormatParam
*param
)
1857 g_return_val_if_fail (param
!= NULL
, NULL
);
1863 b_vformat_attribute_param_get_values (b_VFormatParam
*param
)
1865 g_return_val_if_fail (param
!= NULL
, NULL
);
1867 return param
->values
;
1870 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam
*param
, int nth
)
1872 const char *ret
= NULL
;
1873 GList
*values
= b_vformat_attribute_param_get_values(param
);
1876 ret
= g_list_nth_data(values
, nth
);
1880 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1882 //static unsigned char _evc_base64_rank[256];
1884 static void base64_init(char *rank
)
1888 memset(rank
, 0xff, sizeof(rank
));
1889 for (i
=0;i
<64;i
++) {
1890 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1895 /* call this when finished encoding everything, to
1896 flush off the last little bit */
1897 static size_t base64_encode_close(const unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1900 unsigned char *outptr
= out
;
1903 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1905 c1
= ((unsigned char *)save
)[1];
1906 c2
= ((unsigned char *)save
)[2];
1908 switch (((char *)save
)[0]) {
1910 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1911 g_assert(outptr
[2] != 0);
1916 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1917 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1932 performs an 'encode step', only encodes blocks of 3 characters to the
1933 output at a time, saves left-over state in state and save (initialise to
1934 0 on first invocation).
1936 static size_t base64_encode_step(const unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1938 register const unsigned char *inptr
;
1939 register unsigned char *outptr
;
1947 if (len
+ ((char *)save
)[0] > 2) {
1948 const unsigned char *inend
= in
+len
-2;
1949 register int c1
, c2
, c3
;
1950 register int already
;
1954 switch (((char *)save
)[0]) {
1955 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1956 case 2: c1
= ((unsigned char *)save
)[1];
1957 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1960 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1961 while (inptr
< inend
) {
1967 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1968 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1969 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1970 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1971 /* this is a bit ugly ... */
1972 if (break_lines
&& (++already
)>=19) {
1978 ((char *)save
)[0] = 0;
1979 len
= 2-(inptr
-inend
);
1984 register char *saveout
;
1986 /* points to the slot for the next char to save */
1987 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1989 /* len can only be 0 1 or 2 */
1991 case 2: *saveout
++ = *inptr
++;
1992 case 1: *saveout
++ = *inptr
++;
1994 ((char *)save
)[0]+=len
;
2002 * base64_decode_step: decode a chunk of base64 encoded data
2004 * @len: max length of data to decode
2005 * @out: output stream
2006 * @state: holds the number of bits that are stored in @save
2007 * @save: leftover bits that have not yet been decoded
2009 * Decodes a chunk of base64 encoded data
2011 static size_t base64_decode_step(const unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
2013 unsigned char base64_rank
[256];
2014 base64_init((char*)base64_rank
);
2016 register const unsigned char *inptr
;
2017 register unsigned char *outptr
;
2018 const unsigned char *inend
;
2020 register unsigned int v
;
2026 /* convert 4 base64 bytes to 3 normal bytes */
2030 while (inptr
<inend
) {
2031 c
= base64_rank
[*inptr
++];
2047 /* quick scan back for '=' on the end somewhere */
2048 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
2050 while (inptr
>in
&& i
) {
2052 if (base64_rank
[*inptr
] != 0xff) {
2053 if (*inptr
== '=' && outptr
>out
)
2059 /* if i!= 0 then there is a truncation error! */
2063 static char *base64_encode_simple (const char *data
, size_t len
)
2066 int state
= 0, outlen
;
2067 unsigned int save
= 0;
2069 g_return_val_if_fail (data
!= NULL
, NULL
);
2071 out
= g_malloc (len
* 4 / 3 + 5);
2072 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
2073 out
, &state
, (int*)&save
);
2078 static size_t base64_decode_simple (char *data
, size_t len
)
2081 unsigned int save
= 0;
2083 g_return_val_if_fail (data
!= NULL
, 0);
2085 return base64_decode_step ((unsigned char *)data
, len
,
2086 (unsigned char *)data
, &state
, &save
);
2089 static char *quoted_encode_simple(const unsigned char *string
, int len
)
2091 GString
*tmp
= g_string_new("");
2094 while(string
[i
] != 0) {
2095 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
2096 g_string_append_printf(tmp
, "=%02X", string
[i
]);
2098 g_string_append_c(tmp
, string
[i
]);
2103 char *ret
= tmp
->str
;
2104 g_string_free(tmp
, FALSE
);
2109 static size_t quoted_decode_simple (char *data
, size_t len
)
2111 g_return_val_if_fail (data
!= NULL
, 0);
2113 GString
*string
= g_string_new(data
);
2121 //Get the index of the next encoded char
2122 int i
= strcspn(string
->str
, "=");
2123 if (i
>= strlen(string
->str
))
2127 strncat(hex
, &string
->str
[i
+ 1], 2);
2128 char rep
= ((int)(strtod(hex
, NULL
)));
2129 g_string_erase(string
, i
, 2);
2130 g_string_insert_c(string
, i
, rep
);
2133 memset(data
, 0, strlen(data
));
2134 strcpy(data
, string
->str
);
2135 g_string_free(string
, 1);
2137 return strlen(data
);