Logo Search packages:      
Sourcecode: pan version File versions  Download package

pan-glib-extensions.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <config.h>

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>

#include <glib.h>

#include <gmime/gmime-charset.h>
#include <gmime/gmime-filter-charset.h>
#include <gmime/gmime-stream-filter.h>
#include <gmime/gmime-stream-mem.h>
#include <gmime/gmime-utils.h>

#include <iconv.h>
#include <gmime/gmime-iconv.h>
#include <gmime/gmime-iconv-utils.h>

#include <pan/base/base-prefs.h>
#include <pan/base/log.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/pan-glib-extensions.h>


/***
****
****  DIRECTORIES
****
***/

const char*
pan_get_home_dir (void)
{
      static const char * home = NULL;

      if (home == NULL)
      {
            home = g_get_home_dir ();

#ifdef G_OS_WIN32
            if (home == NULL)
                  home = g_get_current_dir ();
#endif
      }

      return home;
}

/***
****
****  STRINGS
****
***/

/* null-safe strstr */
char*
pan_strstr (const char * s1,
            const char * s2)
{
      g_return_val_if_fail (s1!=NULL, NULL);
      g_return_val_if_fail (s2!=NULL, NULL);

      return strstr (s1, s2);
}

/* null-safe strcmp.  NULLs < strings. */
int 
pan_strcmp (const char * a,
            const char * b)
{
      int retval;

      if (a == b)
            retval = 0;
      else if (!a)
            retval = -1;
      else if (!b)
            retval = 1;
      else
            retval = strcmp (a, b);

      return retval;
}

void
replace_gstr (char   ** oldaddr,
              char    * newval)
{
      char * oldval;

      g_return_if_fail (oldaddr != NULL);

      oldval = *oldaddr;
      *oldaddr = newval;
      g_free (oldval);
}

int
pan_strncpy_len (char        * target,
                 const int     target_size,
                 const char  * source_str,
                 const int     source_len)
{
      int len;

      /* sanity clause */
      g_return_val_if_fail (target_size >= 0, 0);
      g_return_val_if_fail (target!=NULL || target_size==0, 0);
      g_return_val_if_fail (source_len >= 0, 0);
      g_return_val_if_fail (source_str!=NULL || source_len==0, 0);

      len = MIN (target_size-1, source_len);
      memcpy (target, source_str, len);
      target[len] = '\0';

      return len;
}

char*
pan_strchr_len (const char * haystack,
                gssize       haystack_len,
                char         needle)
{
      g_return_val_if_fail (haystack!=NULL || haystack_len==0, NULL);

      if (haystack_len > 0)
      {
            register const char * end = haystack + haystack_len;
            for (; haystack!=end; ++haystack)
                  if (*haystack == needle)
                        return (char*) haystack;
      }

      return NULL;
}

char*
pan_strrchr_len (const char * haystack,
                 gssize       haystack_len,
                 char         needle)
{
      register const char * pch = NULL;

      g_return_val_if_fail (haystack!=NULL || haystack_len==0, NULL);

      if (haystack_len > 0)
      {
            pch = haystack + haystack_len - 1;
            for (; pch!=haystack; --pch)
                  if (*pch == needle)
                        break;
            if (*pch != needle)
                  pch = NULL;
      }

      return (char*) pch;
}

int
pan_strcmp_len  (const char * str_a,
                 int          str_a_len,
                 const char * str_b,
                 int          str_b_len)
{
      int retval = memcmp (str_a, str_b, MIN(str_a_len,str_b_len));
        if (!retval)
                retval = str_a_len - str_b_len;
        return retval;
}

gchar *
pan_strnstr_len (const char * haystack,
                 int          haystack_len,
                 const char * needle,
                 int          needle_len)
{
      g_return_val_if_fail (haystack != NULL, NULL);
      g_return_val_if_fail (needle != NULL, NULL);
                                                                                                                                           
      if (haystack_len<0 || needle_len<0)
            return strstr (haystack, needle);
      else
      {
            const char *p = haystack;
            const char *end;

            if (needle_len == 0)
                  return (char*) haystack;

            if (haystack_len < needle_len)
                  return NULL;

            end = haystack + haystack_len - needle_len;

            while (*p && p <= end)
            {
                  int i;
                  for (i=0; i<needle_len; ++i)
                        if (p[i] != needle[i])
                              goto next;
                                                                                                         
                  return (char *)p;
                                                                                                         
            next:
                  ++p;
            }

            return NULL;
      }
} 


/**
 * Replaces the search string inside original with the replace string.
 * This should be safe with overlapping elements of text, though I haven't
 * not tested it with elaborately cruel cases.
 */
char*
pan_substitute (const char * original,
                const char * search,
                const char * replace)
{
      size_t slen;            /* length of search */
      size_t rlen;            /* length of replace */
      size_t tlen;            /* length of target (predicted) */
      gint i;
      const char * o;
      const char * pchar;
      char * t;
      char * retval = NULL;

      g_return_val_if_fail (original!=NULL, NULL);
      g_return_val_if_fail (*original!='\0', NULL);
      g_return_val_if_fail (search!=NULL, NULL);
      g_return_val_if_fail (*search!='\0', NULL);
      g_return_val_if_fail (replace!=NULL, NULL);

      slen = strlen (search);
      rlen = strlen (replace);

      /* calculate the length */

      i = 0;
      tlen = 0;
      pchar = original;
      while ((pchar = pan_strstr (pchar, search))) {
            i++;
            pchar += slen;
      }
      tlen = strlen(original) + i*(rlen - slen);

      /**
      ***  Make the substitution.
      **/

      o = original;
      t = retval = g_malloc(tlen + 1);
      while ((pchar = pan_strstr (o, search))) {
            (void) memcpy (t, o, (size_t)(pchar-o));
            t += pchar-o;
            (void) memcpy (t, replace, (size_t)rlen);
            t += rlen;
            o = pchar + slen;
      }
      (void) strcpy ( t, o );

      pan_warn_if_fail (strlen(retval)==tlen);

      return retval;
}

/***
****
****  GLIB AUGMENTATION
****
***/

void
pan_g_ptr_array_append (GPtrArray   * target,
                        gpointer    * source_ptr,
                        guint         source_qty)
{
      guint old_len;

      g_return_if_fail (target!=NULL);
      g_return_if_fail (source_ptr!=NULL);
      g_return_if_fail (source_qty>0);
       
      old_len = target->len;
      g_ptr_array_set_size (target, old_len+source_qty);
      memcpy (target->pdata+old_len, source_ptr, sizeof(gpointer)*source_qty);
}

void
pan_g_ptr_array_assign  (GPtrArray  * target,
                         gpointer   * source_ptr,
                         guint        source_qty)
{
      g_return_if_fail (target!=NULL);
      g_return_if_fail (source_qty==0 || source_ptr!=NULL);

      g_ptr_array_set_size (target, source_qty);
      memcpy (target->pdata, source_ptr, sizeof(gpointer)*source_qty);
}

GPtrArray*
pan_g_ptr_array_dup     (const GPtrArray  * source)
{
      GPtrArray * retval;

      g_return_val_if_fail (source!=NULL, NULL);

      retval = g_ptr_array_new ();
      pan_g_ptr_array_assign (retval, source->pdata, source->len);
      return retval;
}


static void
pan_hash_to_ptr_array_ghfunc (gpointer key, gpointer val, gpointer data)
{
      g_ptr_array_add ((GPtrArray*)data, val);
}
void
pan_hash_to_ptr_array  (GHashTable   * hash,
                        GPtrArray    * fillme)
{
      g_return_if_fail (fillme!=NULL);
      g_ptr_array_set_size (fillme, 0);

      g_return_if_fail (hash!=NULL);
      pan_g_ptr_array_reserve (fillme, g_hash_table_size(hash));
      g_hash_table_foreach (hash, pan_hash_to_ptr_array_ghfunc, fillme);
}


void
pan_g_ptr_array_reserve (GPtrArray  * a,
                         int          n)
{
      int len;

      g_return_if_fail (a!=NULL);

      len = a->len;
      g_ptr_array_set_size (a, MAX(len, n));
      a->len = len;
}

void
pan_g_ptr_array_insert (GPtrArray   * a,
                        gpointer      ptr,
                        int           index)
{
        g_return_if_fail (a!=NULL);

        if (index<0 || index>=a->len)
      {
                g_ptr_array_add (a, ptr);
      }
        else
        {
            pan_g_ptr_array_reserve (a, a->len+1);
                g_memmove (&a->pdata[index+1],
                           &a->pdata[index],
                           sizeof(gpointer)*(a->len-index));
                a->pdata[index] = ptr;
            ++a->len;
        }
}

void
pan_g_ptr_array_foreach (GPtrArray   * a,
                         GFunc         func,
                         gpointer      user_data)
{
      guint i;

      g_return_if_fail (a!=NULL);
      g_return_if_fail (func!=NULL);

      for (i=0; i!=a->len; ++i)
      {
            gpointer call_data = g_ptr_array_index (a, i);
            (*func)(call_data, user_data);
      }
}

void
pan_g_string_replace (GString       * string,
                      const char    * search,
                  const char    * replace)
{
      const char * pch;
      size_t slen;
      size_t rlen;
      size_t offset;

      g_return_if_fail (string != NULL);
      g_return_if_fail (is_nonempty_string(search));
      g_return_if_fail (replace != NULL);

      slen = strlen (search);
      rlen = strlen (replace);

      offset = 0;
      while ((pch = pan_strstr (string->str + offset, search)) && *pch) {
            const gint pos = pch - string->str;
            g_string_erase (string, pos, slen);
            g_string_insert (string, pos, replace);
            offset = pos + rlen;
      }
}

void
pan_g_string_strstrip (GString * string)
{
      int i;

      /* sanity clause */
      g_return_if_fail (string != NULL);

      /* trim trailing space */
      for (i=string->len-1; i>=0; --i)
            if (!isspace ((guchar)string->str[i]))
                  break;
      g_string_truncate (string, i+1);

      /* trim leading space */
      for (i=0; i<string->len; ++i)
            if (!isspace ((guchar)string->str[i]))
                  break;
      g_string_erase (string, 0, i);
}

/***
****
****  TOKENS
****
***/

 
void
skip_next_token (const char   * pch,
                 const char     delimiter,
                 const char  ** setme_next_token)
{
      register char ch;

      if (is_nonempty_string(pch))
      {
            for (;;) {
                  ch = *pch;
                  if (ch == delimiter) {
                        ++pch; /* skip past delimiter */
                        if (setme_next_token)
                              *setme_next_token = pch;
                        return;
                  }
                  if (ch)
                         ++pch;
                  else {
                        if (setme_next_token)
                              *setme_next_token = pch;
                        return;
                  }
            }
      }
}
 
const char*
get_next_token_run (const char   * pch,
                    const char     delimiter,
                    const char  ** setme_next_token,
                    const char  ** setme_start,
                    int          * setme_len)
{
      const char * start = pch;

      if (pch!=NULL)
            while (*pch && *pch!=delimiter)
                  ++pch;

      *setme_start = start;
      *setme_len = pch!=NULL ? pch-start : 0;
      if (setme_next_token != NULL)
            *setme_next_token = pch!=NULL && *pch==delimiter ? pch+1 : pch;

      return is_nonempty_string(start) ? start : NULL;
}

gboolean
get_next_token_pstring (const char   * pch,
                        char           delimiter,
                        const char  ** setme_next_token,
                        PString      * setme_pstring)
{
      const char * start = pch;

      if (pch!=NULL)
            while (*pch && *pch!=delimiter)
                  ++pch;

      *setme_pstring = pstring_shallow (start, pch!=NULL ? pch-start : 0);

      if (setme_next_token != NULL)
            *setme_next_token = pch!=NULL && *pch==delimiter ? pch+1 : pch;

      return is_nonempty_string(start);
}

glong
get_next_token_long (const char   * pch,
                     const char     delimiter,
                     const char  ** setme_next_token)
{
      glong retval = 0;

      if (is_nonempty_string(pch))
      {
            const char * m;
            const char * end = NULL;

            /* find the end of this token */
            skip_next_token (pch, delimiter, &end);
            if (setme_next_token != NULL)
                  *setme_next_token = end;

            /* only extract an int of there's a digit in this token.
               otherwise if isspace(token) is true, atoi may walk
               right into the next token and give a wrong retval */
            for (m=pch; m!=end; ++m) {
                  if (*m=='-' || *m=='+' || isdigit((guchar)*m)) {
                        retval = atol (m);
                        break;
                  }
            }
      }

      return retval;
}

gulong
get_next_token_ulong (const char   * pch,
                      const char     delimiter,
                      const char  ** setme_next_token)
{
      gulong retval = 0;

      if (is_nonempty_string(pch))
      {
            const char * m;
            const char * end = NULL;

            /* find the end of this token */
            skip_next_token (pch, delimiter, &end);
            if (setme_next_token != NULL)
                  *setme_next_token = end;

            /* only extract an int of there's a digit in this token.
               otherwise if isspace(token) is true, strtoul may walk
               right into the next token and give a wrong retval */
            for (m=pch; m!=end; ++m) {
                  if (*m=='-' || *m=='+' || isdigit((guchar)*m)) {
                        retval = strtoul (m, NULL, 10);
                        break;
                  }
            }
      }

      return retval;
}

int
get_next_token_int (const char   * pch,
                    const char     delimiter,
                    const char  ** setme_next_token)
{
      int retval = 0;

      if (is_nonempty_string(pch))
      {
            const char * m;
            const char * end = NULL;

            /* find the end of this token */
            skip_next_token (pch, delimiter, &end);
            if (setme_next_token != NULL)
                  *setme_next_token = end;

            /* only extract an int of there's a digit in this token.
               otherwise if isspace(token) is true, atoi may walk
               right into the next token and give a wrong retval */
            for (m=pch; m!=end; ++m) {
                  if (*m=='-' || *m=='+' || isdigit((guchar)*m)) {
                        retval = atoi(m);
                        break;
                  }
            }
      }

      return retval;
}

char*
get_next_token_str (const char   * pch,
                    const char     delimiter,
                    const char  ** setme_next_token)
{
      char* retval = NULL;
      register const char* end = NULL;

      if (is_nonempty_string(pch)) {
            end = pch;
            while (is_nonempty_string(end) && *end!=delimiter)
                  ++end;
      }

      if (end != NULL)
            retval = g_strndup (pch, end-pch);

      if (setme_next_token)
            *setme_next_token = is_nonempty_string(end) ? end+1 : end;

      return retval;
}

gboolean
get_next_token_g_str (const char   * pch,
                      const char     delimiter,
                      const char  ** setme_next_token,
                      GString      * setme)
{
      register const char* end = pch;
      gboolean retval;

      while (is_nonempty_string(end) && *end!=delimiter)
            ++end;

      g_string_truncate (setme, 0);

      if (end == pch)
            retval = end==NULL ? FALSE : *end==delimiter;
      else {
            g_string_append_len (setme, pch, end-pch);
            retval = TRUE;
      }

      if (setme_next_token)
            *setme_next_token = is_nonempty_string(end) ? end+1 : end;

      return retval;
}

/***
****
****  UNSORTED
****
***/

int
lower_bound (const void         * key,
             const void         * base,
             size_t               n,
             size_t               size,
             int                  (*compare)(const void *, const void *),
             gboolean           * exact_match )
{
      register int low = 0;
      register int high = n - 1;

      while (low<=high)
      {
            const int mid = (unsigned)(low+high)/2u;
            const void * checkme = (void*)(((char*)(base))+(mid*size));
            const int comp = (*compare)(key,checkme);

            if (comp>0) low = mid+1;
            else if (comp<0 ) high = mid-1;
            else {
                  if ( exact_match!=NULL )
                        *exact_match = TRUE;
                  return mid;
            }
      }

      if (exact_match!=NULL)
            *exact_match = FALSE;

      return low;
}

/***
****
****  XML
****
***/

char*
pan_str_escape (const char * str)
{
      gint size_needed;
      char * retval;
      char * out;
      const char * in;

      /* sanity clause */
      if (!is_nonempty_string (str))
            return g_strdup ("");

      /* how big must the buffer be? */
      size_needed = 1;
      for (in=str; *in; ++in) {
            gint inc;
            switch (*in) {
                  case '&': inc = 5; break;
                  case '>': case '<': inc = 4; break;
                  case '"': inc = 6; break;
                  default: inc = 1; break;
            }
            size_needed += inc;
      }

      /* build the output string */
      retval = out = g_malloc (size_needed);
      for (in=str; *in; ++in) {
            switch (*in) {
                  case '&': memcpy (out, "&amp;", 5); out += 5; break;
                  case '>': memcpy (out, "&gt;", 4); out += 4; break;
                  case '<': memcpy (out, "&lt;", 4); out += 4; break;
                  case '"': memcpy (out, "&quot;", 6); out += 6; break;
                  default: *out++ = *in; break;
            }
      }
      *out = '\0';

      return retval;
}

char*
pan_str_unescape (const char * escaped)
{
      const char * src;
      char * buf;
      char * tgt;

      g_return_val_if_fail (escaped!=NULL, NULL);

      src = escaped;
      tgt = buf = g_new0 (char, strlen(src)+1);
      while (*src)
      {
            if (*src!='&') { *tgt++ = *src++; }
            else if (!strncmp(src,"&#",2)) {
                  char * endp = NULL;
                  long ch;
                  src += 2;
                  if ((ch = strtoul(src, &endp, 0))) {
                        *tgt++ = (char) ch;
                        src = endp + 1;
                  }
            }
            else if (!strncmp(src,"&lt;",4)) { *tgt++ = '<';  src+=4; }
            else if (!strncmp(src,"&gt;",4))   { *tgt++ = '>';  src+=4; }
            else if (!strncmp(src,"&amp;",5))  { *tgt++ = '&';  src+=5; }
            else if (!strncmp(src,"&apos;",6)) { *tgt++ = '\''; src+=6; }
            else if (!strncmp(src,"&quot;",6)) { *tgt++ = '"';  src+=6; }
            else *tgt++ = *src++;
      }

      *tgt = '\0';

      return buf;
}

/***
****
***/

struct tm*
pan_localtime_r(const time_t *const timep, struct tm *p_tm)
{
      struct tm * retval;
#ifdef HAVE_LOCALTIME_R
      retval = localtime_r (timep, p_tm);
#else
      static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

      g_static_mutex_lock (&mutex);
      retval = localtime (timep);
      if (retval != NULL) {
            memcpy (p_tm, retval, sizeof(struct tm));
            retval = p_tm;
      }
      g_static_mutex_unlock (&mutex);
#endif
      return retval;
}

struct tm*
pan_gmtime_r (const time_t * const timep, struct tm * p_tm)
{
      struct tm * retval;
#ifdef HAVE_GMTIME_R
      retval = gmtime_r (timep, p_tm);
#else
      static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

      g_static_mutex_lock (&mutex);
      retval = gmtime (timep);
      if (retval != NULL) {
            memcpy (p_tm, retval, sizeof(struct tm));
            retval = p_tm;
      }
      g_static_mutex_unlock (&mutex);
#endif
      return retval;
}

/* The language isn't used yet, but will be useful for get the default
 * language when we integrate a spellchecker.
 */

struct LocaleStruct {
    const char *locale, *charset;
} locales[] = {
    {"en_US",        "ISO-8859-1"}, 
    {"pt_BR",        "ISO-8859-1"},
    {"ca_ES",        "ISO-8859-15"},
    {"zh_CN.GB2312", "gb2312"},
    {"zh_TW.Big5",   "big5"},
    {"cs_CZ",        "ISO-8859-2"},
    {"da_DK",        "ISO-8859-1"},
    {"de_DE",        "ISO-8859-15"},
    {"nl_NL",        "ISO-8859-15"},
    {"et_EE",        "ISO-8859-15"},
    {"fi_FI",        "ISO-8859-15"},
    {"fr_FR",        "ISO-8859-15"},
    {"el_GR",        "ISO-8859-7"},
    {"hu_HU",        "ISO-8859-2"},
    {"it_IT",        "ISO-8859-15"},
    {"ja_JP",        "ISO-2022-jp"},
    {"ko_KR",        "euc-kr"},
    {"lv_LV",        "ISO-8859-13"},
    {"lt_LT",        "ISO-8859-13"},
    {"no_NO",        "ISO-8859-1"},
    {"pl_PL",        "ISO-8859-2"},
    {"pt_PT",        "ISO-8859-15"},
    {"ro_RO",        "ISO-8859-2"},
    {"ru_RU",        "KOI8-R"},
    {"ru_SU",        "ISO-8859-5"},
    {"sk_SK",        "ISO-8859-2"},
    {"es_ES",        "ISO-8859-15"},
    {"sv_SE",        "ISO-8859-1"},
    {"tr_TR",        "ISO-8859-9"},
    {"uk_UK",        "KOI8-U"}
};

/* find_locale_index_by_locale:
 * finds the longest fit so the one who has en_GB will get en_US if en_GB
 * is not defined.
 * This function is lifted from Balsa.
 */
static gint
get_closest_locale (void)
{
      const char * locale = setlocale (LC_CTYPE, NULL);
      guint i, j, maxfit = 0, maxpos = 0;

      g_return_val_if_fail (locale != NULL, -1);

      if (!locale || strcmp(locale, "C") == 0)
            return 0;

      for (i = 0; i < G_N_ELEMENTS(locales); i++) {
            for (j=0; locale[j] && locales[i].locale[j] == locale[j]; j++);
            if (j > maxfit) {
                  maxfit = j;
                  maxpos = i;
            }
      }
      return maxpos;
}

#define PAN_DEFAULT_CHARSET  "ISO-8859-1"

const char *
get_charset_from_locale (void)
{
      gint loc_idx;

      loc_idx = get_closest_locale ();

      return loc_idx != -1 ? locales[loc_idx].charset : PAN_DEFAULT_CHARSET;
}

const char*
pan_utf8ize (const char    * str,
             gssize          len,
             char         ** g_freeme)
{
      const char * retval;

      g_return_val_if_fail (g_freeme!=NULL, NULL);

      if (!is_nonempty_string (str))
            retval = *g_freeme = g_strdup ("");
      else if  (!g_utf8_validate (str, len, NULL)) {
            GError * err = NULL;
            retval = *g_freeme = g_locale_to_utf8 (str, len, NULL, NULL, &err);

            if (err != NULL) {
                  g_error_free (err);
                  err = NULL;
                  /* last resort ... */
                  retval = *g_freeme = g_convert (str, len, "UTF-8", "ISO-8859-1", NULL, NULL, &err);
            }

            if (err != NULL) {
                  log_add_va (LOG_ERROR, _("Error converting string \"%*s\" to utf8: %d - %s"), len, str, err->code, err->message);
                  g_error_free (err);
            }
      }
      else {
            *g_freeme = NULL;
            retval = str;
      }

      return retval;
}

static char*
g_mime_charset_strndup (const char     * to_charset,
                        const char     * from_charset,
                        const char     * text,
                        int              text_len)
{
      char * retval;
      gssize out_len;
      GMimeStream * in_stream;
      GMimeStream * in_stream_filter;
      GMimeFilter * charset_filter;
      GMimeStream * out_stream;
      GByteArray * byte_array;

      /* set up an input stream with the desired charset filter */
      in_stream = g_mime_stream_mem_new_with_buffer (text, text_len);
      charset_filter = g_mime_filter_charset_new (from_charset, to_charset);
      in_stream_filter = g_mime_stream_filter_new_with_stream (in_stream);
      g_mime_stream_filter_add (GMIME_STREAM_FILTER(in_stream_filter), charset_filter);

      /* set up an output stream attached to a byte array */
      byte_array = g_byte_array_new ();
      out_stream = g_mime_stream_mem_new ();
      g_mime_stream_mem_set_byte_array (GMIME_STREAM_MEM(out_stream), byte_array);

      /* write the input stream to the output stream */
      out_len = g_mime_stream_write_to_stream (in_stream_filter, out_stream);

      /* if the write was successful, zero-terminate the string and return it. */
      if (out_len < 0)
            retval = NULL;
      else {
            g_byte_array_append (byte_array, "", 1);
            retval = byte_array->data;
            g_byte_array_free (byte_array, FALSE);
      }

      /* cleanup */
      g_object_unref (G_OBJECT(out_stream));
      g_object_unref (G_OBJECT(in_stream_filter));
      g_object_unref (G_OBJECT(in_stream));

      return retval;
}

static char*
pan_clean_utf8 (const char * in,
                int          in_len)
{
      GString * str = g_string_new (NULL);
      const char *end;
      char * retval;

      while (g_utf8_validate (in, in_len, &end) == FALSE) {
            if (end > in)
                  g_string_append_len (str, in, end-in);
            g_string_append_c (str, '?');
            if (in_len != -1) 
                  in_len -= (1+end-in);
            in = end+1;
      }

      g_string_append_len (str, in, in_len);
      retval = str->str;
      g_string_free (str, FALSE);

      return retval;
}

char*
pan_header_to_utf8 (const char   * header,
                    int            header_len,
                    const char   * charset)
{
      char * retval = NULL;

      if (header == NULL)
            return NULL;

      if (header_len < 0)
            header_len = strlen (header);

      /* nice girls encode their 8bit strings */
      if (g_strstr_len (header, header_len, "=?") != NULL) {
            guchar * pch;
                  pch = (guchar*) g_strndup (header, header_len);
            retval = g_mime_utils_8bit_header_decode (pch);
            g_free (pch);
      }

      /* naughty boys don't properly encode their 8bit string */
      else if (g_mime_utils_text_is_8bit (header, header_len) &&
             !g_utf8_validate (header, header_len, NULL)) {
            if (is_nonempty_string(charset)) {
                  retval = g_mime_charset_strndup ("UTF-8", charset, header, header_len);
                  if (!g_utf8_validate (retval, -1, NULL)) {
                        g_free (retval);
                        retval = NULL;
                  }
            }
            if (retval == NULL) {
                  const char * local_charset = get_charset_from_locale ();
                  retval = g_mime_charset_strndup ("UTF-8", local_charset, header, header_len);

                  if (!g_utf8_validate (retval, -1, NULL)) {
                        g_free (retval);
                        retval = NULL;
                  }
            }

            if (retval == NULL)
                  retval = pan_clean_utf8 (header, header_len);
            else
                  replace_gstr (&retval, pan_clean_utf8 (retval, -1));
      }

      /* fallback */
      if (retval == NULL)
            retval = g_strndup (header, header_len);

      return retval;
}

char*
pan_body_to_utf8 (GMimePart     * part,
                  const char    * default_charset)
{
      char * retval = NULL;
      const char * content;
      guint content_len = 0u;

      /* sanity clause */
      g_return_val_if_fail (GMIME_IS_PART(part), NULL);

      /* check for an empty part */
      content = g_mime_part_get_content (part, &content_len);
      if (!is_nonempty_string(content))
            return NULL;

      /* use the part's content-type charset, if it's got one */
      if (retval == NULL) {
            const char * charset = g_mime_object_get_content_type_parameter (GMIME_OBJECT (part), "charset");
            if (is_nonempty_string (charset))
                  retval = g_mime_charset_strndup ("UTF-8", charset, content, content_len);
      }

      /* try the group's default charset */
      if (retval==NULL && is_nonempty_string (default_charset))
            retval = g_mime_charset_strndup ("UTF-8", default_charset, content, content_len);

      /* otherwise just use the text and hope for the best */
      if (retval == NULL) {
            const char * end = NULL;
            if (!g_utf8_validate (content, content_len, &end))
                  log_add_va (LOG_ERROR, "Some parts of the article are corrupt and can't be displayed.");
            retval = g_strndup (content, end-content);
      }

      return retval;
}

Generated by  Doxygen 1.6.0   Back to index