# vformat.c

    00001 /*
    00002  * Copyright (C) 2003 Ximian, Inc.
    00003  *
    00004  * This library is free software; you can redistribute it and/or
    00005  * modify it under the terms of the GNU Lesser General Public
    00006  * License as published by the Free Software Foundation; either
    00007  * version 2.1 of the License, or (at your option) any later version.
    00008  *
    00009  * This library is distributed in the hope that it will be useful,
    00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00012  * Lesser General Public License for more details.
    00013  *
    00014  * You should have received a copy of the GNU Lesser General Public
    00015  * License along with this library; if not, write to the Free Software
    00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    00017  *
    00018  * Author: Chris Toshok (toshok@ximian.com)
    00019  * Author: Armin Bauer (armin.bauer@opensync.org)
    00020  *
    00021  */
    00022
    00023 #include "vformat.h"
    00024 #include "clog.h"
    00025
    00026 //#ifdef HAVE_CONFIG_H
    00027 //#include "config.h"
    00028 //#endif
    00029
    00030 #include <string.h>
    00031 #include <stdio.h>
    00032 #include <ctype.h>
    00033 #include <stdlib.h>
    00034 #include <iconv.h>
    00035 //#include <opensync/opensync.h>
    00036
    00037 #define TRACE_INTERNAL 1
    00038 #define TRACE_ENTRY 1
    00039 #define TRACE_EXIT 1
    00040 #define TRACE_ERROR 0
    00041
    00042 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save);
    00043 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save);
    00044 static size_t base64_decode_simple (char *data, size_t len);
    00045 static char  *base64_encode_simple (const char *data, size_t len);
    00046
    00047 static size_t quoted_decode_simple (char *data, size_t len);
    00048 static char *quoted_encode_simple (const unsigned char *string, int len);
    00049
    00050
    00051 /**
    00052  * _helper_is_base64 is helper function to check i a string is "b" or "base64"
    00053  * @param check_string string that should be compared with "b" or "base64"
    00054  * @return 0 if check_string is not base64  and 1 if it is
    00055  */
    00056 static int _helper_is_base64(const char *check_string)
    00057 {
    00058         if(!g_ascii_strcasecmp ((char *) check_string, "BASE64") ||
    00059            !g_ascii_strcasecmp ((char *) check_string, "b") )
    00060                 return (1);
    00061         return (0);
    00062 }
    00063
    00064 time_t b_vformat_time_to_unix(const char *inptime)
    00065 {
    00066         char *date = NULL;
    00067         char *time = NULL;
    00068         char *ftime = NULL;
    00069         if ((ftime = g_strrstr(inptime, "T"))) {
    00070
    00071                 date = g_strndup(inptime, ftime - inptime);
    00072                 if (ftime[3] == ':')
    00073                         time = g_strndup(ftime + 1, 8);
    00074                 else
    00075                         time = g_strndup(ftime + 1, 6);
    00076         } else {
    00077                 date = g_strdup(inptime);
    00078         }
    00079
    00080         struct tm btime;
    00081         memset(&btime, 0, sizeof(struct tm));
    00082         btime.tm_isdst = -1;
    00083
    00084         if (strlen(date) == 10) {
    00085                 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111 - 1900;
    00086                 btime.tm_mon = date[5] * 10 + date[6] - '0' * 11 - 1;
    00087                 btime.tm_mday = date[8] * 10 + date[9] - '0' * 11;
    00088         } else {
    00089                 btime.tm_year = date[0] * 1000 + date[1] * 100 + date[2] * 10 + date[3] - '0' * 1111- 1900;
    00090                 btime.tm_mon = date[4] * 10 + date[5] - '0' * 11 - 1;
    00091                 btime.tm_mday = date[6] * 10 + date[7] - '0' * 11;
    00092         }
    00093
    00094         if (time && strlen(time) == 8) {
    00095                 //Time
    00096                 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
    00097                 btime.tm_min = time[3] * 10 + time[4] - '0' * 11;
    00098                 btime.tm_sec = time[6] * 10 + time[7] - '0' * 11;
    00099         } else if (time && strlen(time) == 6) {
    00100                 btime.tm_hour = time[0] * 10 + time[1] - '0' * 11;
    00101                 btime.tm_min = time[2] * 10 + time[3] - '0' * 11;
    00102                 btime.tm_sec = time[4] * 10 + time[5] - '0' * 11;
    00103         }
    00104
    00105         time_t utime = mktime(&btime);
    00106         return utime;
    00107 }
    00108
    00109 static char *_fold_lines (char *buf)
    00110 {
    00111         GString *str = g_string_new ("");
    00112         GString *line = g_string_new ("");
    00113         char *p = buf;
    00114         char *next, *next2, *q;
    00115         gboolean newline = TRUE;
    00116         gboolean quotedprintable = FALSE;
    00117
    00118         /*
    00119          *  We're pretty liberal with line folding here. We handle
    00120          *  lines folded with \r\n<WS>, \n\r<WS>, \n<WS>, =\r\n and =\n\r.
    00121          *  We also turn single \r's and \n's not followed by <WS> into \r\n's.
    00122          */
    00123
    00124         while (*p) {
    00125
    00126                 /* search new lines for quoted printable encoding */
    00127                 if (newline) {
    00128                         for (q=p; *q != '\n' && *q != '\0'; q++)
    00129                                 line = g_string_append_unichar (line, g_utf8_get_char (q));
    00130
    00131                         if (strstr(line->str, "ENCODING=QUOTED-PRINTABLE"))
    00132                                 quotedprintable = TRUE;
    00133
    00134                         g_string_free(line, TRUE);
    00135                         line = g_string_new ("");
    00136
    00137                         newline = FALSE;
    00138                 }
    00139
    00140
    00141                 if ((quotedprintable && *p == '=') || *p == '\r' || *p == '\n') {
    00142                         next = g_utf8_next_char (p);
    00143                         if (*next == '\n' || *next == '\r') {
    00144                                 next2 = g_utf8_next_char (next);
    00145                                 if (*next2 == '\n' || *next2 == '\r' || *next2 == ' ' || *next2 == '\t') {
    00146                                         p = g_utf8_next_char (next2);
    00147                                 }
    00148                                 else if(quotedprintable) {
    00149                                         p = g_utf8_next_char (next);
    00150                                 }
    00151                                 else {
    00152                                         str = g_string_append (str, CRLF);
    00153                                         p = g_utf8_next_char (next);
    00154                                         newline = TRUE;
    00155                                         quotedprintable = FALSE;
    00156                                 }
    00157                         }
    00158                         else if (*p == '=') {
    00159                                 str = g_string_append_unichar (str, g_utf8_get_char (p));
    00160                                 p = g_utf8_next_char (p);
    00161                         }
    00162                         else if (*next == ' ' || *next == '\t') {
    00163                                 p = g_utf8_next_char (next);
    00164                         }
    00165                         else {
    00166                                 str = g_string_append (str, CRLF);
    00167                                 p = g_utf8_next_char (p);
    00168                                 newline = TRUE;
    00169                                 quotedprintable = FALSE;
    00170                         }
    00171                 }
    00172                 else {
    00173                         str = g_string_append_unichar (str, g_utf8_get_char (p));
    00174                         p = g_utf8_next_char (p);
    00175                 }
    00176         }
    00177
    00178         g_free (buf);
    00179         g_string_free(line, TRUE);
    00180
    00181         return g_string_free (str, FALSE);
    00182 }
    00183
    00184 /* skip forward until we hit the CRLF, or \0 */
    00185 static void _skip_to_next_line (char **p)
    00186 {
    00187         char *lp;
    00188         lp = *p;
    00189
    00190         while (*lp != '\r' && *lp != '\0')
    00191                 lp = g_utf8_next_char (lp);
    00192
    00193         if (*lp == '\r') {
    00194                 lp = g_utf8_next_char (lp); /* \n */
    00195                 lp = g_utf8_next_char (lp); /* start of the next line */
    00196         }
    00197
    00198         *p = lp;
    00199 }
    00200
    00201 /* skip forward until we hit a character in @s, CRLF, or \0.  leave *p
    00202    pointing at the character that causes us to stop */
    00203 static void _skip_until (char **p, char *s)
    00204 {
    00205         char *lp;
    00206
    00207         lp = *p;
    00208
    00209         while (*lp != '\r' && *lp != '\0') {
    00210                 gboolean s_matches = FALSE;
    00211                 char *ls;
    00212                 for (ls = s; *ls; ls = g_utf8_next_char (ls)) {
    00213                         if (g_utf8_get_char (ls) == g_utf8_get_char (lp)) {
    00214                                 s_matches = TRUE;
    00215                                 break;
    00216                         }
    00217                 }
    00218
    00219                 if (s_matches)
    00220                         break;
    00221                 lp++;
    00222         }
    00223
    00224         *p = lp;
    00225 }
    00226
    00227 static void _read_attribute_value_add (b_VFormatAttribute *attr, GString *str, GString *charset)
    00228 {
    00229         /* don't convert empty strings */
    00230         if (str->len == 0) {
    00231                 b_vformat_attribute_add_value(attr, str->str);
    00232                 return;
    00233         }
    00234
    00235         char *inbuf, *outbuf, *p;
    00236         size_t inbytesleft, outbytesleft;
    00237
    00238         inbuf = str->str;
    00239         p = outbuf = malloc(str->len*2);
    00240         inbytesleft = str->len;
    00241         outbytesleft = str->len*2;
    00242
    00243         iconv_t cd;
    00244
    00245         /* if a CHARSET was given, let's try to convert inbuf to UTF-8 */
    00246         if (charset) {
    00247
    00248                 cd = iconv_open("UTF-8", charset->str);
    00249 #ifdef SOLARIS
    00250                 if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
    00251 #else
    00252                 if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
    00253 #endif
    00254                         *p = 0;
    00255                         b_vformat_attribute_add_value(attr, outbuf);
    00256
    00257                 } else {
    00258
    00259                         /* hmm, should not happen */
    00260                         b_vformat_attribute_add_value(attr, str->str);
    00261
    00262                 }
    00263
    00264                 iconv_close(cd);
    00265
    00266         } else {
    00267
    00268                 /* no CHARSET was given, if inbuf is already UTF-8 we add str->str */
    00269                 if (g_utf8_validate (inbuf, -1, NULL)) {
    00270
    00271                         b_vformat_attribute_add_value (attr, str->str);
    00272
    00273                 } else {
    00274
    00275                         /* because inbuf is not UTF-8, we think it is ISO-8859-1 */
    00276                         cd = iconv_open("UTF-8", "ISO-8859-1");
    00277 #ifdef SOLARIS
    00278                         if (iconv(cd, (const char**)&inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
    00279 #else
    00280                         if (iconv(cd, &inbuf, &inbytesleft, &p, &outbytesleft) != (size_t)(-1)) {
    00281 #endif
    00282                                 *p = 0;
    00283                                 b_vformat_attribute_add_value (attr, outbuf);
    00284
    00285                         } else {
    00286
    00287                                 b_vformat_attribute_add_value (attr, str->str);
    00288
    00289                         }
    00290
    00291                         iconv_close(cd);
    00292
    00293                 }
    00294
    00295         }
    00296
    00297         free(outbuf);
    00298
    00299 }
    00300
    00301 static void _read_attribute_value (b_VFormatAttribute *attr, char **p, int format_encoding, GString *charset)
    00302 {
    00303         char *lp = *p;
    00304         GString *str;
    00305
    00306         /* read in the value */
    00307         str = g_string_new ("");
    00308         while (*lp != '\r' && *lp != '\0') {
    00309                 if (*lp == '=' && format_encoding == VF_ENCODING_QP) {
    00310                         char a, b, x1=0, x2=0;
    00311
    00312                         if ((a = *(++lp)) == '\0') break;
    00313                         if ((b = *(++lp)) == '\0') break;
    00314
    00315                         if (isalnum(a)) {
    00316                                 if (isalnum(b)) {
    00317                                         /* e.g. ...N=C3=BCrnberg\r\n
    00318                                          *          ^^^
    00319                                          */
    00320                                         x1=a;
    00321                                         x2=b;
    00322                                 }
    00323                                 else if (b == '=') {
    00324                                         /* e.g. ...N=C=\r\n
    00325                                          *          ^^^
    00326                                          * 3=BCrnberg...
    00327                                          * ^
    00328                                          */
    00329                                         char *tmplp = lp;
    00330                                         if (*(++tmplp) == '\r' && *(++tmplp) == '\n' && isalnum(*(++tmplp))) {
    00331                                                 x1 = a;
    00332                                                 x2 = *tmplp;
    00333                                                 lp = tmplp;
    00334                                         }
    00335                                 }
    00336                                 else {
    00337                                         /* append malformed input, and
    00338                                            continue parsing */
    00339                                         str = g_string_append_c(str, a);
    00340                                         str = g_string_append_c(str, b);
    00341                                 }
    00342                         }
    00343                         else if (a == '=') {
    00344                                 char *tmplp = lp;
    00345                                 char c, d, e;
    00346                                 c = *(++tmplp);
    00347                                 d = *(++tmplp);
    00348                                 e = *(++tmplp);
    00349                                 if (b == '\r' && c == '\n' && isalnum(d) && isalnum(e)) {
    00350                                         x1 = d;
    00351                                         x2 = e;
    00352                                         lp = tmplp;
    00353                                 }
    00354                                 else {
    00355                                         /* append malformed input, and
    00356                                            continue parsing */
    00357                                         str = g_string_append_c(str, a);
    00358                                         str = g_string_append_c(str, b);
    00359                                 }
    00360                         }
    00361                         else {
    00362                                 /* append malformed input, and
    00363                                    continue parsing */
    00364                                 str = g_string_append_c(str, a);
    00365                                 str = g_string_append_c(str, b);
    00366                         }
    00367                         if (x1 && x2) {
    00368                                 char c;
    00369
    00370                                 a = tolower (x1);
    00371                                 b = tolower (x2);
    00372
    00373                                 c = (((a>='a'?a-'a'+10:a-'0')&0x0f) << 4)
    00374                                         | ((b>='a'?b-'a'+10:b-'0')&0x0f);
    00375
    00376                                 str = g_string_append_c (str, c);
    00377                         }
    00378                         lp++;
    00379                         x1 = x2 = 0;
    00380                 }
    00381                 else if (format_encoding == VF_ENCODING_BASE64) {
    00382                         if((*lp != ' ') && (*lp != '\t') )
    00383                                 str = g_string_append_unichar (str, g_utf8_get_char (lp));
    00384                         lp = g_utf8_next_char(lp);
    00385                 }
    00386                 else if (*lp == '\\') {
    00387                         /* convert back to the non-escaped version of
    00388                            the characters */
    00389                         lp = g_utf8_next_char(lp);
    00390                         if (*lp == '\0') {
    00391                                 str = g_string_append_c (str, '\\');
    00392                                 break;
    00393                         }
    00394                         switch (*lp) {
    00395                                 case 'n': str = g_string_append_c (str, '\n'); break;
    00396                                 case 'r': str = g_string_append_c (str, '\r'); break;
    00397                                 case ';': str = g_string_append_c (str, ';'); break;
    00398                                 case ',':
    00399                                         if (!g_ascii_strcasecmp (attr->name, "CATEGORIES")) {
    00400                                                 //We need to handle categories here to work
    00401                                                 //aroung a bug in evo2
    00402                                                 _read_attribute_value_add (attr, str, charset);
    00403                                                 g_string_assign (str, "");
    00404                                         } else
    00405                                                 str = g_string_append_c (str, ',');
    00406                                         break;
    00407                                 case '\\': str = g_string_append_c (str, '\\'); break;
    00408                                 case '"': str = g_string_append_c (str, '"'); break;
    00409                                   /* \t is (incorrectly) used by kOrganizer, so handle it here */
    00410                                 case 't': str = g_string_append_c (str, '\t'); break;
    00411                                 default:
    00412                                         BarryLogf(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*lp);
    00413                                         str = g_string_append_c (str, '\\');
    00414                                         str = g_string_append_unichar (str, g_utf8_get_char(lp));
    00415                                         break;
    00416                         }
    00417                         lp = g_utf8_next_char(lp);
    00418                 }
    00419                 else if ((*lp == ';') ||
    00420                          (*lp == ',' && !g_ascii_strcasecmp (attr->name, "CATEGORIES"))) {
    00421                         _read_attribute_value_add (attr, str, charset);
    00422                         g_string_assign (str, "");
    00423                         lp = g_utf8_next_char(lp);
    00424                 }
    00425                 else {
    00426                         str = g_string_append_unichar (str, g_utf8_get_char (lp));
    00427                         lp = g_utf8_next_char(lp);
    00428                 }
    00429         }
    00430         if (str) {
    00431                 _read_attribute_value_add (attr, str, charset);
    00432                 g_string_free (str, TRUE);
    00433         }
    00434
    00435         if (*lp == '\r') {
    00436                 lp = g_utf8_next_char (lp); /* \n */
    00437                 lp = g_utf8_next_char (lp); /* start of the next line */
    00438         }
    00439
    00440         *p = lp;
    00441 }
    00442
    00443 static void _read_attribute_params(b_VFormatAttribute *attr, char **p, int *format_encoding, GString **charset)
    00444 {
    00445         char *lp = *p;
    00446         GString *str;
    00447         b_VFormatParam *param = NULL;
    00448         gboolean in_quote = FALSE;
    00449         str = g_string_new ("");
    00450
    00451         while (*lp != '\0') {
    00452                 if (*lp == '"') {
    00453                         in_quote = !in_quote;
    00454                         lp = g_utf8_next_char (lp);
    00455                 }
    00456                 else if (in_quote || g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/' || *lp == '.' || *lp == ' ') {
    00457                         str = g_string_append_unichar (str, g_utf8_get_char (lp));
    00458                         lp = g_utf8_next_char (lp);
    00459                 }
    00460                 /* accumulate until we hit the '=' or ';'.  If we hit
    00461                  * a '=' the string contains the parameter name.  if
    00462                  * we hit a ';' the string contains the parameter
    00463                  * value and the name is either ENCODING (if value ==
    00464                  * QUOTED-PRINTABLE) or TYPE (in any other case.)
    00465                  */
    00466                 else if (*lp == '=') {
    00467                         if (str->len > 0) {
    00468                                 param = b_vformat_attribute_param_new (str->str);
    00469                                 g_string_assign (str, "");
    00470                                 lp = g_utf8_next_char (lp);
    00471                         }
    00472                         else {
    00473                                 _skip_until (&lp, ":;");
    00474                                 if (*lp == '\r') {
    00475                                         lp = g_utf8_next_char (lp); /* \n */
    00476                                         lp = g_utf8_next_char (lp); /* start of the next line */
    00477                                         break;
    00478                                 }
    00479                                 else if (*lp == ';')
    00480                                         lp = g_utf8_next_char (lp);
    00481                         }
    00482                 }
    00483                 else if (*lp == ';' || *lp == ':' || *lp == ',') {
    00484                         gboolean colon = (*lp == ':');
    00485                         gboolean comma = (*lp == ',');
    00486
    00487                         if (param) {
    00488                                 if (str->len > 0) {
    00489                                         b_vformat_attribute_param_add_value (param, str->str);
    00490                                         g_string_assign (str, "");
    00491                                         if (!colon)
    00492                                                 lp = g_utf8_next_char (lp);
    00493                                 }
    00494                                 else {
    00495                                         /* we've got a parameter of the form:
    00496                                          * PARAM=(.*,)?[:;]
    00497                                          * so what we do depends on if there are already values
    00498                                          * for the parameter.  If there are, we just finish
    00499                                          * this parameter and skip past the offending character
    00500                                          * (unless it's the ':'). If there aren't values, we free
    00501                                          * the parameter then skip past the character.
    00502                                          */
    00503                                         if (!param->values) {
    00504                                                 b_vformat_attribute_param_free (param);
    00505                                                 param = NULL;
    00506                                                 if (!colon)
    00507                                                         lp = g_utf8_next_char (lp);
    00508                                         }
    00509                                 }
    00510
    00511                                 if (param
    00512                                     && !g_ascii_strcasecmp (param->name, "encoding")) {
    00513                                         if (!g_ascii_strcasecmp (param->values->data, "quoted-printable")) {
    00514                                                 *format_encoding = VF_ENCODING_QP;
    00515                                                 b_vformat_attribute_param_free (param);
    00516                                                 param = NULL;
    00517                                         } else if ( _helper_is_base64(param->values->data)) {
    00518                                                 *format_encoding = VF_ENCODING_BASE64;
    00519 //                                              b_vformat_attribute_param_free (param);
    00520 //                                              param = NULL;
    00521                                         }
    00522                                 } else if (param && !g_ascii_strcasecmp(param->name, "charset")) {
    00523                                         *charset = g_string_new(param->values->data);
    00524                                         b_vformat_attribute_param_free (param);
    00525                                         param = NULL;
    00526                                 }
    00527                         }
    00528                         else {
    00529                                 if (str->len > 0) {
    00530                                         char *param_name;
    00531                                         if (!g_ascii_strcasecmp (str->str,
    00532                                                                  "quoted-printable")) {
    00533                                                 param_name = "ENCODING";
    00534                                                 *format_encoding = VF_ENCODING_QP;
    00535                                         }
    00536                                         /* apple's broken addressbook app outputs naked BASE64
    00537                                            parameters, which aren't even vcard 3.0 compliant. */
    00538                                         else if (!g_ascii_strcasecmp (str->str,
    00539                                                                       "base64")) {
    00540                                                 param_name = "ENCODING";
    00541                                                 g_string_assign (str, "b");
    00542                                                 *format_encoding = VF_ENCODING_BASE64;
    00543                                         }
    00544                                         else {
    00545                                                 param_name = "TYPE";
    00546                                         }
    00547
    00548                                         if (param_name) {
    00549                                                 param = b_vformat_attribute_param_new (param_name);
    00550                                                 b_vformat_attribute_param_add_value (param, str->str);
    00551                                         }
    00552                                         g_string_assign (str, "");
    00553                                         if (!colon)
    00554                                                 lp = g_utf8_next_char (lp);
    00555                                 }
    00556                                 else {
    00557                                         /* we've got an attribute with a truly empty
    00558                                            attribute parameter.  So it's of the form:
    00559
    00560                                            ATTR;[PARAM=value;]*;[PARAM=value;]*:
    00561
    00562                                            (note the extra ';')
    00563
    00564                                            the only thing to do here is, well.. nothing.
    00565                                            we skip over the character if it's not a colon,
    00566                                            and the rest is handled for us: We'll either
    00567                                            continue through the loop again if we hit a ';',
    00568                                            or we'll break out correct below if it was a ':' */
    00569                                         if (!colon)
    00570                                                 lp = g_utf8_next_char (lp);
    00571                                 }
    00572                         }
    00573                         if (param && !comma) {
    00574                                 b_vformat_attribute_add_param (attr, param);
    00575                                 param = NULL;
    00576                         }
    00577                         if (colon)
    00578                                 break;
    00579                 }
    00580                 else {
    00581                         BarryLogf(TRACE_INTERNAL, "invalid character found in parameter spec: \"%i\" String so far: %s", lp[0], str->str);
    00582                         g_string_assign (str, "");
    00583                         _skip_until (&lp, ":;");
    00584                 }
    00585         }
    00586
    00587         if (str)
    00588                 g_string_free (str, TRUE);
    00589
    00590         *p = lp;
    00591 }
    00592
    00593 /* reads an entire attribute from the input buffer, leaving p pointing
    00594    at the start of the next line (past the \r\n) */
    00595 static b_VFormatAttribute *_read_attribute (char **p)
    00596 {
    00597         char *attr_group = NULL;
    00598         char *attr_name = NULL;
    00599         b_VFormatAttribute *attr = NULL;
    00600         GString *str, *charset = NULL;
    00601         char *lp = *p;
    00602
    00603         gboolean is_qp = FALSE;
    00604
    00605         /* first read in the group/name */
    00606         str = g_string_new ("");
    00607         while (*lp != '\r' && *lp != '\0') {
    00608                 if (*lp == ':' || *lp == ';') {
    00609                         if (str->len != 0) {
    00610                                 /* we've got a name, break out to the value/attribute parsing */
    00611                                 attr_name = g_string_free (str, FALSE);
    00612                                 break;
    00613                         }
    00614                         else {
    00615                                 /* a line of the form:
    00616                                  * (group.)?[:;]
    00617                                  *
    00618                                  * since we don't have an attribute
    00619                                  * name, skip to the end of the line
    00620                                  * and try again.
    00621                                  */
    00622                                 g_string_free (str, TRUE);
    00623                                 *p = lp;
    00624                                 _skip_to_next_line(p);
    00625                                 goto lose;
    00626                         }
    00627                 }
    00628                 else if (*lp == '.') {
    00629                         if (attr_group) {
    00630                                 BarryLogf(TRACE_INTERNAL, "extra `.' in attribute specification.  ignoring extra group `%s'", str->str);
    00631                                 g_string_free (str, TRUE);
    00632                                 str = g_string_new ("");
    00633                         }
    00634                         if (str->len != 0) {
    00635                                 attr_group = g_string_free (str, FALSE);
    00636                                 str = g_string_new ("");
    00637                         }
    00638                 }
    00639                 else if (g_unichar_isalnum (g_utf8_get_char (lp)) || *lp == '-' || *lp == '_' || *lp == '/') {
    00640                         str = g_string_append_unichar (str, g_utf8_get_char (lp));
    00641                 }
    00642                 else {
    00643                         BarryLogf(TRACE_INTERNAL, "invalid character found in attribute group/name: \"%i\" String so far: %s", lp[0], str->str);
    00644                         g_string_free (str, TRUE);
    00645                         *p = lp;
    00646                         _skip_to_next_line(p);
    00647                         goto lose;
    00648                 }
    00649
    00650                 lp = g_utf8_next_char(lp);
    00651         }
    00652
    00653         if (!attr_name) {
    00654                 _skip_to_next_line (p);
    00655                 goto lose;
    00656         }
    00657
    00658         attr = b_vformat_attribute_new (attr_group, attr_name);
    00659         g_free (attr_group);
    00660         g_free (attr_name);
    00661
    00662         if (*lp == ';') {
    00663                 /* skip past the ';' */
    00664                 lp = g_utf8_next_char(lp);
    00665                 _read_attribute_params (attr, &lp, &is_qp, &charset);
    00666         }
    00667         if (*lp == ':') {
    00668                 /* skip past the ':' */
    00669                 lp = g_utf8_next_char(lp);
    00670                 _read_attribute_value (attr, &lp, is_qp, charset);
    00671         }
    00672
    00673         if (charset) g_string_free(charset, TRUE);
    00674         *p = lp;
    00675
    00676         if (!attr->values)
    00677                 goto lose;
    00678
    00679         return attr;
    00680  lose:
    00681         if (attr)
    00682                 b_vformat_attribute_free (attr);
    00683         return NULL;
    00684 }
    00685
    00686 static void open_block(char **block, const char *block_name)
    00687 {
    00688         char *start = *block ? *block : "";
    00689         char *result = NULL;
    00690
    00691         result = g_strconcat(start, "/", block_name, NULL);
    00692         if( *block )
    00693                 g_free(*block);
    00694         *block = result;
    00695 }
    00696
    00697 static void close_block(char **block, const char *block_name)
    00698 {
    00699         int name_len = strlen(block_name);
    00700         int block_len = *block ? strlen(*block) : 0;
    00701         char *cmp_start = NULL;
    00702
    00703         if( block_len < name_len + 1 )
    00704                 return;
    00705
    00706         cmp_start = *block + (block_len - name_len - 1);
    00707         if( cmp_start[0] == '/' &&
    00708             g_ascii_strcasecmp(cmp_start+1, block_name) == 0 )
    00709         {
    00710                 // end of block hierarchy contains block name,
    00711                 // so safe to remove
    00712
    00713                 // cut off the end of the string... no need to free/realloc
    00714                 *cmp_start = '\0';
    00715         }
    00716 }
    00717
    00718 /* we try to be as forgiving as we possibly can here - this isn't a
    00719  * validator.  Almost nothing is considered a fatal error.  We always
    00720  * try to return *something*.
    00721  */
    00722 static void _parse(b_VFormat *evc, const char *str)
    00723 {
    00724         char *buf = g_strdup (str);
    00725         char *p, *end;
    00726         b_VFormatAttribute *attr;
    00727
    00728         /* first validate the string is valid utf8 */
    00729         if (!g_utf8_validate (buf, -1, (const char **)&end)) {
    00730                 /* if the string isn't valid, we parse as much as we can from it */
    00731                 BarryLogf(TRACE_INTERNAL, "invalid utf8 passed to b_VFormat.  Limping along.");
    00732                 *end = '\0';
    00733         }
    00734
    00735         buf = _fold_lines (buf);
    00736
    00737         p = buf;
    00738
    00739         attr = _read_attribute (&p);
    00740         if (!attr)
    00741                 attr = _read_attribute (&p);
    00742
    00743         if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "begin")) {
    00744                 BarryLogf(TRACE_INTERNAL, "vformat began without a BEGIN\n");
    00745         }
    00746         if (attr && !g_ascii_strcasecmp (attr->name, "begin"))
    00747                 b_vformat_attribute_free (attr);
    00748         else if (attr)
    00749                 b_vformat_add_attribute (evc, attr);
    00750
    00751         char *block = NULL;
    00752         while (*p) {
    00753                 b_VFormatAttribute *next_attr = _read_attribute (&p);
    00754
    00755                 if (next_attr) {
    00756                         if( g_ascii_strcasecmp(next_attr->name, "begin") == 0 ) {
    00757                                 // add to block hierarchy string
    00758                                 char *value = b_vformat_attribute_get_value(next_attr);
    00759                                 open_block(&block, value);
    00760                                 //BarryLogf(TRACE_INTERNAL, "open block: %s", block);
    00761                                 g_free(value);
    00762                         }
    00763                         else if( g_ascii_strcasecmp(next_attr->name, "end") == 0 ) {
    00764                                 // close off the block
    00765                                 char *value = b_vformat_attribute_get_value(next_attr);
    00766                                 close_block(&block, value);
    00767                                 //BarryLogf(TRACE_INTERNAL, "close block: %s", block);
    00768                                 g_free(value);
    00769                         }
    00770
    00771                         // apply the block to the attr
    00772                         next_attr->block = g_strdup(block);
    00773
    00774                         // add!
    00775                         b_vformat_add_attribute (evc, next_attr);
    00776                         attr = next_attr;
    00777                 }
    00778         }
    00779
    00780         if (!attr || attr->group || g_ascii_strcasecmp (attr->name, "end")) {
    00781                 BarryLogf(TRACE_INTERNAL, "vformat ended without END");
    00782         }
    00783
    00784         g_free (buf);
    00785         g_free (block);
    00786 }
    00787
    00788 char *b_vformat_escape_string (const char *s, b_VFormatType type)
    00789 {
    00790         GString *str;
    00791         const char *p;
    00792
    00793         str = g_string_new ("");
    00794
    00795         /* Escape a string as described in RFC2426, section 5 */
    00796         for (p = s; p && *p; p++) {
    00797                 switch (*p) {
    00798                 case '\n':
    00799                         str = g_string_append (str, "\\n");
    00800                         break;
    00801                 case '\r':
    00802                         if (*(p+1) == '\n')
    00803                                 p++;
    00804                         str = g_string_append (str, "\\n");
    00805                         break;
    00806                 case ';':
    00807                         str = g_string_append (str, "\\;");
    00808                         break;
    00809                 case ',':
    00810                         if (type == VFORMAT_CARD_30 || type == VFORMAT_EVENT_20 || type == VFORMAT_TODO_20)
    00811                                 str = g_string_append (str, "\\,");
    00812                         else
    00813                                 str = g_string_append_c (str, *p);
    00814                         break;
    00815                 case '\\':
    00816                         /**
    00817                          * We won't escape backslashes
    00818                          * on vcard 2.1, unless it is in the end of a value.
    00819                          * See comments above for a better explanation
    00820                         **/
    00821                         if (*p != '\0' && type == VFORMAT_CARD_21) {
    00822                                 BarryLogf(TRACE_INTERNAL, "[%s]We won't escape backslashes", __func__);
    00823                                 str = g_string_append_c(str, *p);
    00824                         }
    00825                         else {
    00826                                 BarryLogf(TRACE_INTERNAL, "[%s] escape backslashes!!", __func__);
    00827                                 str = g_string_append (str, "\\\\");
    00828                         }
    00829                         break;
    00830                 default:
    00831                         str = g_string_append_c (str, *p);
    00832                         break;
    00833                 }
    00834         }
    00835
    00836         return g_string_free (str, FALSE);
    00837 }
    00838
    00839 char*
    00840 b_vformat_unescape_string (const char *s)
    00841 {
    00842         GString *str;
    00843         const char *p;
    00844
    00845         g_return_val_if_fail (s != NULL, NULL);
    00846
    00847         str = g_string_new ("");
    00848
    00849         /* Unescape a string as described in RFC2426, section 4 (Formal Grammar) */
    00850         for (p = s; *p; p++) {
    00851                 if (*p == '\\') {
    00852                         p++;
    00853                         if (*p == '\0') {
    00854                                 str = g_string_append_c (str, '\\');
    00855                                 break;
    00856                         }
    00857                         switch (*p) {
    00858                         case 'n':  str = g_string_append_c (str, '\n'); break;
    00859                         case 'r':  str = g_string_append_c (str, '\r'); break;
    00860                         case ';':  str = g_string_append_c (str, ';'); break;
    00861                         case ',':  str = g_string_append_c (str, ','); break;
    00862                         case '\\': str = g_string_append_c (str, '\\'); break;
    00863                         case '"': str = g_string_append_c (str, '"'); break;
    00864                           /* \t is (incorrectly) used by kOrganizer, so handle it here */
    00865                         case 't': str = g_string_append_c (str, '\t'); break;
    00866                         default:
    00867                                 BarryLogf(TRACE_INTERNAL, "invalid escape, passing it through. escaped char was %u", (unsigned int)*p);
    00868                                 str = g_string_append_c (str, '\\');
    00869                                 str = g_string_append_unichar (str, g_utf8_get_char(p));
    00870                                 break;
    00871                         }
    00872                 }
    00873         }
    00874
    00875         return g_string_free (str, FALSE);
    00876 }
    00877
    00878 void
    00879 b_vformat_construct (b_VFormat *evc, const char *str)
    00880 {
    00881         g_return_if_fail (str != NULL);
    00882
    00883         if (*str)
    00884                 _parse (evc, str);
    00885 }
    00886
    00887 void b_vformat_free(b_VFormat *format)
    00888 {
    00889         g_list_foreach (format->attributes, (GFunc)b_vformat_attribute_free, NULL);
    00890         g_list_free (format->attributes);
    00891         g_free(format);
    00892 }
    00893
    00894 b_VFormat *b_vformat_new_from_string (const char *str)
    00895 {
    00896         g_return_val_if_fail (str != NULL, NULL);
    00897         b_VFormat *evc = g_malloc0(sizeof(b_VFormat));
    00898
    00899         b_vformat_construct (evc, str);
    00900
    00901         return evc;
    00902 }
    00903
    00904 b_VFormat *b_vformat_new(void)
    00905 {
    00906         return b_vformat_new_from_string ("");
    00907 }
    00908
    00909 static int _block_match(b_VFormatAttribute *attr, const char *block)
    00910 {
    00911         // a block matches if the end of the attribute's block
    00912         // string matches a case insensitive compare with block
    00913         //
    00914         // for example, a calendar may or may not start with a
    00915         // BEGIN: VCALENDAR, so DTSTART's block string could be
    00916         // "/vcalendar/vevent" or just "/vevent".  By passing
    00917         // "/vevent" or even "vevent" as the block argument above,
    00918         // we should get a match for any of the above.
    00919
    00920         int attr_len = attr->block ? strlen(attr->block) : 0;
    00921         int block_len = block ? strlen(block) : 0;
    00922
    00923         if( block == NULL )
    00924                 return 1;       // if block is null, match everything
    00925
    00926         if( attr_len < block_len )
    00927                 return 0;       // not enough string to compare
    00928
    00929         if( attr_len == 0 && block_len == 0 )
    00930                 return 1;       // empty and null strings match
    00931
    00932         if( attr->block == NULL )
    00933                 return 0;       // don't compare if one side is null
    00934
    00935         return g_ascii_strcasecmp(&attr->block[attr_len - block_len], block) == 0;
    00936 }
    00937
    00938 b_VFormatAttribute *b_vformat_find_attribute(b_VFormat *vcard, const char *name, int nth, const char *block)
    00939 {
    00940         GList *attributes = b_vformat_get_attributes(vcard);
    00941         GList *a = NULL;
    00942         int i = 0;
    00943         for (a = attributes; a; a = a->next) {
    00944                 b_VFormatAttribute *attr = a->data;
    00945                 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
    00946                         if( block == NULL || _block_match(attr, block) ) {
    00947                                 if( i == nth )
    00948                                         return attr;
    00949                                 i++;
    00950                         }
    00951                 }
    00952         }
    00953         return NULL;
    00954 }
    00955
    00956 /*
    00957 b_VFormatAttribute *b_vformat_find_attribute_next(b_VFormatAttribute *last,
    00958                                                 const char *name,
    00959                                                 int nth)
    00960 {
    00961         GList *attributes = last ? last->next : 0;
    00962         GList *a = NULL;
    00963         int i = 0;
    00964         for (a = attributes; a; a = a->next) {
    00965                 b_VFormatAttribute *attr = a->data;
    00966                 if (!g_ascii_strcasecmp(b_vformat_attribute_get_name(attr), name)) {
    00967                         if( i == nth )
    00968                                 return attr;
    00969                         i++;
    00970                 }
    00971         }
    00972         return NULL;
    00973 }
    00974 */
    00975
    00976 char *b_vformat_to_string (b_VFormat *evc, b_VFormatType type)
    00977 {
    00978         BarryLogf(TRACE_ENTRY, "%s(%p, %i)", __func__, evc, type);
    00979         GList *l;
    00980         GList *v;
    00981
    00982         GString *str = g_string_new ("");
    00983
    00984         switch (type) {
    00985                 case VFORMAT_CARD_21:
    00986                         str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:2.1\r\n");
    00987                         break;
    00988                 case VFORMAT_CARD_30:
    00989                         str = g_string_append (str, "BEGIN:VCARD\r\nVERSION:3.0\r\n");
    00990                         break;
    00991                 case VFORMAT_TODO_10:
    00992                 case VFORMAT_EVENT_10:
    00993                         str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:1.0\r\n");
    00994                         break;
    00995                 case VFORMAT_TODO_20:
    00996                 case VFORMAT_EVENT_20:
    00997                 case VFORMAT_JOURNAL:
    00998                         str = g_string_append (str, "BEGIN:VCALENDAR\r\nVERSION:2.0\r\n");
    00999                         break;
    01000                 case VFORMAT_NOTE:
    01001                         str = g_string_append (str, "BEGIN:VNOTE\r\nVERSION:1.1\r\n");
    01002                         break;
    01003         }
    01004
    01005         for (l = evc->attributes; l; l = l->next) {
    01006                 GList *p;
    01007                 b_VFormatAttribute *attr = l->data;
    01008                 GString *attr_str;
    01009                 int l;
    01010                 int format_encoding = VF_ENCODING_RAW;
    01011
    01012                 attr_str = g_string_new ("");
    01013
    01014                 /* From rfc2425, 5.8.2
    01015                  *
    01016                  * contentline  = [group "."] name *(";" param) ":" value CRLF
    01017                  */
    01018
    01019                 if (attr->group) {
    01020                         attr_str = g_string_append (attr_str, attr->group);
    01021                         attr_str = g_string_append_c (attr_str, '.');
    01022                 }
    01023                 attr_str = g_string_append (attr_str, attr->name);
    01024                 /* handle the parameters */
    01025                 for (p = attr->params; p; p = p->next) {
    01026                         b_VFormatParam *param = p->data;
    01027                         /* 5.8.2:
    01028                          * param        = param-name "=" param-value *("," param-value)
    01029                          */
    01030                         if( type == VFORMAT_CARD_30 || type == VFORMAT_TODO_20
    01031                             || type == VFORMAT_EVENT_20 || type == VFORMAT_JOURNAL) {
    01032
    01033                                 /**
    01034                                  * Character set can only be specified on the CHARSET
    01035                                  * parameter on the Content-Type MIME header field.
    01036                                 **/
    01037                                 if (!g_ascii_strcasecmp (param->name, "CHARSET"))
    01038                                         continue;
    01039                                 attr_str = g_string_append_c (attr_str, ';');
    01040                                 attr_str = g_string_append (attr_str, param->name);
    01041                                 if (param->values) {
    01042                                         attr_str = g_string_append_c (attr_str, '=');
    01043                                 }
    01044                                 for (v = param->values; v; v = v->next) {
    01045                                         if (_helper_is_base64((const char *) v->data)) {
    01046                                                 format_encoding = VF_ENCODING_BASE64;
    01047                                                 /*Only the "B" encoding of [RFC 2047] is an allowed*/
    01048                                                 v->data=g_strdup("B");
    01049                                         }
    01050                                         /**
    01051                                          * QUOTED-PRINTABLE inline encoding has been
    01052                                          * eliminated.
    01053                                         **/
    01054                                         if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE")) {
    01055                                                 BarryLogf(TRACE_ERROR, "%s false encoding QUOTED-PRINTABLE is not allowed", __func__);
    01056                                                 format_encoding = VF_ENCODING_QP;
    01057                                         }
    01058                                         attr_str = g_string_append (attr_str, v->data);
    01059
    01060                                         if (v->next)
    01061                                                 attr_str = g_string_append_c (attr_str, ',');
    01062                                 }
    01063                         }
    01064                         else {
    01065                                 attr_str = g_string_append_c (attr_str, ';');
    01066                                 /**
    01067                                  * The "TYPE=" is optional skip it.
    01068                                  * LOGO, PHOTO and SOUND multimedia formats MUST
    01069                                  * have a "TYPE=" parameter
    01070                                 **/
    01071                                 gboolean must_have_type = FALSE;
    01072                                 if (!g_ascii_strcasecmp (attr->name, "PHOTO") || !g_ascii_strcasecmp (attr->name, "LOGO") || !g_ascii_strcasecmp (attr->name, "SOUND") )
    01073                                         must_have_type = TRUE;
    01074                                 if ( must_have_type || g_ascii_strcasecmp (param->name, "TYPE") )
    01075                                         attr_str = g_string_append (attr_str, param->name);
    01076                                 if ( param->values && (must_have_type || g_ascii_strcasecmp (param->name, "TYPE")) )
    01077                                         attr_str = g_string_append_c (attr_str, '=');
    01078                                 for (v = param->values; v; v = v->next) {
    01079                                         // check for quoted-printable encoding
    01080                                         if (!g_ascii_strcasecmp (param->name, "ENCODING") && !g_ascii_strcasecmp ((char *) v->data, "QUOTED-PRINTABLE"))
    01081                                                 format_encoding = VF_ENCODING_QP;
    01082                                         // check for base64 encoding
    01083                                         if (_helper_is_base64((const char *) v->data)) {
    01084                                                 format_encoding = VF_ENCODING_BASE64;
    01085                                                 v->data=g_strdup("BASE64");
    01086                                         }
    01087                                         attr_str = g_string_append (attr_str, v->data);
    01088                                         if (v->next)
    01089                                                 attr_str = g_string_append_c (attr_str, ',');
    01090                                 }
    01091                         }
    01092                 }
    01093
    01094                 attr_str = g_string_append_c (attr_str, ':');
    01095
    01096                 for (v = attr->values; v; v = v->next) {
    01097                         char *value = v->data;
    01098                         char *escaped_value = NULL;
    01099
    01100                         if (!g_ascii_strcasecmp (attr->name, "RRULE") &&
    01101                                   strstr (value, "BYDAY") == v->data) {
    01102                                 attr_str = g_string_append (attr_str, value);
    01103                         } else {
    01104                                 escaped_value = b_vformat_escape_string (value, type);
    01105                                 attr_str = g_string_append (attr_str, escaped_value);
    01106                         }
    01107
    01108                         if (v->next) {
    01109
    01110                                 /* XXX toshok - i hate you, rfc 2426.
    01111                                    why doesn't CATEGORIES use a ; like
    01112                                    a normal list attribute? */
    01113                                 if (!g_ascii_strcasecmp (attr->name, "CATEGORIES"))
    01114                                         attr_str = g_string_append_c (attr_str, ',');
    01115                                 else
    01116                                         attr_str = g_string_append_c (attr_str, ';');
    01117                         }
    01118
    01119                         g_free (escaped_value);
    01120                 }
    01121
    01122                 /* Folding lines:
    01123                  * ^^^^^^^^^^^^^^
    01124                  *
    01125                  * rfc 2426 (vCard), 2.6 Line Delimiting and Folding:
    01126                  * After generating a content line,
    01127                  * lines longer than 75 characters SHOULD be folded according to the
    01128                  * folding procedure described in [MIME-DIR].
    01129                  *
    01130                  * rfc 2445 (iCalendar), 4.1 Content Lines:
    01131                  * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
    01132                  * break. Long content lines SHOULD be split into a multiple line
    01133                  * representations using a line "folding" technique. That is, a long
    01134                  * line can be split between any two characters by inserting a CRLF
    01135                  * immediately followed by a single linear white space character (i.e.,
    01136                  * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
    01137                  * of CRLF followed immediately by a single linear white space character
    01138                  *  is ignored (i.e., removed) when processing the content type.
    01139                  *
    01140                  * SUMMARY: When generating a content line, lines longer then 75 characters SHOULD be folded!
    01141                  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    01142                  *
    01143                  * Differences between encodings:
    01144                  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    01145                  *
    01146                  * rfc 2425 [MIME-DIR], 5.8.1:
    01147                  * A logical line MAY be continued on the next physical line anywhere
    01148                  * between two characters by inserting a CRLF immediately followed by a
    01149                  * single <WS> (white space) character.
    01150                  *
    01151                  * rfc 2045, 6.7, chapter 5:
    01152                  * The quoted-printable specs says that softbreaks should be generated by inserting a =\r\n
    01153                  * without follwing <WS>
    01154                  *
    01155                  * UTF-8
    01156                  * ^^^^^
    01157                  *
    01158                  * Note that all the line folding above is described in terms of characters
    01159                  * not bytes.  In particular, it would be an error to put a line break
    01160                  * within a UTF-8 character.
    01161                 */
    01162
    01163                 l = 0;
    01164                 do {
    01165                         if (g_utf8_strlen(attr_str->str, attr_str->len) - l > 75) {
    01166                                 l += 75;
    01167
    01168                                 /* If using QP, must be sure that we do not fold within a quote sequence */
    01169                                 if (format_encoding == VF_ENCODING_QP) {
    01170                                   if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-1)) == '=') l--;
    01171                                   else if (g_utf8_get_char(g_utf8_offset_to_pointer(attr_str->str, l-2)) == '=') l -= 2;
    01172                                 }
    01173
    01174                                 char *p = g_utf8_offset_to_pointer(attr_str->str, l);
    01175
    01176                                 if (format_encoding == VF_ENCODING_QP)
    01177                                         attr_str = g_string_insert_len (attr_str, p - attr_str->str, "=" CRLF "", sizeof ("=" CRLF "") - 1);
    01178                                 else
    01179                                         attr_str = g_string_insert_len (attr_str, p - attr_str->str, CRLF " ", sizeof (CRLF " ") - 1);
    01180                         }
    01181                         else
    01182                                 break;
    01183                 } while (l < g_utf8_strlen(attr_str->str, attr_str->len));
    01184
    01185                 attr_str = g_string_append (attr_str, CRLF);
    01186                 /**
    01187                  * base64= <MIME RFC 1521 base64 text>
    01188                  * the end of the text is marked with two CRLF sequences
    01189                  * this results in one blank line before the start of the
    01190                  * next property
    01191                 **/
    01192                 if( format_encoding == VF_ENCODING_BASE64
    01193                    && (type == VFORMAT_CARD_21))
    01194                         attr_str = g_string_append (attr_str, CRLF);
    01195
    01196                 str = g_string_append (str, attr_str->str);
    01197                 g_string_free (attr_str, TRUE);
    01198         }
    01199
    01200         switch (type) {
    01201                 case VFORMAT_CARD_21:
    01202                         str = g_string_append (str, "END:VCARD\r\n");
    01203                         break;
    01204                 case VFORMAT_CARD_30:
    01205                         str = g_string_append (str, "END:VCARD\r\n");
    01206                         break;
    01207                 case VFORMAT_TODO_10:
    01208                 case VFORMAT_EVENT_10:
    01209                         str = g_string_append (str, "END:VCALENDAR\r\n");
    01210                         break;
    01211                 case VFORMAT_TODO_20:
    01212                 case VFORMAT_EVENT_20:
    01213                 case VFORMAT_JOURNAL:
    01214                         str = g_string_append (str, "END:VCALENDAR\r\n");
    01215                         break;
    01216                 case VFORMAT_NOTE:
    01217                         str = g_string_append (str, "END:VNOTE\r\n");
    01218                         break;
    01219         }
    01220
    01221         BarryLogf(TRACE_EXIT, "%s", __func__);
    01222         return g_string_free (str, FALSE);
    01223 }
    01224
    01225 void b_vformat_dump_structure (b_VFormat *evc)
    01226 {
    01227         GList *a;
    01228         GList *v;
    01229         int i;
    01230
    01231         printf ("b_VFormat\n");
    01232         for (a = evc->attributes; a; a = a->next) {
    01233                 GList *p;
    01234                 b_VFormatAttribute *attr = a->data;
    01235                 printf ("+-- %s\n", attr->name);
    01236                 if (attr->params) {
    01237                         printf ("    +- params=\n");
    01238
    01239                         for (p = attr->params, i = 0; p; p = p->next, i++) {
    01240                                 b_VFormatParam *param = p->data;
    01241                                 printf ("    |   [%d] = %s", i,param->name);
    01242                                 printf ("(");
    01243                                 for (v = param->values; v; v = v->next) {
    01244                                         char *value = b_vformat_escape_string ((char*)v->data, VFORMAT_CARD_21);
    01245                                         printf ("%s", value);
    01246                                         if (v->next)
    01247                                                 printf (",");
    01248                                         g_free (value);
    01249                                 }
    01250
    01251                                 printf (")\n");
    01252                         }
    01253                 }
    01254                 printf ("    +- values=\n");
    01255                 for (v = attr->values, i = 0; v; v = v->next, i++) {
    01256                         printf ("        [%d] = `%s'\n", i, (char*)v->data);
    01257                 }
    01258         }
    01259 }
    01260
    01261 b_VFormatAttribute *b_vformat_attribute_new (const char *attr_group, const char *attr_name)
    01262 {
    01263         b_VFormatAttribute *attr;
    01264
    01265         attr = g_new0 (b_VFormatAttribute, 1);
    01266
    01267         attr->group = g_strdup (attr_group);
    01268         attr->name = g_strdup (attr_name);
    01269
    01270         return attr;
    01271 }
    01272
    01273 void
    01274 b_vformat_attribute_free (b_VFormatAttribute *attr)
    01275 {
    01276         g_return_if_fail (attr != NULL);
    01277
    01278         g_free (attr->block);
    01279         g_free (attr->group);
    01280         g_free (attr->name);
    01281
    01282         b_vformat_attribute_remove_values (attr);
    01283
    01284         b_vformat_attribute_remove_params (attr);
    01285
    01286         g_free (attr);
    01287 }
    01288
    01289 b_VFormatAttribute*
    01290 b_vformat_attribute_copy (b_VFormatAttribute *attr)
    01291 {
    01292         b_VFormatAttribute *a;
    01293         GList *p;
    01294
    01295         g_return_val_if_fail (attr != NULL, NULL);
    01296
    01297         a = b_vformat_attribute_new (b_vformat_attribute_get_group (attr),
    01298                                    b_vformat_attribute_get_name (attr));
    01299
    01300         for (p = attr->values; p; p = p->next)
    01301                 b_vformat_attribute_add_value (a, p->data);
    01302
    01303         for (p = attr->params; p; p = p->next)
    01304                 b_vformat_attribute_add_param (a, b_vformat_attribute_param_copy (p->data));
    01305
    01306         return a;
    01307 }
    01308
    01309 void
    01310 b_vformat_remove_attributes (b_VFormat *evc, const char *attr_group, const char *attr_name)
    01311 {
    01312         GList *attr;
    01313
    01314         g_return_if_fail (attr_name != NULL);
    01315
    01316         attr = evc->attributes;
    01317         while (attr) {
    01318                 GList *next_attr;
    01319                 b_VFormatAttribute *a = attr->data;
    01320
    01321                 next_attr = attr->next;
    01322
    01323                 if (((!attr_group && !a->group) ||
    01324                      (attr_group && !g_ascii_strcasecmp (attr_group, a->group))) &&
    01325                     ((!attr_name && !a->name) || !g_ascii_strcasecmp (attr_name, a->name))) {
    01326
    01327                         /* matches, remove/delete the attribute */
    01328                         evc->attributes = g_list_remove_link (evc->attributes, attr);
    01329
    01330                         b_vformat_attribute_free (a);
    01331                 }
    01332
    01333                 attr = next_attr;
    01334         }
    01335 }
    01336
    01337 void
    01338 b_vformat_remove_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
    01339 {
    01340         g_return_if_fail (attr != NULL);
    01341
    01342         evc->attributes = g_list_remove (evc->attributes, attr);
    01343         b_vformat_attribute_free (attr);
    01344 }
    01345
    01346 void
    01347 b_vformat_add_attribute (b_VFormat *evc, b_VFormatAttribute *attr)
    01348 {
    01349         g_return_if_fail (attr != NULL);
    01350
    01351         evc->attributes = g_list_append (evc->attributes, attr);
    01352 }
    01353
    01354 void
    01355 b_vformat_add_attribute_with_value (b_VFormat *b_VFormat,
    01356                                   b_VFormatAttribute *attr, const char *value)
    01357 {
    01358         g_return_if_fail (attr != NULL);
    01359
    01360         b_vformat_attribute_add_value (attr, value);
    01361
    01362         b_vformat_add_attribute (b_VFormat, attr);
    01363 }
    01364
    01365 void
    01366 b_vformat_add_attribute_with_values (b_VFormat *b_VFormat, b_VFormatAttribute *attr, ...)
    01367 {
    01368         va_list ap;
    01369         char *v;
    01370
    01371         g_return_if_fail (attr != NULL);
    01372
    01373         va_start (ap, attr);
    01374
    01375         while ((v = va_arg (ap, char*))) {
    01376                 b_vformat_attribute_add_value (attr, v);
    01377         }
    01378
    01379         va_end (ap);
    01380
    01381         b_vformat_add_attribute (b_VFormat, attr);
    01382 }
    01383
    01384 void
    01385 b_vformat_attribute_add_value (b_VFormatAttribute *attr, const char *value)
    01386 {
    01387         g_return_if_fail (attr != NULL);
    01388
    01389         attr->values = g_list_append (attr->values, g_strdup (value));
    01390 }
    01391
    01392 void
    01393 b_vformat_attribute_add_value_decoded (b_VFormatAttribute *attr, const char *value, int len)
    01394 {
    01395         g_return_if_fail (attr != NULL);
    01396
    01397         switch (attr->encoding) {
    01398                 case VF_ENCODING_RAW:
    01399                         BarryLogf(TRACE_INTERNAL, "can't add_value_decoded with an attribute using RAW encoding.  you must set the ENCODING parameter first");
    01400                         break;
    01401                 case VF_ENCODING_BASE64: {
    01402                         char *b64_data = base64_encode_simple (value, len);
    01403                         GString *decoded = g_string_new_len (value, len);
    01404
    01405                         /* make sure the decoded list is up to date */
    01406                         b_vformat_attribute_get_values_decoded (attr);
    01407
    01408                         attr->values = g_list_append (attr->values, b64_data);
    01409                         attr->decoded_values = g_list_append (attr->decoded_values, decoded);
    01410                         break;
    01411                 }
    01412                 case VF_ENCODING_QP: {
    01413                         char *qp_data = quoted_encode_simple ((unsigned char*)value, len);
    01414                         GString *decoded = g_string_new (value);
    01415
    01416                         /* make sure the decoded list is up to date */
    01417                         b_vformat_attribute_get_values_decoded (attr);
    01418
    01419                         attr->values = g_list_append (attr->values, qp_data);
    01420                         attr->decoded_values = g_list_append (attr->decoded_values, decoded);
    01421                         break;
    01422                 }
    01423                 case VF_ENCODING_8BIT: {
    01424                         char *data = g_strdup(value);
    01425                         GString *decoded = g_string_new (value);
    01426
    01427                         /* make sure the decoded list is up to date */
    01428                         b_vformat_attribute_get_values_decoded (attr);
    01429
    01430                         attr->values = g_list_append (attr->values, data);
    01431                         attr->decoded_values = g_list_append (attr->decoded_values, decoded);
    01432                         break;
    01433                 }
    01434         }
    01435 }
    01436
    01437 void
    01438 b_vformat_attribute_add_values (b_VFormatAttribute *attr, ...)
    01439 {
    01440         va_list ap;
    01441         char *v;
    01442
    01443         g_return_if_fail (attr != NULL);
    01444
    01445         va_start (ap, attr);
    01446
    01447         while ((v = va_arg (ap, char*))) {
    01448                 b_vformat_attribute_add_value (attr, v);
    01449         }
    01450
    01451         va_end (ap);
    01452 }
    01453
    01454 static void
    01455 free_gstring (GString *str)
    01456 {
    01457         g_string_free (str, TRUE);
    01458 }
    01459
    01460 void
    01461 b_vformat_attribute_remove_values (b_VFormatAttribute *attr)
    01462 {
    01463         g_return_if_fail (attr != NULL);
    01464
    01465         g_list_foreach (attr->values, (GFunc)g_free, NULL);
    01466         g_list_free (attr->values);
    01467         attr->values = NULL;
    01468
    01469         g_list_foreach (attr->decoded_values, (GFunc)free_gstring, NULL);
    01470         g_list_free (attr->decoded_values);
    01471         attr->decoded_values = NULL;
    01472 }
    01473
    01474 void
    01475 b_vformat_attribute_remove_params (b_VFormatAttribute *attr)
    01476 {
    01477         g_return_if_fail (attr != NULL);
    01478
    01479         g_list_foreach (attr->params, (GFunc)b_vformat_attribute_param_free, NULL);
    01480         g_list_free (attr->params);
    01481         attr->params = NULL;
    01482
    01483         /* also remove the cached encoding on this attribute */
    01484         attr->encoding_set = FALSE;
    01485         attr->encoding = VF_ENCODING_RAW;
    01486 }
    01487
    01488 b_VFormatParam*
    01489 b_vformat_attribute_param_new (const char *name)
    01490 {
    01491         b_VFormatParam *param = g_new0 (b_VFormatParam, 1);
    01492         param->name = g_strdup (name);
    01493
    01494         return param;
    01495 }
    01496
    01497 void
    01498 b_vformat_attribute_param_free (b_VFormatParam *param)
    01499 {
    01500         g_return_if_fail (param != NULL);
    01501
    01502         g_free (param->name);
    01503
    01504         b_vformat_attribute_param_remove_values (param);
    01505
    01506         g_free (param);
    01507 }
    01508
    01509 b_VFormatParam*
    01510 b_vformat_attribute_param_copy (b_VFormatParam *param)
    01511 {
    01512         b_VFormatParam *p;
    01513         GList *l;
    01514
    01515         g_return_val_if_fail (param != NULL, NULL);
    01516
    01517         p = b_vformat_attribute_param_new (b_vformat_attribute_param_get_name (param));
    01518
    01519         for (l = param->values; l; l = l->next) {
    01520                 b_vformat_attribute_param_add_value (p, l->data);
    01521         }
    01522
    01523         return p;
    01524 }
    01525
    01526 void
    01527 b_vformat_attribute_add_param (b_VFormatAttribute *attr,
    01528                              b_VFormatParam *param)
    01529 {
    01530         g_return_if_fail (attr != NULL);
    01531         g_return_if_fail (param != NULL);
    01532
    01533         attr->params = g_list_append (attr->params, param);
    01534
    01535         /* we handle our special encoding stuff here */
    01536
    01537         if (!g_ascii_strcasecmp (param->name, "ENCODING")) {
    01538                 if (attr->encoding_set) {
    01539                         BarryLogf(TRACE_INTERNAL, "ENCODING specified twice");
    01540                         return;
    01541                 }
    01542
    01543                 if (param->values && param->values->data) {
    01544                         if (_helper_is_base64((const char*)param->values->data))
    01545                                 attr->encoding = VF_ENCODING_BASE64;
    01546                         else if (!g_ascii_strcasecmp ((char*)param->values->data, "QUOTED-PRINTABLE"))
    01547                                 attr->encoding = VF_ENCODING_QP;
    01548                         else if (!g_ascii_strcasecmp ((char *)param->values->data, "8BIT"))
    01549                                 attr->encoding = VF_ENCODING_8BIT;
    01550                         else {
    01551                                 BarryLogf(TRACE_INTERNAL, "Unknown value `%s' for ENCODING parameter.  values will be treated as raw", (char*)param->values->data);
    01552                         }
    01553
    01554                         attr->encoding_set = TRUE;
    01555                 }
    01556                 else {
    01557                         BarryLogf(TRACE_INTERNAL, "ENCODING parameter added with no value");
    01558                 }
    01559         }
    01560 }
    01561
    01562 b_VFormatParam *b_vformat_attribute_find_param(b_VFormatAttribute *attr, const char *name, int level)
    01563 {
    01564         g_return_val_if_fail (attr != NULL, NULL);
    01565         GList *p = NULL;
    01566         for (p = attr->params; p; p = p->next) {
    01567                 b_VFormatParam *param = p->data;
    01568                 if (!g_ascii_strcasecmp (param->name, name)) {
    01569                         if( level == 0 )
    01570                                 return param;
    01571                         else
    01572                                 level--;
    01573                 }
    01574         }
    01575         return NULL;
    01576 }
    01577
    01578 void
    01579 b_vformat_attribute_set_value (b_VFormatAttribute *attr,
    01580                                 int nth, const char *value)
    01581 {
    01582         GList *param = g_list_nth(attr->values, nth);
    01583         g_free(param->data);
    01584         param->data = g_strdup(value);
    01585 }
    01586
    01587 void
    01588 b_vformat_attribute_param_add_value (b_VFormatParam *param,
    01589                                    const char *value)
    01590 {
    01591         g_return_if_fail (param != NULL);
    01592
    01593         param->values = g_list_append (param->values, g_strdup (value));
    01594 }
    01595
    01596 void
    01597 b_vformat_attribute_param_add_values (b_VFormatParam *param,
    01598                                     ...)
    01599 {
    01600         va_list ap;
    01601         char *v;
    01602
    01603         g_return_if_fail (param != NULL);
    01604
    01605         va_start (ap, param);
    01606
    01607         while ((v = va_arg (ap, char*))) {
    01608                 b_vformat_attribute_param_add_value (param, v);
    01609         }
    01610
    01611         va_end (ap);
    01612 }
    01613
    01614 void
    01615 b_vformat_attribute_add_param_with_value (b_VFormatAttribute *attr, const char *name, const char *value)
    01616 {
    01617         g_return_if_fail (attr != NULL);
    01618         g_return_if_fail (name != NULL);
    01619
    01620         if (!value)
    01621                 return;
    01622
    01623         b_VFormatParam *param = b_vformat_attribute_param_new(name);
    01624
    01625         b_vformat_attribute_param_add_value (param, value);
    01626
    01627         b_vformat_attribute_add_param (attr, param);
    01628 }
    01629
    01630 void
    01631 b_vformat_attribute_add_param_with_values (b_VFormatAttribute *attr,
    01632                                          b_VFormatParam *param, ...)
    01633 {
    01634         va_list ap;
    01635         char *v;
    01636
    01637         g_return_if_fail (attr != NULL);
    01638         g_return_if_fail (param != NULL);
    01639
    01640         va_start (ap, param);
    01641
    01642         while ((v = va_arg (ap, char*))) {
    01643                 b_vformat_attribute_param_add_value (param, v);
    01644         }
    01645
    01646         va_end (ap);
    01647
    01648         b_vformat_attribute_add_param (attr, param);
    01649 }
    01650
    01651 void
    01652 b_vformat_attribute_param_remove_values (b_VFormatParam *param)
    01653 {
    01654         g_return_if_fail (param != NULL);
    01655
    01656         g_list_foreach (param->values, (GFunc)g_free, NULL);
    01657         g_list_free (param->values);
    01658         param->values = NULL;
    01659 }
    01660
    01661 GList*
    01662 b_vformat_get_attributes (b_VFormat *format)
    01663 {
    01664         return format->attributes;
    01665 }
    01666
    01667 const char*
    01668 b_vformat_attribute_get_group (b_VFormatAttribute *attr)
    01669 {
    01670         g_return_val_if_fail (attr != NULL, NULL);
    01671
    01672         return attr->group;
    01673 }
    01674
    01675 const char*
    01676 b_vformat_attribute_get_name (b_VFormatAttribute *attr)
    01677 {
    01678         g_return_val_if_fail (attr != NULL, NULL);
    01679
    01680         return attr->name;
    01681 }
    01682
    01683 const char*
    01684 b_vformat_attribute_get_block (b_VFormatAttribute *attr)
    01685 {
    01686         g_return_val_if_fail (attr != NULL, NULL);
    01687
    01688         return attr->block;
    01689 }
    01690
    01691 GList*
    01692 b_vformat_attribute_get_values (b_VFormatAttribute *attr)
    01693 {
    01694         g_return_val_if_fail (attr != NULL, NULL);
    01695
    01696         return attr->values;
    01697 }
    01698
    01699 GList*
    01700 b_vformat_attribute_get_values_decoded (b_VFormatAttribute *attr)
    01701 {
    01702         g_return_val_if_fail (attr != NULL, NULL);
    01703
    01704         if (!attr->decoded_values) {
    01705                 GList *l;
    01706                 switch (attr->encoding) {
    01707                 case VF_ENCODING_RAW:
    01708                 case VF_ENCODING_8BIT:
    01709                         for (l = attr->values; l; l = l->next)
    01710                                 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new ((char*)l->data));
    01711                         break;
    01712                 case VF_ENCODING_BASE64:
    01713                         for (l = attr->values; l; l = l->next) {
    01714                                 char *decoded = g_strdup ((char*)l->data);
    01715                                 int len = base64_decode_simple (decoded, strlen (decoded));
    01716                                 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
    01717                                 g_free (decoded);
    01718                         }
    01719                         break;
    01720                 case VF_ENCODING_QP:
    01721                         for (l = attr->values; l; l = l->next) {
    01722                                 if (!(l->data))
    01723                                         continue;
    01724                                 char *decoded = g_strdup ((char*)l->data);
    01725                                 int len = quoted_decode_simple (decoded, strlen (decoded));
    01726                                 attr->decoded_values = g_list_append (attr->decoded_values, g_string_new_len (decoded, len));
    01727                                 g_free (decoded);
    01728                         }
    01729                         break;
    01730                 }
    01731         }
    01732
    01733         return attr->decoded_values;
    01734 }
    01735
    01736 gboolean
    01737 b_vformat_attribute_is_single_valued (b_VFormatAttribute *attr)
    01738 {
    01739         g_return_val_if_fail (attr != NULL, FALSE);
    01740
    01741         if (attr->values == NULL
    01742             || attr->values->next != NULL)
    01743                 return FALSE;
    01744
    01745         return TRUE;
    01746 }
    01747
    01748 char*
    01749 b_vformat_attribute_get_value (b_VFormatAttribute *attr)
    01750 {
    01751         GList *values;
    01752
    01753         g_return_val_if_fail (attr != NULL, NULL);
    01754
    01755         values = b_vformat_attribute_get_values (attr);
    01756
    01757         if (!b_vformat_attribute_is_single_valued (attr))
    01758                 BarryLogf(TRACE_INTERNAL, "b_vformat_attribute_get_value called on multivalued attribute");
    01759
    01760         return values ? g_strdup ((char*)values->data) : NULL;
    01761 }
    01762
    01763 GString*
    01764 b_vformat_attribute_get_value_decoded (b_VFormatAttribute *attr)
    01765 {
    01766         GList *values;
    01767         GString *str = NULL;
    01768
    01769         g_return_val_if_fail (attr != NULL, NULL);
    01770
    01771         values = b_vformat_attribute_get_values_decoded (attr);
    01772
    01773         if (!b_vformat_attribute_is_single_valued (attr))
    01774                 BarryLogf(TRACE_INTERNAL, "b_vformat_attribute_get_value_decoded called on multivalued attribute");
    01775
    01776         if (values)
    01777                 str = values->data;
    01778
    01779         return str ? g_string_new_len (str->str, str->len) : NULL;
    01780 }
    01781
    01782 const char *b_vformat_attribute_get_nth_value(b_VFormatAttribute *attr, int nth)
    01783 {
    01784         GList *values = b_vformat_attribute_get_values_decoded(attr);
    01785         if (!values)
    01786                 return NULL;
    01787         GString *retstr = (GString *)g_list_nth_data(values, nth);
    01788         if (!retstr)
    01789                 return NULL;
    01790
    01791         if (!g_utf8_validate(retstr->str, -1, NULL)) {
    01792                 values = b_vformat_attribute_get_values(attr);
    01793                 if (!values)
    01794                         return NULL;
    01795                 return g_list_nth_data(values, nth);
    01796         }
    01797
    01798         return retstr->str;
    01799 }
    01800
    01801 gboolean
    01802 b_vformat_attribute_has_type (b_VFormatAttribute *attr, const char *typestr)
    01803 {
    01804         GList *params;
    01805         GList *p;
    01806
    01807         g_return_val_if_fail (attr != NULL, FALSE);
    01808         g_return_val_if_fail (typestr != NULL, FALSE);
    01809
    01810         params = b_vformat_attribute_get_params (attr);
    01811
    01812         for (p = params; p; p = p->next) {
    01813                 b_VFormatParam *param = p->data;
    01814
    01815                 if (!strcasecmp (b_vformat_attribute_param_get_name (param), "TYPE")) {
    01816                         GList *values = b_vformat_attribute_param_get_values (param);
    01817                         GList *v;
    01818
    01819                         for (v = values; v; v = v->next) {
    01820                                 if (!strcasecmp ((char*)v->data, typestr))
    01821                                         return TRUE;
    01822                         }
    01823                 }
    01824         }
    01825
    01826         return FALSE;
    01827 }
    01828
    01829
    01830 gboolean b_vformat_attribute_has_param(b_VFormatAttribute *attr, const char *name)
    01831 {
    01832         g_return_val_if_fail (attr != NULL, FALSE);
    01833         g_return_val_if_fail (name != NULL, FALSE);
    01834
    01835         GList *params = b_vformat_attribute_get_params(attr);
    01836         GList *p;
    01837         for (p = params; p; p = p->next) {
    01838                 b_VFormatParam *param = p->data;
    01839                 if (!strcasecmp(name, b_vformat_attribute_param_get_name(param)))
    01840                         return TRUE;
    01841         }
    01842         return FALSE;
    01843 }
    01844
    01845 GList*
    01846 b_vformat_attribute_get_params (b_VFormatAttribute *attr)
    01847 {
    01848         g_return_val_if_fail (attr != NULL, NULL);
    01849
    01850         return attr->params;
    01851 }
    01852
    01853 const char*
    01854 b_vformat_attribute_param_get_name (b_VFormatParam *param)
    01855 {
    01856         g_return_val_if_fail (param != NULL, NULL);
    01857
    01858         return param->name;
    01859 }
    01860
    01861 GList*
    01862 b_vformat_attribute_param_get_values (b_VFormatParam *param)
    01863 {
    01864         g_return_val_if_fail (param != NULL, NULL);
    01865
    01866         return param->values;
    01867 }
    01868
    01869 const char *b_vformat_attribute_param_get_nth_value(b_VFormatParam *param, int nth)
    01870 {
    01871         const char *ret = NULL;
    01872         GList *values = b_vformat_attribute_param_get_values(param);
    01873         if (!values)
    01874                 return NULL;
    01875         ret = g_list_nth_data(values, nth);
    01876         return ret;
    01877 }
    01878
    01879 static const char *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    01880
    01881 //static unsigned char _evc_base64_rank[256];
    01882
    01883 static void base64_init(char *rank)
    01884 {
    01885         int i;
    01886
    01887         memset(rank, 0xff, sizeof(rank));
    01888         for (i=0;i<64;i++) {
    01889                 rank[(unsigned int)base64_alphabet[i]] = i;
    01890         }
    01891         rank['='] = 0;
    01892 }
    01893
    01894 /* call this when finished encoding everything, to
    01895    flush off the last little bit */
    01896 static size_t base64_encode_close(const unsigned char *in, size_t inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
    01897 {
    01898         int c1, c2;
    01899         unsigned char *outptr = out;
    01900
    01901         if (inlen>0)
    01902                 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
    01903
    01904         c1 = ((unsigned char *)save)[1];
    01905         c2 = ((unsigned char *)save)[2];
    01906
    01907         switch (((char *)save)[0]) {
    01908         case 2:
    01909                 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
    01910                 g_assert(outptr[2] != 0);
    01911                 goto skip;
    01912         case 1:
    01913                 outptr[2] = '=';
    01914         skip:
    01915                 outptr[0] = base64_alphabet[ c1 >> 2 ];
    01916                 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
    01917                 outptr[3] = '=';
    01918                 outptr += 4;
    01919                 break;
    01920         }
    01921         if (break_lines)
    01922                 *outptr++ = '\n';
    01923
    01924         *save = 0;
    01925         *state = 0;
    01926
    01927         return outptr-out;
    01928 }
    01929
    01930 /*
    01931   performs an 'encode step', only encodes blocks of 3 characters to the
    01932   output at a time, saves left-over state in state and save (initialise to
    01933   0 on first invocation).
    01934 */
    01935 static size_t base64_encode_step(const unsigned char *in, size_t len, gboolean break_lines, unsigned char *out, int *state, int *save)
    01936 {
    01937         register const unsigned char *inptr;
    01938         register unsigned char *outptr;
    01939
    01940         if (len<=0)
    01941                 return 0;
    01942
    01943         inptr = in;
    01944         outptr = out;
    01945
    01946         if (len + ((char *)save)[0] > 2) {
    01947                 const unsigned char *inend = in+len-2;
    01948                 register int c1, c2, c3;
    01949                 register int already;
    01950
    01951                 already = *state;
    01952
    01953                 switch (((char *)save)[0]) {
    01954                 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
    01955                 case 2: c1 = ((unsigned char *)save)[1];
    01956                         c2 = ((unsigned char *)save)[2]; goto skip2;
    01957                 }
    01958
    01959                 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
    01960                 while (inptr < inend) {
    01961                         c1 = *inptr++;
    01962                 skip1:
    01963                         c2 = *inptr++;
    01964                 skip2:
    01965                         c3 = *inptr++;
    01966                         *outptr++ = base64_alphabet[ c1 >> 2 ];
    01967                         *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
    01968                         *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
    01969                         *outptr++ = base64_alphabet[ c3 & 0x3f ];
    01970                         /* this is a bit ugly ... */
    01971                         if (break_lines && (++already)>=19) {
    01972                                 *outptr++='\n';
    01973                                 already = 0;
    01974                         }
    01975                 }
    01976
    01977                 ((char *)save)[0] = 0;
    01978                 len = 2-(inptr-inend);
    01979                 *state = already;
    01980         }
    01981
    01982         if (len>0) {
    01983                 register char *saveout;
    01984
    01985                 /* points to the slot for the next char to save */
    01986                 saveout = & (((char *)save)[1]) + ((char *)save)[0];
    01987
    01988                 /* len can only be 0 1 or 2 */
    01989                 switch(len) {
    01990                 case 2: *saveout++ = *inptr++;
    01991                 case 1: *saveout++ = *inptr++;
    01992                 }
    01993                 ((char *)save)[0]+=len;
    01994         }
    01995
    01996         return outptr-out;
    01997 }
    01998
    01999
    02000 /**
    02001  * base64_decode_step: decode a chunk of base64 encoded data
    02002  * @in: input stream
    02003  * @len: max length of data to decode
    02004  * @out: output stream
    02005  * @state: holds the number of bits that are stored in @save
    02006  * @save: leftover bits that have not yet been decoded
    02007  *
    02008  * Decodes a chunk of base64 encoded data
    02009  **/
    02010 static size_t base64_decode_step(const unsigned char *in, size_t len, unsigned char *out, int *state, unsigned int *save)
    02011 {
    02012         unsigned char base64_rank[256];
    02013         base64_init((char*)base64_rank);
    02014
    02015         register const unsigned char *inptr;
    02016         register unsigned char *outptr;
    02017         const unsigned char *inend;
    02018         unsigned char c;
    02019         register unsigned int v;
    02020         int i;
    02021
    02022         inend = in+len;
    02023         outptr = out;
    02024
    02025         /* convert 4 base64 bytes to 3 normal bytes */
    02026         v=*save;
    02027         i=*state;
    02028         inptr = in;
    02029         while (inptr<inend) {
    02030                 c = base64_rank[*inptr++];
    02031                 if (c != 0xff) {
    02032                         v = (v<<6) | c;
    02033                         i++;
    02034                         if (i==4) {
    02035                                 *outptr++ = v>>16;
    02036                                 *outptr++ = v>>8;
    02037                                 *outptr++ = v;
    02038                                 i=0;
    02039                         }
    02040                 }
    02041         }
    02042
    02043         *save = v;
    02044         *state = i;
    02045
    02046         /* quick scan back for '=' on the end somewhere */
    02047         /* fortunately we can drop 1 output char for each trailing = (upto 2) */
    02048         i=2;
    02049         while (inptr>in && i) {
    02050                 inptr--;
    02051                 if (base64_rank[*inptr] != 0xff) {
    02052                         if (*inptr == '=' && outptr>out)
    02053                                 outptr--;
    02054                         i--;
    02055                 }
    02056         }
    02057
    02058         /* if i!= 0 then there is a truncation error! */
    02059         return outptr-out;
    02060 }
    02061
    02062 static char *base64_encode_simple (const char *data, size_t len)
    02063 {
    02064         unsigned char *out;
    02065         int state = 0, outlen;
    02066         unsigned int save = 0;
    02067
    02068         g_return_val_if_fail (data != NULL, NULL);
    02069
    02070         out = g_malloc (len * 4 / 3 + 5);
    02071         outlen = base64_encode_close ((unsigned char *)data, len, FALSE,
    02072                                       out, &state, (int*)&save);
    02073         out[outlen] = '\0';
    02074         return (char *)out;
    02075 }
    02076
    02077 static size_t base64_decode_simple (char *data, size_t len)
    02078 {
    02079         int state = 0;
    02080         unsigned int save = 0;
    02081
    02082         g_return_val_if_fail (data != NULL, 0);
    02083
    02084         return base64_decode_step ((unsigned char *)data, len,
    02085                                         (unsigned char *)data, &state, &save);
    02086 }
    02087
    02088 static char *quoted_encode_simple(const unsigned char *string, int len)
    02089 {
    02090         GString *tmp = g_string_new("");
    02091
    02092         int i = 0;
    02093         while(string[i] != 0) {
    02094                 if (string[i] > 127 || string[i] == 13 || string[i] == 10 || string[i] == '=') {
    02095                         g_string_append_printf(tmp, "=%02X", string[i]);
    02096                 } else {
    02097                         g_string_append_c(tmp, string[i]);
    02098                 }
    02099                 i++;
    02100         }
    02101
    02102         char *ret = tmp->str;
    02103         g_string_free(tmp, FALSE);
    02104         return ret;
    02105 }
    02106
    02107
    02108 static size_t quoted_decode_simple (char *data, size_t len)
    02109 {
    02110         g_return_val_if_fail (data != NULL, 0);
    02111
    02112         GString *string = g_string_new(data);
    02113         if (!string)
    02114                 return 0;
    02115
    02116         char hex[5];
    02117         hex[4] = 0;
    02118
    02119         while (1) {
    02120                 //Get the index of the next encoded char
    02121                 int i = strcspn(string->str, "=");
    02122                 if (i >= strlen(string->str))
    02123                         break;
    02124
    02125                 strcpy(hex, "0x");
    02126                 strncat(hex, &string->str[i + 1], 2);
    02127                 char rep = ((int)(strtod(hex, NULL)));
    02128                 g_string_erase(string, i, 2);
    02129                 g_string_insert_c(string, i, rep);
    02130         }
    02131
    02132         memset(data, 0, strlen(data));
    02133         strcpy(data, string->str);
    02134         g_string_free(string, 1);
    02135
    02136         return strlen(data);
    02137 }