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 }