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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Author: Chris Toshok (toshok@ximian.com)
19 * Author: Armin Bauer (armin.bauer@opensync.org)
34 #include <opensync/opensync.h>
36 static size_t base64_encode_step(unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
);
37 static size_t base64_decode_step(unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
);
38 size_t base64_decode_simple (char *data
, size_t len
);
39 char *base64_encode_simple (const char *data
, size_t len
);
41 size_t quoted_decode_simple (char *data
, size_t len
);
42 char *quoted_encode_simple (const unsigned char *string
, int len
);
46 * _helper_is_base64 is helper function to check i a string is "b" or "base64"
47 * @param check_string string that should be compared with "b" or "base64"
48 * @return 0 if check_string is not base64 and 1 if it is
50 static int _helper_is_base64(const char *check_string
)
52 if(!g_ascii_strcasecmp ((char *) check_string
, "BASE64") ||
53 !g_ascii_strcasecmp ((char *) check_string
, "b") )
58 time_t vformat_time_to_unix(const char *inptime
)
63 if ((ftime
= g_strrstr(inptime
, "T"))) {
65 date
= g_strndup(inptime
, ftime
- inptime
);
67 time
= g_strndup(ftime
+ 1, 8);
69 time
= g_strndup(ftime
+ 1, 6);
71 date
= g_strdup(inptime
);
75 memset(&btime
, 0, sizeof(struct tm
));
78 if (strlen(date
) == 10) {
79 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111 - 1900;
80 btime
.tm_mon
= date
[5] * 10 + date
[6] - '0' * 11 - 1;
81 btime
.tm_mday
= date
[8] * 10 + date
[9] - '0' * 11;
83 btime
.tm_year
= date
[0] * 1000 + date
[1] * 100 + date
[2] * 10 + date
[3] - '0' * 1111- 1900;
84 btime
.tm_mon
= date
[4] * 10 + date
[5] - '0' * 11 - 1;
85 btime
.tm_mday
= date
[6] * 10 + date
[7] - '0' * 11;
88 if (time
&& strlen(time
) == 8) {
90 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
91 btime
.tm_min
= time
[3] * 10 + time
[4] - '0' * 11;
92 btime
.tm_sec
= time
[6] * 10 + time
[7] - '0' * 11;
93 } else if (time
&& strlen(time
) == 6) {
94 btime
.tm_hour
= time
[0] * 10 + time
[1] - '0' * 11;
95 btime
.tm_min
= time
[2] * 10 + time
[3] - '0' * 11;
96 btime
.tm_sec
= time
[4] * 10 + time
[5] - '0' * 11;
99 time_t utime
= mktime(&btime
);
103 static char *_fold_lines (char *buf
)
105 GString
*str
= g_string_new ("");
106 GString
*line
= g_string_new ("");
108 char *next
, *next2
, *q
;
109 gboolean newline
= TRUE
;
110 gboolean quotedprintable
= FALSE
;
113 * We're pretty liberal with line folding here. We handle
114 * lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
115 * We also turn single \r's and \n's not followed by <WS> into \r\n's.
120 /* search new lines for quoted printable encoding */
122 for (q
=p
; *q
!= '\n' && *q
!= '\0'; q
++)
123 line
= g_string_append_unichar (line
, g_utf8_get_char (q
));
125 if (strstr(line
->str
, "ENCODING=QUOTED-PRINTABLE"))
126 quotedprintable
= TRUE
;
128 g_string_free(line
, TRUE
);
129 line
= g_string_new ("");
135 if ((quotedprintable
&& *p
== '=') || *p
== '\r' || *p
== '\n') {
136 next
= g_utf8_next_char (p
);
137 if (*next
== '\n' || *next
== '\r') {
138 next2
= g_utf8_next_char (next
);
139 if (*next2
== '\n' || *next2
== '\r' || *next2
== ' ' || *next2
== '\t') {
140 p
= g_utf8_next_char (next2
);
143 str
= g_string_append (str
, CRLF
);
144 p
= g_utf8_next_char (next
);
146 quotedprintable
= FALSE
;
149 else if (*p
== '=') {
150 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
151 p
= g_utf8_next_char (p
);
153 else if (*next
== ' ' || *next
== '\t') {
154 p
= g_utf8_next_char (next
);
157 str
= g_string_append (str
, CRLF
);
158 p
= g_utf8_next_char (p
);
160 quotedprintable
= FALSE
;
164 str
= g_string_append_unichar (str
, g_utf8_get_char (p
));
165 p
= g_utf8_next_char (p
);
170 g_string_free(line
, TRUE
);
172 return g_string_free (str
, FALSE
);
175 /* skip forward until we hit the CRLF, or \0 */
176 static void _skip_to_next_line (char **p
)
181 while (*lp
!= '\r' && *lp
!= '\0')
182 lp
= g_utf8_next_char (lp
);
185 lp
= g_utf8_next_char (lp
); /* \n */
186 lp
= g_utf8_next_char (lp
); /* start of the next line */
192 /* skip forward until we hit a character in @s, CRLF, or \0. leave *p
193 pointing at the character that causes us to stop */
194 static void _skip_until (char **p
, char *s
)
200 while (*lp
!= '\r' && *lp
!= '\0') {
201 gboolean s_matches
= FALSE
;
203 for (ls
= s
; *ls
; ls
= g_utf8_next_char (ls
)) {
204 if (g_utf8_get_char (ls
) == g_utf8_get_char (lp
)) {
218 static void _read_attribute_value_add (VFormatAttribute
*attr
, GString
*str
, GString
*charset
)
220 /* don't convert empty strings */
222 vformat_attribute_add_value(attr
, str
->str
);
226 char *inbuf
, *outbuf
, *p
;
227 size_t inbytesleft
, outbytesleft
;
230 p
= outbuf
= malloc(str
->len
*2);
231 inbytesleft
= str
->len
;
232 outbytesleft
= str
->len
*2;
236 /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
239 cd
= iconv_open("UTF-8", charset
->str
);
241 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
243 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
246 vformat_attribute_add_value(attr
, outbuf
);
250 /* hmm, should not happen */
251 vformat_attribute_add_value(attr
, str
->str
);
259 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
260 if (g_utf8_validate (inbuf
, -1, NULL
)) {
262 vformat_attribute_add_value (attr
, str
->str
);
266 /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
267 cd
= iconv_open("UTF-8", "ISO-8859-1");
269 if (iconv(cd
, (const char**)&inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
271 if (iconv(cd
, &inbuf
, &inbytesleft
, &p
, &outbytesleft
) != (size_t)(-1)) {
274 vformat_attribute_add_value (attr
, outbuf
);
278 vformat_attribute_add_value (attr
, str
->str
);
292 static void _read_attribute_value (VFormatAttribute
*attr
, char **p
, int format_encoding
, GString
*charset
)
297 /* read in the value */
298 str
= g_string_new ("");
299 while (*lp
!= '\r' && *lp
!= '\0') {
300 if (*lp
== '=' && format_encoding
== VF_ENCODING_QP
) {
301 char a
, b
, x1
=0, x2
=0;
303 if ((a
= *(++lp
)) == '\0') break;
304 if ((b
= *(++lp
)) == '\0') break;
308 /* e.g. ...N=C3=BCrnberg\r\n
321 if (*(++tmplp
) == '\r' && *(++tmplp
) == '\n' && isalnum(*(++tmplp
))) {
328 /* append malformed input, and
330 str
= g_string_append_c(str
, a
);
331 str
= g_string_append_c(str
, b
);
340 if (b
== '\r' && c
== '\n' && isalnum(d
) && isalnum(e
)) {
346 /* append malformed input, and
348 str
= g_string_append_c(str
, a
);
349 str
= g_string_append_c(str
, b
);
353 /* append malformed input, and
355 str
= g_string_append_c(str
, a
);
356 str
= g_string_append_c(str
, b
);
364 c
= (((a
>='a'?a
-'a'+10:a
-'0')&0x0f) << 4)
365 | ((b
>='a'?b
-'a'+10:b
-'0')&0x0f);
367 str
= g_string_append_c (str
, c
);
372 else if (format_encoding
== VF_ENCODING_BASE64
) {
373 if((*lp
!= ' ') && (*lp
!= '\t') )
374 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
375 lp
= g_utf8_next_char(lp
);
377 else if (*lp
== '\\') {
378 /* convert back to the non-escaped version of
380 lp
= g_utf8_next_char(lp
);
382 str
= g_string_append_c (str
, '\\');
386 case 'n': str
= g_string_append_c (str
, '\n'); break;
387 case 'r': str
= g_string_append_c (str
, '\r'); break;
388 case ';': str
= g_string_append_c (str
, ';'); break;
390 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES")) {
391 //We need to handle categories here to work
392 //aroung a bug in evo2
393 _read_attribute_value_add (attr
, str
, charset
);
394 g_string_assign (str
, "");
396 str
= g_string_append_c (str
, ',');
398 case '\\': str
= g_string_append_c (str
, '\\'); break;
399 case '"': str
= g_string_append_c (str
, '"'); break;
400 /* \t is (incorrectly) used by kOrganizer, so handle it here */
401 case 't': str
= g_string_append_c (str
, '\t'); break;
403 osync_trace(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %i", *lp
);
404 str
= g_string_append_c (str
, '\\');
405 str
= g_string_append_unichar (str
, g_utf8_get_char(lp
));
408 lp
= g_utf8_next_char(lp
);
410 else if ((*lp
== ';') ||
411 (*lp
== ',' && !g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))) {
412 _read_attribute_value_add (attr
, str
, charset
);
413 g_string_assign (str
, "");
414 lp
= g_utf8_next_char(lp
);
417 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
418 lp
= g_utf8_next_char(lp
);
422 _read_attribute_value_add (attr
, str
, charset
);
423 g_string_free (str
, TRUE
);
427 lp
= g_utf8_next_char (lp
); /* \n */
428 lp
= g_utf8_next_char (lp
); /* start of the next line */
434 static void _read_attribute_params(VFormatAttribute
*attr
, char **p
, int *format_encoding
, GString
**charset
)
438 VFormatParam
*param
= NULL
;
439 gboolean in_quote
= FALSE
;
440 str
= g_string_new ("");
442 while (*lp
!= '\0') {
444 in_quote
= !in_quote
;
445 lp
= g_utf8_next_char (lp
);
447 else if (in_quote
|| g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/' || *lp
== '.' || *lp
== ' ') {
448 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
449 lp
= g_utf8_next_char (lp
);
451 /* accumulate until we hit the '=' or ';'. If we hit
452 * a '=' the string contains the parameter name. if
453 * we hit a ';' the string contains the parameter
454 * value and the name is either ENCODING (if value ==
455 * QUOTED-PRINTABLE) or TYPE (in any other case.)
457 else if (*lp
== '=') {
459 param
= vformat_attribute_param_new (str
->str
);
460 g_string_assign (str
, "");
461 lp
= g_utf8_next_char (lp
);
464 _skip_until (&lp
, ":;");
466 lp
= g_utf8_next_char (lp
); /* \n */
467 lp
= g_utf8_next_char (lp
); /* start of the next line */
471 lp
= g_utf8_next_char (lp
);
474 else if (*lp
== ';' || *lp
== ':' || *lp
== ',') {
475 gboolean colon
= (*lp
== ':');
476 gboolean comma
= (*lp
== ',');
480 vformat_attribute_param_add_value (param
, str
->str
);
481 g_string_assign (str
, "");
483 lp
= g_utf8_next_char (lp
);
486 /* we've got a parameter of the form:
488 * so what we do depends on if there are already values
489 * for the parameter. If there are, we just finish
490 * this parameter and skip past the offending character
491 * (unless it's the ':'). If there aren't values, we free
492 * the parameter then skip past the character.
494 if (!param
->values
) {
495 vformat_attribute_param_free (param
);
498 lp
= g_utf8_next_char (lp
);
503 && !g_ascii_strcasecmp (param
->name
, "encoding")) {
504 if (!g_ascii_strcasecmp (param
->values
->data
, "quoted-printable")) {
505 *format_encoding
= VF_ENCODING_QP
;
506 vformat_attribute_param_free (param
);
508 } else if ( _helper_is_base64(param
->values
->data
)) {
509 *format_encoding
= VF_ENCODING_BASE64
;
510 // vformat_attribute_param_free (param);
513 } else if (param
&& !g_ascii_strcasecmp(param
->name
, "charset")) {
514 *charset
= g_string_new(param
->values
->data
);
515 vformat_attribute_param_free (param
);
522 if (!g_ascii_strcasecmp (str
->str
,
523 "quoted-printable")) {
524 param_name
= "ENCODING";
525 *format_encoding
= VF_ENCODING_QP
;
527 /* apple's broken addressbook app outputs naked BASE64
528 parameters, which aren't even vcard 3.0 compliant. */
529 else if (!g_ascii_strcasecmp (str
->str
,
531 param_name
= "ENCODING";
532 g_string_assign (str
, "b");
533 *format_encoding
= VF_ENCODING_BASE64
;
540 param
= vformat_attribute_param_new (param_name
);
541 vformat_attribute_param_add_value (param
, str
->str
);
543 g_string_assign (str
, "");
545 lp
= g_utf8_next_char (lp
);
548 /* we've got an attribute with a truly empty
549 attribute parameter. So it's of the form:
551 ATTR;[PARAM=value;]*;[PARAM=value;]*:
555 the only thing to do here is, well.. nothing.
556 we skip over the character if it's not a colon,
557 and the rest is handled for us: We'll either
558 continue through the loop again if we hit a ';',
559 or we'll break out correct below if it was a ':' */
561 lp
= g_utf8_next_char (lp
);
564 if (param
&& !comma
) {
565 vformat_attribute_add_param (attr
, param
);
572 osync_trace(TRACE_INTERNAL
, "invalid character found in parameter spec: \"%i\" String so far: %s", lp
[0], str
->str
);
573 g_string_assign (str
, "");
574 _skip_until (&lp
, ":;");
579 g_string_free (str
, TRUE
);
584 /* reads an entire attribute from the input buffer, leaving p pointing
585 at the start of the next line (past the \r\n) */
586 static VFormatAttribute
*_read_attribute (char **p
)
588 char *attr_group
= NULL
;
589 char *attr_name
= NULL
;
590 VFormatAttribute
*attr
= NULL
;
591 GString
*str
, *charset
= NULL
;
594 gboolean is_qp
= FALSE
;
596 /* first read in the group/name */
597 str
= g_string_new ("");
598 while (*lp
!= '\r' && *lp
!= '\0') {
599 if (*lp
== ':' || *lp
== ';') {
601 /* we've got a name, break out to the value/attribute parsing */
602 attr_name
= g_string_free (str
, FALSE
);
606 /* a line of the form:
609 * since we don't have an attribute
610 * name, skip to the end of the line
613 g_string_free (str
, TRUE
);
615 _skip_to_next_line(p
);
619 else if (*lp
== '.') {
621 osync_trace(TRACE_INTERNAL
, "extra `.' in attribute specification. ignoring extra group `%s'",
623 g_string_free (str
, TRUE
);
624 str
= g_string_new ("");
627 attr_group
= g_string_free (str
, FALSE
);
628 str
= g_string_new ("");
631 else if (g_unichar_isalnum (g_utf8_get_char (lp
)) || *lp
== '-' || *lp
== '_' || *lp
== '/') {
632 str
= g_string_append_unichar (str
, g_utf8_get_char (lp
));
635 osync_trace(TRACE_INTERNAL
, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp
[0], str
->str
);
636 g_string_free (str
, TRUE
);
638 _skip_to_next_line(p
);
642 lp
= g_utf8_next_char(lp
);
646 _skip_to_next_line (p
);
650 attr
= vformat_attribute_new (attr_group
, attr_name
);
655 /* skip past the ';' */
656 lp
= g_utf8_next_char(lp
);
657 _read_attribute_params (attr
, &lp
, &is_qp
, &charset
);
660 /* skip past the ':' */
661 lp
= g_utf8_next_char(lp
);
662 _read_attribute_value (attr
, &lp
, is_qp
, charset
);
665 if (charset
) g_string_free(charset
, TRUE
);
674 vformat_attribute_free (attr
);
678 /* we try to be as forgiving as we possibly can here - this isn't a
679 * validator. Almost nothing is considered a fatal error. We always
680 * try to return *something*.
682 static void _parse(VFormat
*evc
, const char *str
)
684 char *buf
= g_strdup (str
);
686 VFormatAttribute
*attr
;
688 /* first validate the string is valid utf8 */
689 if (!g_utf8_validate (buf
, -1, (const char **)&end
)) {
690 /* if the string isn't valid, we parse as much as we can from it */
691 osync_trace(TRACE_INTERNAL
, "invalid utf8 passed to VFormat. Limping along.");
695 buf
= _fold_lines (buf
);
699 attr
= _read_attribute (&p
);
701 attr
= _read_attribute (&p
);
703 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "begin")) {
704 osync_trace(TRACE_INTERNAL
, "vformat began without a BEGIN\n");
706 if (attr
&& !g_ascii_strcasecmp (attr
->name
, "begin"))
707 vformat_attribute_free (attr
);
709 vformat_add_attribute (evc
, attr
);
712 VFormatAttribute
*next_attr
= _read_attribute (&p
);
715 //if (g_ascii_strcasecmp (next_attr->name, "end"))
716 vformat_add_attribute (evc
, next_attr
);
721 if (!attr
|| attr
->group
|| g_ascii_strcasecmp (attr
->name
, "end")) {
722 osync_trace(TRACE_INTERNAL
, "vformat ended without END");
728 char *vformat_escape_string (const char *s
, VFormatType type
)
733 str
= g_string_new ("");
735 /* Escape a string as described in RFC2426, section 5 */
736 for (p
= s
; p
&& *p
; p
++) {
739 str
= g_string_append (str
, "\\n");
744 str
= g_string_append (str
, "\\n");
747 str
= g_string_append (str
, "\\;");
750 if (type
== VFORMAT_CARD_30
|| type
== VFORMAT_EVENT_20
|| type
== VFORMAT_TODO_20
)
751 str
= g_string_append (str
, "\\,");
753 str
= g_string_append_c (str
, *p
);
757 * We won't escape backslashes
758 * on vcard 2.1, unless it is in the end of a value.
759 * See comments above for a better explanation
761 if (*p
!= '\0' && type
== VFORMAT_CARD_21
) {
762 osync_trace(TRACE_INTERNAL
, "[%s]We won't escape backslashes", __func__
);
763 str
= g_string_append_c(str
, *p
);
766 osync_trace(TRACE_INTERNAL
, "[%s] escape backslashes!!", __func__
);
767 str
= g_string_append (str
, "\\\\");
771 str
= g_string_append_c (str
, *p
);
776 return g_string_free (str
, FALSE
);
780 vformat_unescape_string (const char *s
)
785 g_return_val_if_fail (s
!= NULL
, NULL
);
787 str
= g_string_new ("");
789 /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
790 for (p
= s
; *p
; p
++) {
794 str
= g_string_append_c (str
, '\\');
798 case 'n': str
= g_string_append_c (str
, '\n'); break;
799 case 'r': str
= g_string_append_c (str
, '\r'); break;
800 case ';': str
= g_string_append_c (str
, ';'); break;
801 case ',': str
= g_string_append_c (str
, ','); break;
802 case '\\': str
= g_string_append_c (str
, '\\'); break;
803 case '"': str
= g_string_append_c (str
, '"'); break;
804 /* \t is (incorrectly) used by kOrganizer, so handle it here */
805 case 't': str
= g_string_append_c (str
, '\t'); break;
807 osync_trace(TRACE_INTERNAL
, "invalid escape, passing it through. escaped char was %s", *p
);
808 str
= g_string_append_c (str
, '\\');
809 str
= g_string_append_unichar (str
, g_utf8_get_char(p
));
815 return g_string_free (str
, FALSE
);
819 vformat_construct (VFormat
*evc
, const char *str
)
821 g_return_if_fail (str
!= NULL
);
827 void vformat_free(VFormat
*format
)
829 g_list_foreach (format
->attributes
, (GFunc
)vformat_attribute_free
, NULL
);
830 g_list_free (format
->attributes
);
834 VFormat
*vformat_new_from_string (const char *str
)
836 g_return_val_if_fail (str
!= NULL
, NULL
);
837 VFormat
*evc
= g_malloc0(sizeof(VFormat
));
839 vformat_construct (evc
, str
);
844 VFormat
*vformat_new(void)
846 return vformat_new_from_string ("");
849 VFormatAttribute
*vformat_find_attribute(VFormat
*vcard
, const char *name
, int nth
)
851 GList
*attributes
= vformat_get_attributes(vcard
);
854 for (a
= attributes
; a
; a
= a
->next
) {
855 VFormatAttribute
*attr
= a
->data
;
856 if (!g_ascii_strcasecmp(vformat_attribute_get_name(attr
), name
)) {
865 char *vformat_to_string (VFormat
*evc
, VFormatType type
)
867 osync_trace(TRACE_ENTRY
, "%s(%p, %i)", __func__
, type
);
871 GString
*str
= g_string_new ("");
874 case VFORMAT_CARD_21
:
875 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
877 case VFORMAT_CARD_30
:
878 str
= g_string_append (str
, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
880 case VFORMAT_TODO_10
:
881 case VFORMAT_EVENT_10
:
882 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
884 case VFORMAT_TODO_20
:
885 case VFORMAT_EVENT_20
:
886 str
= g_string_append (str
, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
889 str
= g_string_append (str
, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
893 for (l
= evc
->attributes
; l
; l
= l
->next
) {
895 VFormatAttribute
*attr
= l
->data
;
898 int format_encoding
= VF_ENCODING_RAW
;
900 attr_str
= g_string_new ("");
902 /* From rfc2425, 5.8.2
904 * contentline = [group "."] name *(";" param) ":" value CRLF
908 attr_str
= g_string_append (attr_str
, attr
->group
);
909 attr_str
= g_string_append_c (attr_str
, '.');
911 attr_str
= g_string_append (attr_str
, attr
->name
);
912 /* handle the parameters */
913 for (p
= attr
->params
; p
; p
= p
->next
) {
914 VFormatParam
*param
= p
->data
;
916 * param = param-name "=" param-value *("," param-value)
918 if( type
== VFORMAT_CARD_30
|| type
== VFORMAT_TODO_20
919 || type
== VFORMAT_EVENT_20
) {
922 * Character set can only be specified on the CHARSET
923 * parameter on the Content-Type MIME header field.
925 if (!g_ascii_strcasecmp (param
->name
, "CHARSET"))
927 attr_str
= g_string_append_c (attr_str
, ';');
928 attr_str
= g_string_append (attr_str
, param
->name
);
930 attr_str
= g_string_append_c (attr_str
, '=');
932 for (v
= param
->values
; v
; v
= v
->next
) {
933 if (_helper_is_base64((const char *) v
->data
)) {
934 format_encoding
= VF_ENCODING_BASE64
;
935 /*Only the "B" encoding of [RFC 2047] is an allowed*/
939 * QUOTED-PRINTABLE inline encoding has been
942 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE")) {
943 osync_trace(TRACE_ERROR
, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__
);
944 format_encoding
= VF_ENCODING_QP
;
946 attr_str
= g_string_append (attr_str
, v
->data
);
949 attr_str
= g_string_append_c (attr_str
, ',');
953 attr_str
= g_string_append_c (attr_str
, ';');
955 * The "TYPE=" is optional skip it.
956 * LOGO, PHOTO and SOUND multimedia formats MUST
957 * have a "TYPE=" parameter
959 gboolean must_have_type
= FALSE
;
960 if (!g_ascii_strcasecmp (attr
->name
, "PHOTO") || !g_ascii_strcasecmp (attr
->name
, "LOGO") || !g_ascii_strcasecmp (attr
->name
, "SOUND") )
961 must_have_type
= TRUE
;
962 if ( must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE") )
963 attr_str
= g_string_append (attr_str
, param
->name
);
964 if ( param
->values
&& (must_have_type
|| g_ascii_strcasecmp (param
->name
, "TYPE")) )
965 attr_str
= g_string_append_c (attr_str
, '=');
966 for (v
= param
->values
; v
; v
= v
->next
) {
967 // check for quoted-printable encoding
968 if (!g_ascii_strcasecmp (param
->name
, "ENCODING") && !g_ascii_strcasecmp ((char *) v
->data
, "QUOTED-PRINTABLE"))
969 format_encoding
= VF_ENCODING_QP
;
970 // check for base64 encoding
971 if (_helper_is_base64((const char *) v
->data
)) {
972 format_encoding
= VF_ENCODING_BASE64
;
975 attr_str
= g_string_append (attr_str
, v
->data
);
977 attr_str
= g_string_append_c (attr_str
, ',');
982 attr_str
= g_string_append_c (attr_str
, ':');
984 for (v
= attr
->values
; v
; v
= v
->next
) {
985 char *value
= v
->data
;
986 char *escaped_value
= NULL
;
988 if (!g_ascii_strcasecmp (attr
->name
, "RRULE") &&
989 strstr (value
, "BYDAY") == v
->data
) {
990 attr_str
= g_string_append (attr_str
, value
);
992 escaped_value
= vformat_escape_string (value
, type
);
993 attr_str
= g_string_append (attr_str
, escaped_value
);
998 /* XXX toshok - i hate you, rfc 2426.
999 why doesn't CATEGORIES use a ; like
1000 a normal list attribute? */
1001 if (!g_ascii_strcasecmp (attr
->name
, "CATEGORIES"))
1002 attr_str
= g_string_append_c (attr_str
, ',');
1004 attr_str
= g_string_append_c (attr_str
, ';');
1007 g_free (escaped_value
);
1013 * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
1014 * After generating a content line,
1015 * lines longer than 75 characters SHOULD be folded according to the
1016 * folding procedure described in [MIME-DIR].
1018 * rfc 2445 (iCalendar), 4.1 Content Lines:
1019 * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1020 * break. Long content lines SHOULD be split into a multiple line
1021 * representations using a line "folding" technique. That is, a long
1022 * line can be split between any two characters by inserting a CRLF
1023 * immediately followed by a single linear white space character (i.e.,
1024 * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1025 * of CRLF followed immediately by a single linear white space character
1026 * is ignored (i.e., removed) when processing the content type.
1028 * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
1029 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1031 * Differences between encodings:
1032 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1034 * rfc 2425 [MIME-DIR], 5.8.1:
1035 * A logical line MAY be continued on the next physical line anywhere
1036 * between two characters by inserting a CRLF immediately followed by a
1037 * single <WS> (white space) character.
1039 * rfc 2045, 6.7, chapter 5:
1040 * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
1041 * without follwing <WS>
1046 * Note that all the line folding above is described in terms of characters
1047 * not bytes. In particular, it would be an error to put a line break
1048 * within a UTF-8 character.
1053 if (g_utf8_strlen(attr_str
->str
, attr_str
->len
) - l
> 75) {
1056 /* If using QP, must be sure that we do not fold within a quote sequence */
1057 if (format_encoding
== VF_ENCODING_QP
) {
1058 if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-1)) == '=') l
--;
1059 else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str
->str
, l
-2)) == '=') l
-= 2;
1062 char *p
= g_utf8_offset_to_pointer(attr_str
->str
, l
);
1064 if (format_encoding
== VF_ENCODING_QP
)
1065 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, "=" CRLF
"", sizeof ("=" CRLF
"") - 1);
1067 attr_str
= g_string_insert_len (attr_str
, p
- attr_str
->str
, CRLF
" ", sizeof (CRLF
" ") - 1);
1071 } while (l
< g_utf8_strlen(attr_str
->str
, attr_str
->len
));
1073 attr_str
= g_string_append (attr_str
, CRLF
);
1075 * base64= <MIME RFC 1521 base64 text>
1076 * the end of the text is marked with two CRLF sequences
1077 * this results in one blank line before the start of the
1080 if( format_encoding
== VF_ENCODING_BASE64
1081 && (type
== VFORMAT_CARD_21
))
1082 attr_str
= g_string_append (attr_str
, CRLF
);
1084 str
= g_string_append (str
, attr_str
->str
);
1085 g_string_free (attr_str
, TRUE
);
1089 case VFORMAT_CARD_21
:
1090 str
= g_string_append (str
, "END:VCARD\r\n");
1092 case VFORMAT_CARD_30
:
1093 str
= g_string_append (str
, "END:VCARD\r\n");
1095 case VFORMAT_TODO_10
:
1096 case VFORMAT_EVENT_10
:
1097 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1099 case VFORMAT_TODO_20
:
1100 case VFORMAT_EVENT_20
:
1101 str
= g_string_append (str
, "END:VCALENDAR\r\n");
1104 str
= g_string_append (str
, "END:VNOTE\r\n");
1108 osync_trace(TRACE_EXIT
, "%s(%p, %i)", __func__
, type
);
1109 return g_string_free (str
, FALSE
);
1112 void vformat_dump_structure (VFormat
*evc
)
1118 printf ("VFormat\n");
1119 for (a
= evc
->attributes
; a
; a
= a
->next
) {
1121 VFormatAttribute
*attr
= a
->data
;
1122 printf ("+-- %s\n", attr
->name
);
1124 printf (" +- params=\n");
1126 for (p
= attr
->params
, i
= 0; p
; p
= p
->next
, i
++) {
1127 VFormatParam
*param
= p
->data
;
1128 printf (" | [%d] = %s", i
,param
->name
);
1130 for (v
= param
->values
; v
; v
= v
->next
) {
1131 char *value
= vformat_escape_string ((char*)v
->data
, VFORMAT_CARD_21
);
1132 printf ("%s", value
);
1141 printf (" +- values=\n");
1142 for (v
= attr
->values
, i
= 0; v
; v
= v
->next
, i
++) {
1143 printf (" [%d] = `%s'\n", i
, (char*)v
->data
);
1148 VFormatAttribute
*vformat_attribute_new (const char *attr_group
, const char *attr_name
)
1150 VFormatAttribute
*attr
;
1152 attr
= g_new0 (VFormatAttribute
, 1);
1154 attr
->group
= g_strdup (attr_group
);
1155 attr
->name
= g_strdup (attr_name
);
1161 vformat_attribute_free (VFormatAttribute
*attr
)
1163 g_return_if_fail (attr
!= NULL
);
1165 g_free (attr
->group
);
1166 g_free (attr
->name
);
1168 vformat_attribute_remove_values (attr
);
1170 vformat_attribute_remove_params (attr
);
1176 vformat_attribute_copy (VFormatAttribute
*attr
)
1178 VFormatAttribute
*a
;
1181 g_return_val_if_fail (attr
!= NULL
, NULL
);
1183 a
= vformat_attribute_new (vformat_attribute_get_group (attr
),
1184 vformat_attribute_get_name (attr
));
1186 for (p
= attr
->values
; p
; p
= p
->next
)
1187 vformat_attribute_add_value (a
, p
->data
);
1189 for (p
= attr
->params
; p
; p
= p
->next
)
1190 vformat_attribute_add_param (a
, vformat_attribute_param_copy (p
->data
));
1196 vformat_remove_attributes (VFormat
*evc
, const char *attr_group
, const char *attr_name
)
1200 g_return_if_fail (attr_name
!= NULL
);
1202 attr
= evc
->attributes
;
1205 VFormatAttribute
*a
= attr
->data
;
1207 next_attr
= attr
->next
;
1209 if (((!attr_group
&& !a
->group
) ||
1210 (attr_group
&& !g_ascii_strcasecmp (attr_group
, a
->group
))) &&
1211 ((!attr_name
&& !a
->name
) || !g_ascii_strcasecmp (attr_name
, a
->name
))) {
1213 /* matches, remove/delete the attribute */
1214 evc
->attributes
= g_list_remove_link (evc
->attributes
, attr
);
1216 vformat_attribute_free (a
);
1224 vformat_remove_attribute (VFormat
*evc
, VFormatAttribute
*attr
)
1226 g_return_if_fail (attr
!= NULL
);
1228 evc
->attributes
= g_list_remove (evc
->attributes
, attr
);
1229 vformat_attribute_free (attr
);
1233 vformat_add_attribute (VFormat
*evc
, VFormatAttribute
*attr
)
1235 g_return_if_fail (attr
!= NULL
);
1237 evc
->attributes
= g_list_append (evc
->attributes
, attr
);
1241 vformat_add_attribute_with_value (VFormat
*VFormat
,
1242 VFormatAttribute
*attr
, const char *value
)
1244 g_return_if_fail (attr
!= NULL
);
1246 vformat_attribute_add_value (attr
, value
);
1248 vformat_add_attribute (VFormat
, attr
);
1252 vformat_add_attribute_with_values (VFormat
*VFormat
, VFormatAttribute
*attr
, ...)
1257 g_return_if_fail (attr
!= NULL
);
1259 va_start (ap
, attr
);
1261 while ((v
= va_arg (ap
, char*))) {
1262 vformat_attribute_add_value (attr
, v
);
1267 vformat_add_attribute (VFormat
, attr
);
1271 vformat_attribute_add_value (VFormatAttribute
*attr
, const char *value
)
1273 g_return_if_fail (attr
!= NULL
);
1275 attr
->values
= g_list_append (attr
->values
, g_strdup (value
));
1279 vformat_attribute_add_value_decoded (VFormatAttribute
*attr
, const char *value
, int len
)
1281 g_return_if_fail (attr
!= NULL
);
1283 switch (attr
->encoding
) {
1284 case VF_ENCODING_RAW
:
1285 osync_trace(TRACE_INTERNAL
, "can't add_value_decoded with an attribute using RAW encoding. you must set the ENCODING parameter first");
1287 case VF_ENCODING_BASE64
: {
1288 char *b64_data
= base64_encode_simple (value
, len
);
1289 GString
*decoded
= g_string_new_len (value
, len
);
1291 /* make sure the decoded list is up to date */
1292 vformat_attribute_get_values_decoded (attr
);
1294 attr
->values
= g_list_append (attr
->values
, b64_data
);
1295 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1298 case VF_ENCODING_QP
: {
1299 char *qp_data
= quoted_encode_simple ((unsigned char*)value
, len
);
1300 GString
*decoded
= g_string_new (value
);
1302 /* make sure the decoded list is up to date */
1303 vformat_attribute_get_values_decoded (attr
);
1305 attr
->values
= g_list_append (attr
->values
, qp_data
);
1306 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1309 case VF_ENCODING_8BIT
: {
1310 char *data
= g_strdup(value
);
1311 GString
*decoded
= g_string_new (value
);
1313 /* make sure the decoded list is up to date */
1314 vformat_attribute_get_values_decoded (attr
);
1316 attr
->values
= g_list_append (attr
->values
, data
);
1317 attr
->decoded_values
= g_list_append (attr
->decoded_values
, decoded
);
1324 vformat_attribute_add_values (VFormatAttribute
*attr
, ...)
1329 g_return_if_fail (attr
!= NULL
);
1331 va_start (ap
, attr
);
1333 while ((v
= va_arg (ap
, char*))) {
1334 vformat_attribute_add_value (attr
, v
);
1341 free_gstring (GString
*str
)
1343 g_string_free (str
, TRUE
);
1347 vformat_attribute_remove_values (VFormatAttribute
*attr
)
1349 g_return_if_fail (attr
!= NULL
);
1351 g_list_foreach (attr
->values
, (GFunc
)g_free
, NULL
);
1352 g_list_free (attr
->values
);
1353 attr
->values
= NULL
;
1355 g_list_foreach (attr
->decoded_values
, (GFunc
)free_gstring
, NULL
);
1356 g_list_free (attr
->decoded_values
);
1357 attr
->decoded_values
= NULL
;
1361 vformat_attribute_remove_params (VFormatAttribute
*attr
)
1363 g_return_if_fail (attr
!= NULL
);
1365 g_list_foreach (attr
->params
, (GFunc
)vformat_attribute_param_free
, NULL
);
1366 g_list_free (attr
->params
);
1367 attr
->params
= NULL
;
1369 /* also remove the cached encoding on this attribute */
1370 attr
->encoding_set
= FALSE
;
1371 attr
->encoding
= VF_ENCODING_RAW
;
1375 vformat_attribute_param_new (const char *name
)
1377 VFormatParam
*param
= g_new0 (VFormatParam
, 1);
1378 param
->name
= g_strdup (name
);
1384 vformat_attribute_param_free (VFormatParam
*param
)
1386 g_return_if_fail (param
!= NULL
);
1388 g_free (param
->name
);
1390 vformat_attribute_param_remove_values (param
);
1396 vformat_attribute_param_copy (VFormatParam
*param
)
1401 g_return_val_if_fail (param
!= NULL
, NULL
);
1403 p
= vformat_attribute_param_new (vformat_attribute_param_get_name (param
));
1405 for (l
= param
->values
; l
; l
= l
->next
) {
1406 vformat_attribute_param_add_value (p
, l
->data
);
1413 vformat_attribute_add_param (VFormatAttribute
*attr
,
1414 VFormatParam
*param
)
1416 g_return_if_fail (attr
!= NULL
);
1417 g_return_if_fail (param
!= NULL
);
1419 attr
->params
= g_list_append (attr
->params
, param
);
1421 /* we handle our special encoding stuff here */
1423 if (!g_ascii_strcasecmp (param
->name
, "ENCODING")) {
1424 if (attr
->encoding_set
) {
1425 osync_trace(TRACE_INTERNAL
, "ENCODING specified twice");
1429 if (param
->values
&& param
->values
->data
) {
1430 if (_helper_is_base64((const char*)param
->values
->data
))
1431 attr
->encoding
= VF_ENCODING_BASE64
;
1432 else if (!g_ascii_strcasecmp ((char*)param
->values
->data
, "QUOTED-PRINTABLE"))
1433 attr
->encoding
= VF_ENCODING_QP
;
1434 else if (!g_ascii_strcasecmp ((char *)param
->values
->data
, "8BIT"))
1435 attr
->encoding
= VF_ENCODING_8BIT
;
1437 osync_trace(TRACE_INTERNAL
, "Unknown value `%s' for ENCODING parameter. values will be treated as raw",
1438 (char*)param
->values
->data
);
1441 attr
->encoding_set
= TRUE
;
1444 osync_trace(TRACE_INTERNAL
, "ENCODING parameter added with no value");
1449 VFormatParam
*vformat_attribute_find_param(VFormatAttribute
*attr
, const char *name
)
1451 g_return_val_if_fail (attr
!= NULL
, NULL
);
1453 for (p
= attr
->params
; p
; p
= p
->next
) {
1454 VFormatParam
*param
= p
->data
;
1455 if (!g_ascii_strcasecmp (param
->name
, name
))
1462 vformat_attribute_set_value (VFormatAttribute
*attr
,
1463 int nth
, const char *value
)
1465 GList
*param
= g_list_nth(attr
->values
, nth
);
1466 g_free(param
->data
);
1467 param
->data
= g_strdup(value
);
1471 vformat_attribute_param_add_value (VFormatParam
*param
,
1474 g_return_if_fail (param
!= NULL
);
1476 param
->values
= g_list_append (param
->values
, g_strdup (value
));
1480 vformat_attribute_param_add_values (VFormatParam
*param
,
1486 g_return_if_fail (param
!= NULL
);
1488 va_start (ap
, param
);
1490 while ((v
= va_arg (ap
, char*))) {
1491 vformat_attribute_param_add_value (param
, v
);
1498 vformat_attribute_add_param_with_value (VFormatAttribute
*attr
, const char *name
, const char *value
)
1500 g_return_if_fail (attr
!= NULL
);
1501 g_return_if_fail (name
!= NULL
);
1506 VFormatParam
*param
= vformat_attribute_param_new(name
);
1508 vformat_attribute_param_add_value (param
, value
);
1510 vformat_attribute_add_param (attr
, param
);
1514 vformat_attribute_add_param_with_values (VFormatAttribute
*attr
,
1515 VFormatParam
*param
, ...)
1520 g_return_if_fail (attr
!= NULL
);
1521 g_return_if_fail (param
!= NULL
);
1523 va_start (ap
, param
);
1525 while ((v
= va_arg (ap
, char*))) {
1526 vformat_attribute_param_add_value (param
, v
);
1531 vformat_attribute_add_param (attr
, param
);
1535 vformat_attribute_param_remove_values (VFormatParam
*param
)
1537 g_return_if_fail (param
!= NULL
);
1539 g_list_foreach (param
->values
, (GFunc
)g_free
, NULL
);
1540 g_list_free (param
->values
);
1541 param
->values
= NULL
;
1545 vformat_get_attributes (VFormat
*format
)
1547 return format
->attributes
;
1551 vformat_attribute_get_group (VFormatAttribute
*attr
)
1553 g_return_val_if_fail (attr
!= NULL
, NULL
);
1559 vformat_attribute_get_name (VFormatAttribute
*attr
)
1561 g_return_val_if_fail (attr
!= NULL
, NULL
);
1567 vformat_attribute_get_values (VFormatAttribute
*attr
)
1569 g_return_val_if_fail (attr
!= NULL
, NULL
);
1571 return attr
->values
;
1575 vformat_attribute_get_values_decoded (VFormatAttribute
*attr
)
1577 g_return_val_if_fail (attr
!= NULL
, NULL
);
1579 if (!attr
->decoded_values
) {
1581 switch (attr
->encoding
) {
1582 case VF_ENCODING_RAW
:
1583 case VF_ENCODING_8BIT
:
1584 for (l
= attr
->values
; l
; l
= l
->next
)
1585 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new ((char*)l
->data
));
1587 case VF_ENCODING_BASE64
:
1588 for (l
= attr
->values
; l
; l
= l
->next
) {
1589 char *decoded
= g_strdup ((char*)l
->data
);
1590 int len
= base64_decode_simple (decoded
, strlen (decoded
));
1591 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1595 case VF_ENCODING_QP
:
1596 for (l
= attr
->values
; l
; l
= l
->next
) {
1599 char *decoded
= g_strdup ((char*)l
->data
);
1600 int len
= quoted_decode_simple (decoded
, strlen (decoded
));
1601 attr
->decoded_values
= g_list_append (attr
->decoded_values
, g_string_new_len (decoded
, len
));
1608 return attr
->decoded_values
;
1612 vformat_attribute_is_single_valued (VFormatAttribute
*attr
)
1614 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1616 if (attr
->values
== NULL
1617 || attr
->values
->next
!= NULL
)
1624 vformat_attribute_get_value (VFormatAttribute
*attr
)
1628 g_return_val_if_fail (attr
!= NULL
, NULL
);
1630 values
= vformat_attribute_get_values (attr
);
1632 if (!vformat_attribute_is_single_valued (attr
))
1633 osync_trace(TRACE_INTERNAL
, "vformat_attribute_get_value called on multivalued attribute");
1635 return values
? g_strdup ((char*)values
->data
) : NULL
;
1639 vformat_attribute_get_value_decoded (VFormatAttribute
*attr
)
1642 GString
*str
= NULL
;
1644 g_return_val_if_fail (attr
!= NULL
, NULL
);
1646 values
= vformat_attribute_get_values_decoded (attr
);
1648 if (!vformat_attribute_is_single_valued (attr
))
1649 osync_trace(TRACE_INTERNAL
, "vformat_attribute_get_value_decoded called on multivalued attribute");
1654 return str
? g_string_new_len (str
->str
, str
->len
) : NULL
;
1657 const char *vformat_attribute_get_nth_value(VFormatAttribute
*attr
, int nth
)
1659 GList
*values
= vformat_attribute_get_values_decoded(attr
);
1662 GString
*retstr
= (GString
*)g_list_nth_data(values
, nth
);
1666 if (!g_utf8_validate(retstr
->str
, -1, NULL
)) {
1667 values
= vformat_attribute_get_values(attr
);
1670 return g_list_nth_data(values
, nth
);
1677 vformat_attribute_has_type (VFormatAttribute
*attr
, const char *typestr
)
1682 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1683 g_return_val_if_fail (typestr
!= NULL
, FALSE
);
1685 params
= vformat_attribute_get_params (attr
);
1687 for (p
= params
; p
; p
= p
->next
) {
1688 VFormatParam
*param
= p
->data
;
1690 if (!strcasecmp (vformat_attribute_param_get_name (param
), "TYPE")) {
1691 GList
*values
= vformat_attribute_param_get_values (param
);
1694 for (v
= values
; v
; v
= v
->next
) {
1695 if (!strcasecmp ((char*)v
->data
, typestr
))
1705 gboolean
vformat_attribute_has_param(VFormatAttribute
*attr
, const char *name
)
1707 g_return_val_if_fail (attr
!= NULL
, FALSE
);
1708 g_return_val_if_fail (name
!= NULL
, FALSE
);
1710 GList
*params
= vformat_attribute_get_params(attr
);
1712 for (p
= params
; p
; p
= p
->next
) {
1713 VFormatParam
*param
= p
->data
;
1714 if (!strcasecmp(name
, vformat_attribute_param_get_name(param
)))
1721 vformat_attribute_get_params (VFormatAttribute
*attr
)
1723 g_return_val_if_fail (attr
!= NULL
, NULL
);
1725 return attr
->params
;
1729 vformat_attribute_param_get_name (VFormatParam
*param
)
1731 g_return_val_if_fail (param
!= NULL
, NULL
);
1737 vformat_attribute_param_get_values (VFormatParam
*param
)
1739 g_return_val_if_fail (param
!= NULL
, NULL
);
1741 return param
->values
;
1744 const char *vformat_attribute_param_get_nth_value(VFormatParam
*param
, int nth
)
1746 const char *ret
= NULL
;
1747 GList
*values
= vformat_attribute_param_get_values(param
);
1750 ret
= g_list_nth_data(values
, nth
);
1754 static const char *base64_alphabet
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1756 //static unsigned char _evc_base64_rank[256];
1758 static void base64_init(char *rank
)
1762 memset(rank
, 0xff, sizeof(rank
));
1763 for (i
=0;i
<64;i
++) {
1764 rank
[(unsigned int)base64_alphabet
[i
]] = i
;
1769 /* call this when finished encoding everything, to
1770 flush off the last little bit */
1771 static size_t base64_encode_close(unsigned char *in
, size_t inlen
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1774 unsigned char *outptr
= out
;
1777 outptr
+= base64_encode_step(in
, inlen
, break_lines
, outptr
, state
, save
);
1779 c1
= ((unsigned char *)save
)[1];
1780 c2
= ((unsigned char *)save
)[2];
1782 switch (((char *)save
)[0]) {
1784 outptr
[2] = base64_alphabet
[ ( (c2
&0x0f) << 2 ) ];
1785 g_assert(outptr
[2] != 0);
1790 outptr
[0] = base64_alphabet
[ c1
>> 2 ];
1791 outptr
[1] = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 )];
1806 performs an 'encode step', only encodes blocks of 3 characters to the
1807 output at a time, saves left-over state in state and save (initialise to
1808 0 on first invocation).
1810 static size_t base64_encode_step(unsigned char *in
, size_t len
, gboolean break_lines
, unsigned char *out
, int *state
, int *save
)
1812 register unsigned char *inptr
, *outptr
;
1820 if (len
+ ((char *)save
)[0] > 2) {
1821 unsigned char *inend
= in
+len
-2;
1822 register int c1
, c2
, c3
;
1823 register int already
;
1827 switch (((char *)save
)[0]) {
1828 case 1: c1
= ((unsigned char *)save
)[1]; goto skip1
;
1829 case 2: c1
= ((unsigned char *)save
)[1];
1830 c2
= ((unsigned char *)save
)[2]; goto skip2
;
1833 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1834 while (inptr
< inend
) {
1840 *outptr
++ = base64_alphabet
[ c1
>> 2 ];
1841 *outptr
++ = base64_alphabet
[ c2
>> 4 | ( (c1
&0x3) << 4 ) ];
1842 *outptr
++ = base64_alphabet
[ ( (c2
&0x0f) << 2 ) | (c3
>> 6) ];
1843 *outptr
++ = base64_alphabet
[ c3
& 0x3f ];
1844 /* this is a bit ugly ... */
1845 if (break_lines
&& (++already
)>=19) {
1851 ((char *)save
)[0] = 0;
1852 len
= 2-(inptr
-inend
);
1857 register char *saveout
;
1859 /* points to the slot for the next char to save */
1860 saveout
= & (((char *)save
)[1]) + ((char *)save
)[0];
1862 /* len can only be 0 1 or 2 */
1864 case 2: *saveout
++ = *inptr
++;
1865 case 1: *saveout
++ = *inptr
++;
1867 ((char *)save
)[0]+=len
;
1875 * base64_decode_step: decode a chunk of base64 encoded data
1877 * @len: max length of data to decode
1878 * @out: output stream
1879 * @state: holds the number of bits that are stored in @save
1880 * @save: leftover bits that have not yet been decoded
1882 * Decodes a chunk of base64 encoded data
1884 static size_t base64_decode_step(unsigned char *in
, size_t len
, unsigned char *out
, int *state
, unsigned int *save
)
1886 unsigned char base64_rank
[256];
1887 base64_init((char*)base64_rank
);
1889 register unsigned char *inptr
, *outptr
;
1890 unsigned char *inend
, c
;
1891 register unsigned int v
;
1897 /* convert 4 base64 bytes to 3 normal bytes */
1901 while (inptr
<inend
) {
1902 c
= base64_rank
[*inptr
++];
1918 /* quick scan back for '=' on the end somewhere */
1919 /* fortunately we can drop 1 output char for each trailing = (upto 2) */
1921 while (inptr
>in
&& i
) {
1923 if (base64_rank
[*inptr
] != 0xff) {
1924 if (*inptr
== '=' && outptr
>out
)
1930 /* if i!= 0 then there is a truncation error! */
1934 char *base64_encode_simple (const char *data
, size_t len
)
1937 int state
= 0, outlen
;
1938 unsigned int save
= 0;
1940 g_return_val_if_fail (data
!= NULL
, NULL
);
1942 out
= g_malloc (len
* 4 / 3 + 5);
1943 outlen
= base64_encode_close ((unsigned char *)data
, len
, FALSE
,
1944 out
, &state
, (int*)&save
);
1949 size_t base64_decode_simple (char *data
, size_t len
)
1952 unsigned int save
= 0;
1954 g_return_val_if_fail (data
!= NULL
, 0);
1956 return base64_decode_step ((unsigned char *)data
, len
,
1957 (unsigned char *)data
, &state
, &save
);
1960 char *quoted_encode_simple(const unsigned char *string
, int len
)
1962 GString
*tmp
= g_string_new("");
1965 while(string
[i
] != 0) {
1966 if (string
[i
] > 127 || string
[i
] == 13 || string
[i
] == 10 || string
[i
] == '=') {
1967 g_string_append_printf(tmp
, "=%02X", string
[i
]);
1969 g_string_append_c(tmp
, string
[i
]);
1974 char *ret
= tmp
->str
;
1975 g_string_free(tmp
, FALSE
);
1980 size_t quoted_decode_simple (char *data
, size_t len
)
1982 g_return_val_if_fail (data
!= NULL
, 0);
1984 GString
*string
= g_string_new(data
);
1992 //Get the index of the next encoded char
1993 int i
= strcspn(string
->str
, "=");
1994 if (i
>= strlen(string
->str
))
1998 strncat(hex
, &string
->str
[i
+ 1], 2);
1999 char rep
= ((int)(strtod(hex
, NULL
)));
2000 g_string_erase(string
, i
, 2);
2001 g_string_insert_c(string
, i
, rep
);
2004 memset(data
, 0, strlen(data
));
2005 strcpy(data
, string
->str
);
2006 g_string_free(string
, 1);
2008 return strlen(data
);