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