From f36577b27b6f352f140cf1f25755d39661bd4072 Mon Sep 17 00:00:00 2001 From: hiro Date: Wed, 31 Aug 2005 06:10:31 +0000 Subject: made some core modules library (libsylph). git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@528 ee746299-78ed-0310-b773-934348b2243d --- ChangeLog | 18 + ChangeLog.ja | 19 + Makefile.am | 2 +- configure.in | 3 +- libsylph/Makefile.am | 25 + libsylph/base64.c | 168 +++ libsylph/base64.h | 46 + libsylph/codeconv.c | 1982 +++++++++++++++++++++++++ libsylph/codeconv.h | 239 +++ libsylph/defs.h | 122 ++ libsylph/prefs.c | 465 ++++++ libsylph/prefs.h | 80 + libsylph/quoted-printable.c | 232 +++ libsylph/quoted-printable.h | 36 + libsylph/session.c | 793 ++++++++++ libsylph/session.h | 205 +++ libsylph/socket.c | 1397 ++++++++++++++++++ libsylph/socket.h | 124 ++ libsylph/ssl.c | 175 +++ libsylph/ssl.h | 58 + libsylph/stringtable.c | 163 ++ libsylph/stringtable.h | 38 + libsylph/unmime.c | 134 ++ libsylph/unmime.h | 27 + libsylph/utils.c | 3436 +++++++++++++++++++++++++++++++++++++++++++ libsylph/utils.h | 493 +++++++ libsylph/uuencode.c | 101 ++ libsylph/uuencode.h | 24 + libsylph/xml.c | 655 +++++++++ libsylph/xml.h | 108 ++ src/Makefile.am | 18 +- src/base64.c | 168 --- src/base64.h | 46 - src/codeconv.c | 1982 ------------------------- src/codeconv.h | 239 --- src/prefs.c | 465 ------ src/prefs.h | 80 - src/quoted-printable.c | 232 --- src/quoted-printable.h | 36 - src/session.c | 793 ---------- src/session.h | 205 --- src/socket.c | 1397 ------------------ src/socket.h | 124 -- src/ssl.c | 175 --- src/ssl.h | 58 - src/stringtable.c | 163 -- src/stringtable.h | 38 - src/unmime.c | 134 -- src/unmime.h | 27 - src/utils.c | 3436 ------------------------------------------- src/utils.h | 493 ------- src/uuencode.c | 101 -- src/uuencode.h | 24 - src/xml.c | 656 --------- src/xml.h | 108 -- 55 files changed, 11370 insertions(+), 11196 deletions(-) create mode 100644 libsylph/Makefile.am create mode 100644 libsylph/base64.c create mode 100644 libsylph/base64.h create mode 100644 libsylph/codeconv.c create mode 100644 libsylph/codeconv.h create mode 100644 libsylph/defs.h create mode 100644 libsylph/prefs.c create mode 100644 libsylph/prefs.h create mode 100644 libsylph/quoted-printable.c create mode 100644 libsylph/quoted-printable.h create mode 100644 libsylph/session.c create mode 100644 libsylph/session.h create mode 100644 libsylph/socket.c create mode 100644 libsylph/socket.h create mode 100644 libsylph/ssl.c create mode 100644 libsylph/ssl.h create mode 100644 libsylph/stringtable.c create mode 100644 libsylph/stringtable.h create mode 100644 libsylph/unmime.c create mode 100644 libsylph/unmime.h create mode 100644 libsylph/utils.c create mode 100644 libsylph/utils.h create mode 100644 libsylph/uuencode.c create mode 100644 libsylph/uuencode.h create mode 100644 libsylph/xml.c create mode 100644 libsylph/xml.h delete mode 100644 src/base64.c delete mode 100644 src/base64.h delete mode 100644 src/codeconv.c delete mode 100644 src/codeconv.h delete mode 100644 src/prefs.c delete mode 100644 src/prefs.h delete mode 100644 src/quoted-printable.c delete mode 100644 src/quoted-printable.h delete mode 100644 src/session.c delete mode 100644 src/session.h delete mode 100644 src/socket.c delete mode 100644 src/socket.h delete mode 100644 src/ssl.c delete mode 100644 src/ssl.h delete mode 100644 src/stringtable.c delete mode 100644 src/stringtable.h delete mode 100644 src/unmime.c delete mode 100644 src/unmime.h delete mode 100644 src/utils.c delete mode 100644 src/utils.h delete mode 100644 src/uuencode.c delete mode 100644 src/uuencode.h delete mode 100644 src/xml.c delete mode 100644 src/xml.h diff --git a/ChangeLog b/ChangeLog index 7da95688..1f656473 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2005-08-31 + + * libsylph/Makefile.am + libsylph/base64.[ch] + libsylph/codeconv.[ch] + libsylph/defs.h + libsylph/prefs.[ch] + libsylph/quoted-printable.[ch] + libsylph/session.[ch] + libsylph/socket.[ch] + libsylph/ssl.[ch] + libsylph/stringtable.[ch] + libsylph/unmime.[ch] + libsylph/utils.[ch] + libsylph/uuencode.[ch] + libsylph/xml.[ch]: made some core modules library (libsylph). + * src/Makefile.am: modified so that it uses libsylph.la. + 2005-08-30 * src/prefs.[ch] diff --git a/ChangeLog.ja b/ChangeLog.ja index 01659a92..552108ab 100644 --- a/ChangeLog.ja +++ b/ChangeLog.ja @@ -1,3 +1,22 @@ +2005-08-31 + + * libsylph/Makefile.am + libsylph/base64.[ch] + libsylph/codeconv.[ch] + libsylph/defs.h + libsylph/prefs.[ch] + libsylph/quoted-printable.[ch] + libsylph/session.[ch] + libsylph/socket.[ch] + libsylph/ssl.[ch] + libsylph/stringtable.[ch] + libsylph/unmime.[ch] + libsylph/utils.[ch] + libsylph/uuencode.[ch] + libsylph/xml.[ch]: いくつかのコアモジュールをライブラリ化した + (libsylph)。 + * src/Makefile.am: libsylph.la を使うように修正。 + 2005-08-30 * src/prefs.[ch] diff --git a/Makefile.am b/Makefile.am index 880e6d73..55f65ca3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = ac po src manual faq +SUBDIRS = ac libsylph src po manual faq EXTRA_DIST = config.rpath \ ChangeLog.ja \ diff --git a/configure.in b/configure.in index 77a12372..bcdccae9 100644 --- a/configure.in +++ b/configure.in @@ -342,10 +342,11 @@ AC_OUTPUT([ Makefile sylpheed.spec ac/Makefile -po/Makefile.in +libsylph/Makefile src/version.h src/Makefile src/icons/Makefile +po/Makefile.in faq/Makefile faq/de/Makefile faq/en/Makefile diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am new file mode 100644 index 00000000..bb2eec32 --- /dev/null +++ b/libsylph/Makefile.am @@ -0,0 +1,25 @@ + +AM_CPPFLAGS = -DG_LOG_DOMAIN=\"LibSylph\" + +INCLUDES = $(GLIB_CFLAGS) -I$(top_srcdir) -I$(includedir) + +#lib_LTLIBRARIES = libsylph.la +noinst_LTLIBRARIES = libsylph.la + +libsylph_la_SOURCES = \ + defs.h \ + base64.c base64.h \ + codeconv.c codeconv.h \ + prefs.c prefs.h \ + quoted-printable.c quoted-printable.h \ + session.c session.h \ + socket.c socket.h \ + ssl.c ssl.h \ + stringtable.c stringtable.h \ + unmime.c unmime.h \ + utils.c utils.h \ + uuencode.c uuencode.h \ + xml.c xml.h + +libsylph_la_LDFLAGS = +libsylph_la_LIBADD = $(GLIB_LIBS) diff --git a/libsylph/base64.c b/libsylph/base64.c new file mode 100644 index 00000000..484cd286 --- /dev/null +++ b/libsylph/base64.c @@ -0,0 +1,168 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include + +#include "base64.h" + +static const gchar base64char[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const gchar base64val[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 +}; + +#define BASE64VAL(c) (isascii((guchar)c) ? base64val[(gint)(c)] : -1) + +void base64_encode(gchar *out, const guchar *in, gint inlen) +{ + const guchar *inp = in; + gchar *outp = out; + + while (inlen >= 3) { + *outp++ = base64char[(inp[0] >> 2) & 0x3f]; + *outp++ = base64char[((inp[0] & 0x03) << 4) | + ((inp[1] >> 4) & 0x0f)]; + *outp++ = base64char[((inp[1] & 0x0f) << 2) | + ((inp[2] >> 6) & 0x03)]; + *outp++ = base64char[inp[2] & 0x3f]; + + inp += 3; + inlen -= 3; + } + + if (inlen > 0) { + *outp++ = base64char[(inp[0] >> 2) & 0x3f]; + if (inlen == 1) { + *outp++ = base64char[(inp[0] & 0x03) << 4]; + *outp++ = '='; + } else { + *outp++ = base64char[((inp[0] & 0x03) << 4) | + ((inp[1] >> 4) & 0x0f)]; + *outp++ = base64char[((inp[1] & 0x0f) << 2)]; + } + *outp++ = '='; + } + + *outp = '\0'; +} + +gint base64_decode(guchar *out, const gchar *in, gint inlen) +{ + const gchar *inp = in; + guchar *outp = out; + gchar buf[4]; + + if (inlen < 0) + inlen = G_MAXINT; + + while (inlen >= 4 && *inp != '\0') { + buf[0] = *inp++; + inlen--; + if (BASE64VAL(buf[0]) == -1) break; + + buf[1] = *inp++; + inlen--; + if (BASE64VAL(buf[1]) == -1) break; + + buf[2] = *inp++; + inlen--; + if (buf[2] != '=' && BASE64VAL(buf[2]) == -1) break; + + buf[3] = *inp++; + inlen--; + if (buf[3] != '=' && BASE64VAL(buf[3]) == -1) break; + + *outp++ = ((BASE64VAL(buf[0]) << 2) & 0xfc) | + ((BASE64VAL(buf[1]) >> 4) & 0x03); + if (buf[2] != '=') { + *outp++ = ((BASE64VAL(buf[1]) & 0x0f) << 4) | + ((BASE64VAL(buf[2]) >> 2) & 0x0f); + if (buf[3] != '=') { + *outp++ = ((BASE64VAL(buf[2]) & 0x03) << 6) | + (BASE64VAL(buf[3]) & 0x3f); + } + } + } + + return outp - out; +} + +Base64Decoder *base64_decoder_new(void) +{ + Base64Decoder *decoder; + + decoder = g_new0(Base64Decoder, 1); + return decoder; +} + +void base64_decoder_free(Base64Decoder *decoder) +{ + g_free(decoder); +} + +gint base64_decoder_decode(Base64Decoder *decoder, + const gchar *in, guchar *out) +{ + gint len, total_len = 0; + gint buf_len; + gchar buf[4]; + + g_return_val_if_fail(decoder != NULL, -1); + g_return_val_if_fail(in != NULL, -1); + g_return_val_if_fail(out != NULL, -1); + + buf_len = decoder->buf_len; + memcpy(buf, decoder->buf, sizeof(buf)); + + for (;;) { + while (buf_len < 4) { + gchar c = *in; + + in++; + if (c == '\0') break; + if (c == '\r' || c == '\n') continue; + if (c != '=' && BASE64VAL(c) == -1) + return -1; + buf[buf_len++] = c; + } + if (buf_len < 4 || buf[0] == '=' || buf[1] == '=') { + decoder->buf_len = buf_len; + memcpy(decoder->buf, buf, sizeof(buf)); + return total_len; + } + len = base64_decode(out, buf, 4); + out += len; + total_len += len; + buf_len = 0; + if (len < 3) { + decoder->buf_len = 0; + return total_len; + } + } +} diff --git a/libsylph/base64.h b/libsylph/base64.h new file mode 100644 index 00000000..4aa55758 --- /dev/null +++ b/libsylph/base64.h @@ -0,0 +1,46 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __BASE64_H__ +#define __BASE64_H__ + +#include + +typedef struct _Base64Decoder Base64Decoder; + +struct _Base64Decoder +{ + gint buf_len; + gchar buf[4]; +}; + +void base64_encode (gchar *out, + const guchar *in, + gint inlen); +gint base64_decode (guchar *out, + const gchar *in, + gint inlen); + +Base64Decoder *base64_decoder_new (void); +void base64_decoder_free (Base64Decoder *decoder); +gint base64_decoder_decode (Base64Decoder *decoder, + const gchar *in, + guchar *out); + +#endif /* __BASE64_H__ */ diff --git a/libsylph/codeconv.c b/libsylph/codeconv.c new file mode 100644 index 00000000..c88b588c --- /dev/null +++ b/libsylph/codeconv.c @@ -0,0 +1,1982 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include + +#if HAVE_LOCALE_H +# include +#endif + +#include + +#include "codeconv.h" +#include "unmime.h" +#include "base64.h" +#include "quoted-printable.h" +#include "utils.h" + +typedef enum +{ + JIS_ASCII, + JIS_KANJI, + JIS_HWKANA, + JIS_AUXKANJI +} JISState; + +#define SUBST_CHAR '_' +#define ESC '\033' + +#define iseuckanji(c) \ + (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe) +#define iseuchwkana1(c) \ + (((c) & 0xff) == 0x8e) +#define iseuchwkana2(c) \ + (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) +#define iseucaux(c) \ + (((c) & 0xff) == 0x8f) +#define issjiskanji1(c) \ + ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \ + (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc)) +#define issjiskanji2(c) \ + ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \ + (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc)) +#define issjishwkana(c) \ + (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) + +#define K_IN() \ + if (state != JIS_KANJI) { \ + *out++ = ESC; \ + *out++ = '$'; \ + *out++ = 'B'; \ + state = JIS_KANJI; \ + } + +#define K_OUT() \ + if (state != JIS_ASCII) { \ + *out++ = ESC; \ + *out++ = '('; \ + *out++ = 'B'; \ + state = JIS_ASCII; \ + } + +#define HW_IN() \ + if (state != JIS_HWKANA) { \ + *out++ = ESC; \ + *out++ = '('; \ + *out++ = 'I'; \ + state = JIS_HWKANA; \ + } + +#define AUX_IN() \ + if (state != JIS_AUXKANJI) { \ + *out++ = ESC; \ + *out++ = '$'; \ + *out++ = '('; \ + *out++ = 'D'; \ + state = JIS_AUXKANJI; \ + } + +static gchar *conv_jistoeuc(const gchar *inbuf, gint *error); +static gchar *conv_euctojis(const gchar *inbuf, gint *error); +static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error); + +static gchar *conv_jistoutf8(const gchar *inbuf, gint *error); +static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error); +static gchar *conv_euctoutf8(const gchar *inbuf, gint *error); +static gchar *conv_anytoutf8(const gchar *inbuf, gint *error); + +static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error); +static gchar *conv_utf8tojis(const gchar *inbuf, gint *error); + +/* static void conv_unreadable_eucjp(gchar *str); */ +static void conv_unreadable_8bit(gchar *str); +/* static void conv_unreadable_latin(gchar *str); */ + +static gchar *conv_jistodisp(const gchar *inbuf, gint *error); +static gchar *conv_sjistodisp(const gchar *inbuf, gint *error); +static gchar *conv_euctodisp(const gchar *inbuf, gint *error); + +static gchar *conv_anytodisp(const gchar *inbuf, gint *error); +static gchar *conv_ustodisp(const gchar *inbuf, gint *error); +static gchar *conv_noconv(const gchar *inbuf, gint *error); + +static gchar *conv_jistoeuc(const gchar *inbuf, gint *error) +{ + gchar *outbuf; + const guchar *in = (guchar *)inbuf; + guchar *out; + JISState state = JIS_ASCII; + gint error_ = 0; + + outbuf = g_malloc(strlen(inbuf) * 2 + 1); + out = (guchar *)outbuf; + + while (*in != '\0') { + if (*in == ESC) { + in++; + if (*in == '$') { + if (*(in + 1) == '@' || *(in + 1) == 'B') { + state = JIS_KANJI; + in += 2; + } else if (*(in + 1) == '(' && + *(in + 2) == 'D') { + state = JIS_AUXKANJI; + in += 3; + } else { + /* unknown escape sequence */ + error_ = -1; + state = JIS_ASCII; + } + } else if (*in == '(') { + if (*(in + 1) == 'B' || *(in + 1) == 'J') { + state = JIS_ASCII; + in += 2; + } else if (*(in + 1) == 'I') { + state = JIS_HWKANA; + in += 2; + } else { + /* unknown escape sequence */ + error_ = -1; + state = JIS_ASCII; + } + } else { + /* unknown escape sequence */ + error_ = -1; + state = JIS_ASCII; + } + } else if (*in == 0x0e) { + state = JIS_HWKANA; + in++; + } else if (*in == 0x0f) { + state = JIS_ASCII; + in++; + } else { + switch (state) { + case JIS_ASCII: + *out++ = *in++; + break; + case JIS_KANJI: + *out++ = *in++ | 0x80; + if (*in == '\0') break; + *out++ = *in++ | 0x80; + break; + case JIS_HWKANA: + *out++ = 0x8e; + *out++ = *in++ | 0x80; + break; + case JIS_AUXKANJI: + *out++ = 0x8f; + *out++ = *in++ | 0x80; + if (*in == '\0') break; + *out++ = *in++ | 0x80; + break; + } + } + } + + *out = '\0'; + + if (error) + *error = error_; + + return outbuf; +} + +#define JIS_HWDAKUTEN 0x5e +#define JIS_HWHANDAKUTEN 0x5f + +static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym) +{ + static guint16 h2z_tbl[] = { + /* 0x20 - 0x2f */ + 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521, + 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543, + /* 0x30 - 0x3f */ + 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d, + 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d, + /* 0x40 - 0x4f */ + 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c, + 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e, + /* 0x50 - 0x5f */ + 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569, + 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c + }; + + static guint16 dakuten_tbl[] = { + /* 0x30 - 0x3f */ + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e, + 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e, + /* 0x40 - 0x4f */ + 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000 + }; + + static guint16 handakuten_tbl[] = { + /* 0x4a - 0x4e */ + 0x2551, 0x2554, 0x2557, 0x255a, 0x255d + }; + + guint16 out_code; + + jis_code &= 0x7f; + sound_sym &= 0x7f; + + if (jis_code < 0x21 || jis_code > 0x5f) + return 0; + + if (sound_sym == JIS_HWDAKUTEN && + jis_code >= 0x36 && jis_code <= 0x4e) { + out_code = dakuten_tbl[jis_code - 0x30]; + if (out_code != 0) { + *outbuf = out_code >> 8; + *(outbuf + 1) = out_code & 0xff; + return 2; + } + } + + if (sound_sym == JIS_HWHANDAKUTEN && + jis_code >= 0x4a && jis_code <= 0x4e) { + out_code = handakuten_tbl[jis_code - 0x4a]; + *outbuf = out_code >> 8; + *(outbuf + 1) = out_code & 0xff; + return 2; + } + + out_code = h2z_tbl[jis_code - 0x20]; + *outbuf = out_code >> 8; + *(outbuf + 1) = out_code & 0xff; + return 1; +} + +static gchar *conv_euctojis(const gchar *inbuf, gint *error) +{ + gchar *outbuf; + const guchar *in = (guchar *)inbuf; + guchar *out; + JISState state = JIS_ASCII; + gint error_ = 0; + + outbuf = g_malloc(strlen(inbuf) * 3 + 4); + out = (guchar *)outbuf; + + while (*in != '\0') { + if (isascii(*in)) { + K_OUT(); + *out++ = *in++; + } else if (iseuckanji(*in)) { + if (iseuckanji(*(in + 1))) { + K_IN(); + *out++ = *in++ & 0x7f; + *out++ = *in++ & 0x7f; + } else { + error_ = -1; + K_OUT(); + *out++ = SUBST_CHAR; + in++; + if (*in != '\0' && !isascii(*in)) { + *out++ = SUBST_CHAR; + in++; + } + } + } else if (iseuchwkana1(*in)) { + if (iseuchwkana2(*(in + 1))) { + //if (prefs_common.allow_jisx0201_kana) { + if (0) { + HW_IN(); + in++; + *out++ = *in++ & 0x7f; + } else { + guchar jis_ch[2]; + gint len; + + if (iseuchwkana1(*(in + 2)) && + iseuchwkana2(*(in + 3))) + len = conv_jis_hantozen + (jis_ch, + *(in + 1), *(in + 3)); + else + len = conv_jis_hantozen + (jis_ch, + *(in + 1), '\0'); + if (len == 0) + in += 2; + else { + K_IN(); + in += len * 2; + *out++ = jis_ch[0]; + *out++ = jis_ch[1]; + } + } + } else { + error_ = -1; + K_OUT(); + in++; + if (*in != '\0' && !isascii(*in)) { + *out++ = SUBST_CHAR; + in++; + } + } + } else if (iseucaux(*in)) { + in++; + if (iseuckanji(*in) && iseuckanji(*(in + 1))) { + AUX_IN(); + *out++ = *in++ & 0x7f; + *out++ = *in++ & 0x7f; + } else { + error_ = -1; + K_OUT(); + if (*in != '\0' && !isascii(*in)) { + *out++ = SUBST_CHAR; + in++; + if (*in != '\0' && !isascii(*in)) { + *out++ = SUBST_CHAR; + in++; + } + } + } + } else { + error_ = -1; + K_OUT(); + *out++ = SUBST_CHAR; + in++; + } + } + + K_OUT(); + *out = '\0'; + + if (error) + *error = error_; + + return outbuf; +} + +static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error) +{ + gchar *outbuf; + const guchar *in = (guchar *)inbuf; + guchar *out; + gint error_ = 0; + + outbuf = g_malloc(strlen(inbuf) * 2 + 1); + out = (guchar *)outbuf; + + while (*in != '\0') { + if (isascii(*in)) { + *out++ = *in++; + } else if (issjiskanji1(*in)) { + if (issjiskanji2(*(in + 1))) { + guchar out1 = *in; + guchar out2 = *(in + 1); + guchar row; + + row = out1 < 0xa0 ? 0x70 : 0xb0; + if (out2 < 0x9f) { + out1 = (out1 - row) * 2 - 1; + out2 -= out2 > 0x7f ? 0x20 : 0x1f; + } else { + out1 = (out1 - row) * 2; + out2 -= 0x7e; + } + + *out++ = out1 | 0x80; + *out++ = out2 | 0x80; + in += 2; + } else { + error_ = -1; + *out++ = SUBST_CHAR; + in++; + if (*in != '\0' && !isascii(*in)) { + *out++ = SUBST_CHAR; + in++; + } + } + } else if (issjishwkana(*in)) { + *out++ = 0x8e; + *out++ = *in++; + } else { + error_ = -1; + *out++ = SUBST_CHAR; + in++; + } + } + + *out = '\0'; + + if (error) + *error = error_; + + return outbuf; +} + +static gchar *conv_jistoutf8(const gchar *inbuf, gint *error) +{ + gchar *eucstr, *utf8str; + gint e_error = 0, u_error = 0; + + eucstr = conv_jistoeuc(inbuf, &e_error); + utf8str = conv_euctoutf8(eucstr, &u_error); + g_free(eucstr); + + if (error) + *error = (e_error | u_error); + + return utf8str; +} + +static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error) +{ + gchar *utf8str; + + utf8str = conv_iconv_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8, error); + if (!utf8str) + utf8str = g_strdup(inbuf); + + return utf8str; +} + +static gchar *conv_euctoutf8(const gchar *inbuf, gint *error) +{ + static iconv_t cd = (iconv_t)-1; + static gboolean iconv_ok = TRUE; + + if (cd == (iconv_t)-1) { + if (!iconv_ok) { + if (error) + *error = -1; + return g_strdup(inbuf); + } + + cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS); + if (cd == (iconv_t)-1) { + cd = iconv_open(CS_UTF_8, CS_EUC_JP); + if (cd == (iconv_t)-1) { + g_warning("conv_euctoutf8(): %s\n", + g_strerror(errno)); + iconv_ok = FALSE; + if (error) + *error = -1; + return g_strdup(inbuf); + } + } + } + + return conv_iconv_strdup_with_cd(inbuf, cd, error); +} + +static gchar *conv_anytoutf8(const gchar *inbuf, gint *error) +{ + switch (conv_guess_ja_encoding(inbuf)) { + case C_ISO_2022_JP: + return conv_jistoutf8(inbuf, error); + case C_SHIFT_JIS: + return conv_sjistoutf8(inbuf, error); + case C_EUC_JP: + return conv_euctoutf8(inbuf, error); + default: + if (error) + *error = 0; + return g_strdup(inbuf); + } +} + +static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error) +{ + static iconv_t cd = (iconv_t)-1; + static gboolean iconv_ok = TRUE; + + if (cd == (iconv_t)-1) { + if (!iconv_ok) { + if (error) + *error = -1; + return g_strdup(inbuf); + } + + cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8); + if (cd == (iconv_t)-1) { + cd = iconv_open(CS_EUC_JP, CS_UTF_8); + if (cd == (iconv_t)-1) { + g_warning("conv_utf8toeuc(): %s\n", + g_strerror(errno)); + iconv_ok = FALSE; + if (error) + *error = -1; + return g_strdup(inbuf); + } + } + } + + return conv_iconv_strdup_with_cd(inbuf, cd, error); +} + +static gchar *conv_utf8tojis(const gchar *inbuf, gint *error) +{ + gchar *eucstr, *jisstr; + gint e_error = 0, j_error = 0; + + eucstr = conv_utf8toeuc(inbuf, &e_error); + jisstr = conv_euctojis(eucstr, &j_error); + g_free(eucstr); + + if (error) + *error = (e_error | j_error); + + return jisstr; +} + +#if 0 +static gchar valid_eucjp_tbl[][96] = { + /* 0xa2a0 - 0xa2ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 }, + + /* 0xa3a0 - 0xa3ff */ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + + /* 0xa4a0 - 0xa4ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + /* 0xa5a0 - 0xa5ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + /* 0xa6a0 - 0xa6ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + /* 0xa7a0 - 0xa7ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + /* 0xa8a0 - 0xa8ff */ + { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static gboolean isprintableeuckanji(guchar c1, guchar c2) +{ + if (c1 <= 0xa0 || c1 >= 0xf5) + return FALSE; + if (c2 <= 0xa0 || c2 == 0xff) + return FALSE; + + if (c1 >= 0xa9 && c1 <= 0xaf) + return FALSE; + + if (c1 >= 0xa2 && c1 <= 0xa8) + return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0]; + + if (c1 == 0xcf) { + if (c2 >= 0xd4 && c2 <= 0xfe) + return FALSE; + } else if (c1 == 0xf4) { + if (c2 >= 0xa7 && c2 <= 0xfe) + return FALSE; + } + + return TRUE; +} + +static void conv_unreadable_eucjp(gchar *str) +{ + register guchar *p = str; + + while (*p != '\0') { + if (isascii(*p)) { + /* convert CR+LF -> LF */ + if (*p == '\r' && *(p + 1) == '\n') + memmove(p, p + 1, strlen(p)); + /* printable 7 bit code */ + p++; + } else if (iseuckanji(*p)) { + if (isprintableeuckanji(*p, *(p + 1))) { + /* printable euc-jp code */ + p += 2; + } else { + /* substitute unprintable code */ + *p++ = SUBST_CHAR; + if (*p != '\0') { + if (isascii(*p)) + p++; + else + *p++ = SUBST_CHAR; + } + } + } else if (iseuchwkana1(*p)) { + if (iseuchwkana2(*(p + 1))) + /* euc-jp hankaku kana */ + p += 2; + else + *p++ = SUBST_CHAR; + } else if (iseucaux(*p)) { + if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) { + /* auxiliary kanji */ + p += 3; + } else + *p++ = SUBST_CHAR; + } else + /* substitute unprintable 1 byte code */ + *p++ = SUBST_CHAR; + } +} +#endif + +static void conv_unreadable_8bit(gchar *str) +{ + register gchar *p = str; + + while (*p != '\0') { + /* convert CR+LF -> LF */ + if (*p == '\r' && *(p + 1) == '\n') + memmove(p, p + 1, strlen(p)); + else if (!isascii(*(guchar *)p)) *p = SUBST_CHAR; + p++; + } +} + +#if 0 +static void conv_unreadable_latin(gchar *str) +{ + register guchar *p = str; + + while (*p != '\0') { + /* convert CR+LF -> LF */ + if (*p == '\r' && *(p + 1) == '\n') + memmove(p, p + 1, strlen(p)); + else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f) + *p = SUBST_CHAR; + p++; + } +} +#endif + +#define NCV '\0' + +void conv_mb_alnum(gchar *str) +{ + static guchar char_tbl[] = { + /* 0xa0 - 0xaf */ + NCV, ' ', NCV, NCV, ',', '.', NCV, ':', + ';', '?', '!', NCV, NCV, NCV, NCV, NCV, + /* 0xb0 - 0xbf */ + NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, + NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, + /* 0xc0 - 0xcf */ + NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, + NCV, NCV, '(', ')', NCV, NCV, '[', ']', + /* 0xd0 - 0xdf */ + '{', '}', NCV, NCV, NCV, NCV, NCV, NCV, + NCV, NCV, NCV, NCV, '+', '-', NCV, NCV, + /* 0xe0 - 0xef */ + NCV, '=', NCV, '<', '>', NCV, NCV, NCV, + NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV + }; + + register guchar *p = (guchar *)str; + register gint len; + + len = strlen(str); + + while (len > 1) { + if (*p == 0xa3) { + register guchar ch = *(p + 1); + + if (ch >= 0xb0 && ch <= 0xfa) { + /* [a-zA-Z] */ + *p = ch & 0x7f; + p++; + len--; + memmove(p, p + 1, len); + len--; + } else { + p += 2; + len -= 2; + } + } else if (*p == 0xa1) { + register guchar ch = *(p + 1); + + if (ch >= 0xa0 && ch <= 0xef && + NCV != char_tbl[ch - 0xa0]) { + *p = char_tbl[ch - 0xa0]; + p++; + len--; + memmove(p, p + 1, len); + len--; + } else { + p += 2; + len -= 2; + } + } else if (iseuckanji(*p)) { + p += 2; + len -= 2; + } else { + p++; + len--; + } + } +} + +CharSet conv_guess_ja_encoding(const gchar *str) +{ + const guchar *p = (const guchar *)str; + CharSet guessed = C_US_ASCII; + + while (*p != '\0') { + if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) { + if (guessed == C_US_ASCII) + return C_ISO_2022_JP; + p += 2; + } else if (isascii(*p)) { + p++; + } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) { + if (*p >= 0xfd && *p <= 0xfe) + return C_EUC_JP; + else if (guessed == C_SHIFT_JIS) { + if ((issjiskanji1(*p) && + issjiskanji2(*(p + 1))) || + issjishwkana(*p)) + guessed = C_SHIFT_JIS; + else + guessed = C_EUC_JP; + } else + guessed = C_EUC_JP; + p += 2; + } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) { + if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1))) + guessed = C_SHIFT_JIS; + else + return C_SHIFT_JIS; + p += 2; + } else if (issjishwkana(*p)) { + guessed = C_SHIFT_JIS; + p++; + } else { + p++; + } + } + + return guessed; +} + +static gchar *conv_jistodisp(const gchar *inbuf, gint *error) +{ + return conv_jistoutf8(inbuf, error); +} + +static gchar *conv_sjistodisp(const gchar *inbuf, gint *error) +{ + return conv_sjistoutf8(inbuf, error); +} + +static gchar *conv_euctodisp(const gchar *inbuf, gint *error) +{ + return conv_euctoutf8(inbuf, error); +} + +gchar *conv_utf8todisp(const gchar *inbuf, gint *error) +{ + if (g_utf8_validate(inbuf, -1, NULL) == TRUE) { + if (error) + *error = 0; + return g_strdup(inbuf); + } else + return conv_ustodisp(inbuf, error); +} + +static gchar *conv_anytodisp(const gchar *inbuf, gint *error) +{ + gchar *outbuf; + + outbuf = conv_anytoutf8(inbuf, error); + if (g_utf8_validate(outbuf, -1, NULL) != TRUE) { + if (error) + *error = -1; + conv_unreadable_8bit(outbuf); + } + + return outbuf; +} + +static gchar *conv_ustodisp(const gchar *inbuf, gint *error) +{ + gchar *outbuf; + + outbuf = g_strdup(inbuf); + conv_unreadable_8bit(outbuf); + if (error) + *error = 0; + + return outbuf; +} + +gchar *conv_localetodisp(const gchar *inbuf, gint *error) +{ + gchar *str; + + str = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(), + CS_INTERNAL, error); + if (!str) + str = conv_utf8todisp(inbuf, NULL); + + return str; +} + +static gchar *conv_noconv(const gchar *inbuf, gint *error) +{ + if (error) + *error = 0; + return g_strdup(inbuf); +} + +static const gchar * +conv_get_fallback_for_private_encoding(const gchar *encoding) +{ + if (encoding && (encoding[0] == 'X' || encoding[0] == 'x') && + encoding[1] == '-') { + if (!g_ascii_strcasecmp(encoding, CS_X_GBK)) + return CS_GBK; + } + + return encoding; +} + +CodeConverter *conv_code_converter_new(const gchar *src_encoding, + const gchar *dest_encoding) +{ + CodeConverter *conv; + + src_encoding = conv_get_fallback_for_private_encoding(src_encoding); + + conv = g_new0(CodeConverter, 1); + conv->code_conv_func = + conv_get_code_conv_func(src_encoding, dest_encoding); + conv->src_encoding = g_strdup(src_encoding); + conv->dest_encoding = g_strdup(dest_encoding); + + return conv; +} + +void conv_code_converter_destroy(CodeConverter *conv) +{ + g_free(conv->src_encoding); + g_free(conv->dest_encoding); + g_free(conv); +} + +gchar *conv_convert(CodeConverter *conv, const gchar *inbuf) +{ + if (conv->code_conv_func != conv_noconv) + return conv->code_conv_func(inbuf, NULL); + else + return conv_iconv_strdup + (inbuf, conv->src_encoding, conv->dest_encoding, NULL); +} + +gchar *conv_codeset_strdup_full(const gchar *inbuf, + const gchar *src_encoding, + const gchar *dest_encoding, + gint *error) +{ + CodeConvFunc conv_func; + + src_encoding = conv_get_fallback_for_private_encoding(src_encoding); + + conv_func = conv_get_code_conv_func(src_encoding, dest_encoding); + if (conv_func != conv_noconv) + return conv_func(inbuf, error); + + return conv_iconv_strdup(inbuf, src_encoding, dest_encoding, error); +} + +CodeConvFunc conv_get_code_conv_func(const gchar *src_encoding, + const gchar *dest_encoding) +{ + CodeConvFunc code_conv = conv_noconv; + CharSet src_charset; + CharSet dest_charset; + + if (!src_encoding) + src_charset = conv_get_locale_charset(); + else + src_charset = conv_get_charset_from_str(src_encoding); + + /* auto detection mode */ + if (!src_encoding && !dest_encoding) { + if (conv_is_ja_locale()) + return conv_anytodisp; + else + return conv_noconv; + } + + dest_charset = conv_get_charset_from_str(dest_encoding); + + if (dest_charset == C_US_ASCII) + return conv_ustodisp; + + switch (src_charset) { + case C_US_ASCII: + case C_ISO_8859_1: + case C_ISO_8859_2: + case C_ISO_8859_3: + case C_ISO_8859_4: + case C_ISO_8859_5: + case C_ISO_8859_6: + case C_ISO_8859_7: + case C_ISO_8859_8: + case C_ISO_8859_9: + case C_ISO_8859_10: + case C_ISO_8859_11: + case C_ISO_8859_13: + case C_ISO_8859_14: + case C_ISO_8859_15: + break; + case C_ISO_2022_JP: + case C_ISO_2022_JP_2: + case C_ISO_2022_JP_3: + if (dest_charset == C_AUTO) + code_conv = conv_jistodisp; + else if (dest_charset == C_EUC_JP) + code_conv = conv_jistoeuc; + else if (dest_charset == C_UTF_8) + code_conv = conv_jistoutf8; + break; + case C_SHIFT_JIS: + if (dest_charset == C_AUTO) + code_conv = conv_sjistodisp; + else if (dest_charset == C_EUC_JP) + code_conv = conv_sjistoeuc; + else if (dest_charset == C_UTF_8) + code_conv = conv_sjistoutf8; + break; + case C_EUC_JP: + if (dest_charset == C_AUTO) + code_conv = conv_euctodisp; + else if (dest_charset == C_ISO_2022_JP || + dest_charset == C_ISO_2022_JP_2 || + dest_charset == C_ISO_2022_JP_3) + code_conv = conv_euctojis; + else if (dest_charset == C_UTF_8) + code_conv = conv_euctoutf8; + break; + case C_UTF_8: + if (dest_charset == C_EUC_JP) + code_conv = conv_utf8toeuc; + else if (dest_charset == C_ISO_2022_JP || + dest_charset == C_ISO_2022_JP_2 || + dest_charset == C_ISO_2022_JP_3) + code_conv = conv_utf8tojis; + break; + default: + break; + } + + return code_conv; +} + +gchar *conv_iconv_strdup(const gchar *inbuf, + const gchar *src_code, const gchar *dest_code, + gint *error) +{ + iconv_t cd; + gchar *outbuf; + + if (!src_code) + src_code = conv_get_locale_charset_str(); + if (!dest_code) + dest_code = CS_INTERNAL; + + cd = iconv_open(dest_code, src_code); + if (cd == (iconv_t)-1) { + if (error) + *error = -1; + return NULL; + } + + outbuf = conv_iconv_strdup_with_cd(inbuf, cd, error); + + iconv_close(cd); + + return outbuf; +} + +gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd, gint *error) +{ + const gchar *inbuf_p; + gchar *outbuf; + gchar *outbuf_p; + size_t in_size; + size_t in_left; + size_t out_size; + size_t out_left; + size_t n_conv; + size_t len; + gint error_ = 0; + + inbuf_p = inbuf; + in_size = strlen(inbuf); + in_left = in_size; + out_size = (in_size + 1) * 2; + outbuf = g_malloc(out_size); + outbuf_p = outbuf; + out_left = out_size; + +#define EXPAND_BUF() \ +{ \ + len = outbuf_p - outbuf; \ + out_size *= 2; \ + outbuf = g_realloc(outbuf, out_size); \ + outbuf_p = outbuf + len; \ + out_left = out_size - len; \ +} + + while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left, + &outbuf_p, &out_left)) == (size_t)-1) { + if (EILSEQ == errno) { + /* g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno)); */ + error_ = -1; + inbuf_p++; + in_left--; + if (out_left == 0) { + EXPAND_BUF(); + } + *outbuf_p++ = SUBST_CHAR; + out_left--; + } else if (EINVAL == errno) { + error_ = -1; + break; + } else if (E2BIG == errno) { + EXPAND_BUF(); + } else { + g_warning("conv_iconv_strdup(): %s\n", + g_strerror(errno)); + error_ = -1; + break; + } + } + + while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) == + (size_t)-1) { + if (E2BIG == errno) { + EXPAND_BUF(); + } else { + g_warning("conv_iconv_strdup(): %s\n", + g_strerror(errno)); + error_ = -1; + break; + } + } + +#undef EXPAND_BUF + + len = outbuf_p - outbuf; + outbuf = g_realloc(outbuf, len + 1); + outbuf[len] = '\0'; + + if (error) + *error = error_; + + return outbuf; +} + +static const struct { + CharSet charset; + gchar *const name; +} charsets[] = { + {C_US_ASCII, CS_US_ASCII}, + {C_US_ASCII, CS_ANSI_X3_4_1968}, + {C_UTF_8, CS_UTF_8}, + {C_UTF_7, CS_UTF_7}, + {C_ISO_8859_1, CS_ISO_8859_1}, + {C_ISO_8859_2, CS_ISO_8859_2}, + {C_ISO_8859_3, CS_ISO_8859_3}, + {C_ISO_8859_4, CS_ISO_8859_4}, + {C_ISO_8859_5, CS_ISO_8859_5}, + {C_ISO_8859_6, CS_ISO_8859_6}, + {C_ISO_8859_7, CS_ISO_8859_7}, + {C_ISO_8859_8, CS_ISO_8859_8}, + {C_ISO_8859_9, CS_ISO_8859_9}, + {C_ISO_8859_10, CS_ISO_8859_10}, + {C_ISO_8859_11, CS_ISO_8859_11}, + {C_ISO_8859_13, CS_ISO_8859_13}, + {C_ISO_8859_14, CS_ISO_8859_14}, + {C_ISO_8859_15, CS_ISO_8859_15}, + {C_BALTIC, CS_BALTIC}, + {C_CP1250, CS_CP1250}, + {C_CP1251, CS_CP1251}, + {C_CP1252, CS_CP1252}, + {C_CP1253, CS_CP1253}, + {C_CP1254, CS_CP1254}, + {C_CP1255, CS_CP1255}, + {C_CP1256, CS_CP1256}, + {C_CP1257, CS_CP1257}, + {C_CP1258, CS_CP1258}, + {C_WINDOWS_1250, CS_WINDOWS_1250}, + {C_WINDOWS_1251, CS_WINDOWS_1251}, + {C_WINDOWS_1252, CS_WINDOWS_1252}, + {C_WINDOWS_1253, CS_WINDOWS_1253}, + {C_WINDOWS_1254, CS_WINDOWS_1254}, + {C_WINDOWS_1255, CS_WINDOWS_1255}, + {C_WINDOWS_1256, CS_WINDOWS_1256}, + {C_WINDOWS_1257, CS_WINDOWS_1257}, + {C_WINDOWS_1258, CS_WINDOWS_1258}, + {C_KOI8_R, CS_KOI8_R}, + {C_KOI8_T, CS_KOI8_T}, + {C_KOI8_U, CS_KOI8_U}, + {C_ISO_2022_JP, CS_ISO_2022_JP}, + {C_ISO_2022_JP_2, CS_ISO_2022_JP_2}, + {C_ISO_2022_JP_3, CS_ISO_2022_JP_3}, + {C_EUC_JP, CS_EUC_JP}, + {C_EUC_JP, CS_EUCJP}, + {C_EUC_JP_MS, CS_EUC_JP_MS}, + {C_SHIFT_JIS, CS_SHIFT_JIS}, + {C_SHIFT_JIS, CS_SHIFT__JIS}, + {C_SHIFT_JIS, CS_SJIS}, + {C_ISO_2022_KR, CS_ISO_2022_KR}, + {C_EUC_KR, CS_EUC_KR}, + {C_ISO_2022_CN, CS_ISO_2022_CN}, + {C_EUC_CN, CS_EUC_CN}, + {C_GB2312, CS_GB2312}, + {C_GBK, CS_GBK}, + {C_EUC_TW, CS_EUC_TW}, + {C_BIG5, CS_BIG5}, + {C_BIG5_HKSCS, CS_BIG5_HKSCS}, + {C_TIS_620, CS_TIS_620}, + {C_WINDOWS_874, CS_WINDOWS_874}, + {C_GEORGIAN_PS, CS_GEORGIAN_PS}, + {C_TCVN5712_1, CS_TCVN5712_1}, +}; + +static const struct { + gchar *const locale; + CharSet charset; + CharSet out_charset; +} locale_table[] = { + {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP}, + {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP}, + {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP}, + {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP}, + {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP}, + {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP}, +#ifdef G_OS_WIN32 + {"ja_JP" , C_SHIFT_JIS , C_ISO_2022_JP}, +#else + {"ja_JP" , C_EUC_JP , C_ISO_2022_JP}, +#endif + {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR}, + {"ko_KR" , C_EUC_KR , C_EUC_KR}, + {"zh_CN.GB2312" , C_GB2312 , C_GB2312}, + {"zh_CN.GBK" , C_GBK , C_GBK}, + {"zh_CN" , C_GB2312 , C_GB2312}, + {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS}, + {"zh_TW.eucTW" , C_EUC_TW , C_BIG5}, + {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5}, + {"zh_TW.Big5" , C_BIG5 , C_BIG5}, + {"zh_TW" , C_BIG5 , C_BIG5}, + + {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R}, + {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R}, + {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R}, + {"ru_RU" , C_ISO_8859_5 , C_KOI8_R}, + {"tg_TJ" , C_KOI8_T , C_KOI8_T}, + {"ru_UA" , C_KOI8_U , C_KOI8_U}, + {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U}, + {"uk_UA" , C_KOI8_U , C_KOI8_U}, + + {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251}, + {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251}, + + {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255}, + + {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, + {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1}, + {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1}, + {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1}, + {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1}, + {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_US" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, + {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_US" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1}, + {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1}, + {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1}, + {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1}, + {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1}, + {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1}, + {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1}, + {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1}, + {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1}, + {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1}, + {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1}, + {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1}, + {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1}, + {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1}, + {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1}, + {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1}, + {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1}, + {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1}, + {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1}, + {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1}, + {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1}, + + {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2}, + {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2}, + {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2}, + {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2}, + {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2}, + {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2}, + {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2}, + {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2}, + + {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5}, + {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2}, + + {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3}, + + {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4}, + {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, + {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, + {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13}, + + {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5}, + + {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6}, + {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6}, + + {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7}, + {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8}, + {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8}, + {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9}, + + {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13}, + {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13}, + + {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14}, + + {"ar_IN" , C_UTF_8 , C_UTF_8}, + {"en_IN" , C_UTF_8 , C_UTF_8}, + {"se_NO" , C_UTF_8 , C_UTF_8}, + {"ta_IN" , C_UTF_8 , C_UTF_8}, + {"te_IN" , C_UTF_8 , C_UTF_8}, + {"ur_PK" , C_UTF_8 , C_UTF_8}, + + {"th_TH" , C_TIS_620 , C_TIS_620}, + /* {"th_TH" , C_WINDOWS_874}, */ + /* {"th_TH" , C_ISO_8859_11}, */ + + {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS}, + {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1}, + + {"C" , C_US_ASCII , C_US_ASCII}, + {"POSIX" , C_US_ASCII , C_US_ASCII}, + {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII}, +}; + +static GHashTable *conv_get_charset_to_str_table(void) +{ + static GHashTable *table; + gint i; + + if (table) + return table; + + table = g_hash_table_new(NULL, g_direct_equal); + + for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { + if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset)) + == NULL) { + g_hash_table_insert + (table, GUINT_TO_POINTER(charsets[i].charset), + charsets[i].name); + } + } + + return table; +} + +static GHashTable *conv_get_charset_from_str_table(void) +{ + static GHashTable *table; + gint i; + + if (table) + return table; + + table = g_hash_table_new(str_case_hash, str_case_equal); + + for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { + g_hash_table_insert(table, charsets[i].name, + GUINT_TO_POINTER(charsets[i].charset)); + } + + return table; +} + +const gchar *conv_get_charset_str(CharSet charset) +{ + GHashTable *table; + + table = conv_get_charset_to_str_table(); + return g_hash_table_lookup(table, GUINT_TO_POINTER(charset)); +} + +CharSet conv_get_charset_from_str(const gchar *charset) +{ + GHashTable *table; + + if (!charset) return C_AUTO; + + table = conv_get_charset_from_str_table(); + return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset)); +} + +CharSet conv_get_locale_charset(void) +{ + static CharSet cur_charset = -1; + const gchar *cur_locale; + const gchar *p; + gint i; + + if (cur_charset != -1) + return cur_charset; + + cur_locale = conv_get_current_locale(); + if (!cur_locale) { + cur_charset = C_US_ASCII; + return cur_charset; + } + + if (strcasestr(cur_locale, "UTF-8")) { + cur_charset = C_UTF_8; + return cur_charset; + } + + if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { + cur_charset = C_ISO_8859_15; + return cur_charset; + } + + for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { + const gchar *p; + + /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and + "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */ + if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, + strlen(locale_table[i].locale))) { + cur_charset = locale_table[i].charset; + return cur_charset; + } else if ((p = strchr(locale_table[i].locale, '_')) && + !strchr(p + 1, '.')) { + if (strlen(cur_locale) == 2 && + !g_ascii_strncasecmp(cur_locale, + locale_table[i].locale, 2)) { + cur_charset = locale_table[i].charset; + return cur_charset; + } + } + } + + cur_charset = C_AUTO; + return cur_charset; +} + +const gchar *conv_get_locale_charset_str(void) +{ + static const gchar *codeset = NULL; + + if (!codeset) + codeset = conv_get_charset_str(conv_get_locale_charset()); + + return codeset ? codeset : CS_INTERNAL; +} + +CharSet conv_get_internal_charset(void) +{ + return C_INTERNAL; +} + +const gchar *conv_get_internal_charset_str(void) +{ + return CS_INTERNAL; +} + +CharSet conv_get_outgoing_charset(void) +{ + static CharSet out_charset = -1; + const gchar *cur_locale; + const gchar *p; + gint i; + + if (out_charset != -1) + return out_charset; + + cur_locale = conv_get_current_locale(); + if (!cur_locale) { + out_charset = C_AUTO; + return out_charset; + } + + if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { + out_charset = C_ISO_8859_15; + return out_charset; + } + + for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { + const gchar *p; + + if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, + strlen(locale_table[i].locale))) { + out_charset = locale_table[i].out_charset; + break; + } else if ((p = strchr(locale_table[i].locale, '_')) && + !strchr(p + 1, '.')) { + if (strlen(cur_locale) == 2 && + !g_ascii_strncasecmp(cur_locale, + locale_table[i].locale, 2)) { + out_charset = locale_table[i].out_charset; + break; + } + } + } + + return out_charset; +} + +const gchar *conv_get_outgoing_charset_str(void) +{ + CharSet out_charset; + const gchar *str; + + out_charset = conv_get_outgoing_charset(); + str = conv_get_charset_str(out_charset); + + return str ? str : CS_UTF_8; +} + +gboolean conv_is_multibyte_encoding(CharSet encoding) +{ + switch (encoding) { + case C_EUC_JP: + case C_EUC_JP_MS: + case C_EUC_KR: + case C_EUC_TW: + case C_EUC_CN: + case C_ISO_2022_JP: + case C_ISO_2022_JP_2: + case C_ISO_2022_JP_3: + case C_ISO_2022_KR: + case C_ISO_2022_CN: + case C_SHIFT_JIS: + case C_GB2312: + case C_GBK: + case C_BIG5: + case C_UTF_8: + case C_UTF_7: + return TRUE; + default: + return FALSE; + } +} + +const gchar *conv_get_current_locale(void) +{ + static const gchar *cur_locale; + + if (!cur_locale) { +#ifdef G_OS_WIN32 + cur_locale = g_win32_getlocale(); +#else + cur_locale = g_getenv("LC_ALL"); + if (!cur_locale) cur_locale = g_getenv("LC_CTYPE"); + if (!cur_locale) cur_locale = g_getenv("LANG"); + if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL); +#endif /* G_OS_WIN32 */ + + debug_print("current locale: %s\n", + cur_locale ? cur_locale : "(none)"); + } + + return cur_locale; +} + +gboolean conv_is_ja_locale(void) +{ + static gint is_ja_locale = -1; + const gchar *cur_locale; + + if (is_ja_locale != -1) + return is_ja_locale != 0; + + is_ja_locale = 0; + cur_locale = conv_get_current_locale(); + if (cur_locale) { + if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0) + is_ja_locale = 1; + } + + return is_ja_locale != 0; +} + +gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding) +{ + gchar *buf; + gchar *decoded_str; + + if (is_ascii_str(str)) + return unmime_header(str); + + if (default_encoding) { + buf = conv_codeset_strdup + (str, default_encoding, CS_INTERNAL); + if (buf) { + decoded_str = unmime_header(buf); + g_free(buf); + return decoded_str; + } + } + + if (conv_is_ja_locale()) + buf = conv_anytodisp(str, NULL); + else + buf = conv_localetodisp(str, NULL); + + decoded_str = unmime_header(buf); + g_free(buf); + + return decoded_str; +} + +#define MAX_LINELEN 76 +#define MAX_HARD_LINELEN 996 +#define MIMESEP_BEGIN "=?" +#define MIMESEP_END "?=" + +#define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0)) + +#define LBREAK_IF_REQUIRED(cond, is_plain_text) \ +{ \ + if (len - (destp - dest) < MAX_LINELEN + 2) { \ + *destp = '\0'; \ + return; \ + } \ + \ + if ((cond) && *srcp) { \ + if (destp > dest && left < MAX_LINELEN - 1) { \ + if (g_ascii_isspace(*(destp - 1))) \ + destp--; \ + else if (is_plain_text && \ + g_ascii_isspace(*srcp)) \ + srcp++; \ + if (*srcp) { \ + *destp++ = '\n'; \ + *destp++ = ' '; \ + left = MAX_LINELEN - 1; \ + } \ + } \ + } \ +} + +void conv_encode_header(gchar *dest, gint len, const gchar *src, + gint header_len, gboolean addr_field, + const gchar *out_encoding) +{ + const gchar *cur_encoding; + gint mimestr_len; + gchar *mimesep_enc; + gint left; + const gchar *srcp = src; + gchar *destp = dest; + gboolean use_base64; + + g_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE); + + if (MB_CUR_MAX > 1) { + use_base64 = TRUE; + mimesep_enc = "?B?"; + } else { + use_base64 = FALSE; + mimesep_enc = "?Q?"; + } + + cur_encoding = CS_INTERNAL; + if (!out_encoding) + out_encoding = conv_get_outgoing_charset_str(); + if (!strcmp(out_encoding, CS_US_ASCII)) + out_encoding = CS_ISO_8859_1; + + mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) + + strlen(mimesep_enc) + strlen(MIMESEP_END); + + left = MAX_LINELEN - header_len; + + while (*srcp) { + LBREAK_IF_REQUIRED(left <= 0, TRUE); + + while (g_ascii_isspace(*srcp)) { + *destp++ = *srcp++; + left--; + LBREAK_IF_REQUIRED(left <= 0, TRUE); + } + + /* output as it is if the next word is ASCII string */ + if (!is_next_nonascii(srcp)) { + gint word_len; + + word_len = get_next_word_len(srcp); + LBREAK_IF_REQUIRED(left < word_len, TRUE); + while (word_len > 0) { + LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE) + *destp++ = *srcp++; + left--; + word_len--; + } + + continue; + } + + /* don't include parentheses in encoded strings */ + if (addr_field && (*srcp == '(' || *srcp == ')')) { + LBREAK_IF_REQUIRED(left < 2, FALSE); + *destp++ = *srcp++; + left--; + } + + while (1) { + gint mb_len = 0; + gint cur_len = 0; + gchar *part_str; + gchar *out_str; + gchar *enc_str; + const gchar *p = srcp; + gint out_str_len; + gint out_enc_str_len; + gint mime_block_len; + gboolean cont = FALSE; + + while (*p != '\0') { + if (g_ascii_isspace(*p) && + !is_next_nonascii(p + 1)) + break; + /* don't include parentheses in encoded + strings */ + if (addr_field && (*p == '(' || *p == ')')) + break; + + mb_len = g_utf8_skip[*(guchar *)p]; + + Xstrndup_a(part_str, srcp, cur_len + mb_len, ); + out_str = conv_codeset_strdup + (part_str, cur_encoding, out_encoding); + if (!out_str) { + g_warning("conv_encode_header(): code conversion failed\n"); + conv_unreadable_8bit(part_str); + out_str = g_strdup(part_str); + } + out_str_len = strlen(out_str); + + if (use_base64) + out_enc_str_len = B64LEN(out_str_len); + else + out_enc_str_len = + qp_get_q_encoding_len + ((guchar *)out_str); + + g_free(out_str); + + if (mimestr_len + out_enc_str_len <= left) { + cur_len += mb_len; + p += mb_len; + } else if (cur_len == 0) { + LBREAK_IF_REQUIRED(1, FALSE); + continue; + } else { + cont = TRUE; + break; + } + } + + if (cur_len > 0) { + Xstrndup_a(part_str, srcp, cur_len, ); + out_str = conv_codeset_strdup + (part_str, cur_encoding, out_encoding); + if (!out_str) { + g_warning("conv_encode_header(): code conversion failed\n"); + conv_unreadable_8bit(part_str); + out_str = g_strdup(part_str); + } + out_str_len = strlen(out_str); + + if (use_base64) + out_enc_str_len = B64LEN(out_str_len); + else + out_enc_str_len = + qp_get_q_encoding_len + ((guchar *)out_str); + + Xalloca(enc_str, out_enc_str_len + 1, ); + if (use_base64) + base64_encode(enc_str, + (guchar *)out_str, + out_str_len); + else + qp_q_encode(enc_str, (guchar *)out_str); + + g_free(out_str); + + /* output MIME-encoded string block */ + mime_block_len = mimestr_len + strlen(enc_str); + g_snprintf(destp, mime_block_len + 1, + MIMESEP_BEGIN "%s%s%s" MIMESEP_END, + out_encoding, mimesep_enc, enc_str); + destp += mime_block_len; + srcp += cur_len; + + left -= mime_block_len; + } + + LBREAK_IF_REQUIRED(cont, FALSE); + + if (cur_len == 0) + break; + } + } + + *destp = '\0'; +} + +#undef LBREAK_IF_REQUIRED + +gint conv_copy_file(const gchar *src, const gchar *dest, const gchar *encoding) +{ + FILE *src_fp, *dest_fp; + gchar buf[BUFFSIZE]; + CodeConverter *conv; + gboolean err = FALSE; + + if ((src_fp = g_fopen(src, "rb")) == NULL) { + FILE_OP_ERROR(src, "fopen"); + return -1; + } + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + fclose(src_fp); + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning("can't change file mode\n"); + } + + conv = conv_code_converter_new(encoding, NULL); + + while (fgets(buf, sizeof(buf), src_fp) != NULL) { + gchar *outbuf; + + outbuf = conv_convert(conv, buf); + if (outbuf) { + fputs(outbuf, dest_fp); + g_free(outbuf); + } else + fputs(buf, dest_fp); + } + + conv_code_converter_destroy(conv); + + if (ferror(src_fp)) { + FILE_OP_ERROR(src, "fgets"); + err = TRUE; + } + fclose(src_fp); + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); + err = TRUE; + } + if (err) { + g_unlink(dest); + return -1; + } + + return 0; +} + +gint conv_copy_dir(const gchar *src, const gchar *dest, const gchar *encoding) +{ + GDir *dir; + const gchar *dir_name; + gchar *src_file; + gchar *dest_file; + + if ((dir = g_dir_open(src, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", src); + return -1; + } + + if (make_dir_hier(dest) < 0) { + g_dir_close(dir); + return -1; + } + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); + dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, + NULL); + if (is_file_exist(src_file)) + conv_copy_file(src_file, dest_file, encoding); + g_free(dest_file); + g_free(src_file); + } + + g_dir_close(dir); + + return 0; +} + +gchar *conv_filename_from_utf8(const gchar *utf8_file) +{ + gchar *fs_file; + GError *error = NULL; + + fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error); + if (error) { + g_warning("failed to convert encoding of file name: %s\n", + error->message); + g_error_free(error); + } + if (!fs_file) + fs_file = g_strdup(utf8_file); + + return fs_file; +} + +gchar *conv_filename_to_utf8(const gchar *fs_file) +{ + gchar *utf8_file; + GError *error = NULL; + + utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error); + if (error) { + g_warning("failed to convert encoding of file name: %s\n", + error->message); + g_error_free(error); + } + if (!utf8_file) + utf8_file = g_strdup(fs_file); + + return utf8_file; +} diff --git a/libsylph/codeconv.h b/libsylph/codeconv.h new file mode 100644 index 00000000..833b1402 --- /dev/null +++ b/libsylph/codeconv.h @@ -0,0 +1,239 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __CODECONV_H__ +#define __CODECONV_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +typedef struct _CodeConverter CodeConverter; + +typedef enum +{ + C_AUTO, + C_US_ASCII, + C_UTF_8, + C_UTF_7, + C_ISO_8859_1, + C_ISO_8859_2, + C_ISO_8859_3, + C_ISO_8859_4, + C_ISO_8859_5, + C_ISO_8859_6, + C_ISO_8859_7, + C_ISO_8859_8, + C_ISO_8859_9, + C_ISO_8859_10, + C_ISO_8859_11, + C_ISO_8859_13, + C_ISO_8859_14, + C_ISO_8859_15, + C_BALTIC, + C_CP1250, + C_CP1251, + C_CP1252, + C_CP1253, + C_CP1254, + C_CP1255, + C_CP1256, + C_CP1257, + C_CP1258, + C_WINDOWS_1250, + C_WINDOWS_1251, + C_WINDOWS_1252, + C_WINDOWS_1253, + C_WINDOWS_1254, + C_WINDOWS_1255, + C_WINDOWS_1256, + C_WINDOWS_1257, + C_WINDOWS_1258, + C_KOI8_R, + C_KOI8_T, + C_KOI8_U, + C_ISO_2022_JP, + C_ISO_2022_JP_2, + C_ISO_2022_JP_3, + C_EUC_JP, + C_EUC_JP_MS, + C_SHIFT_JIS, + C_ISO_2022_KR, + C_EUC_KR, + C_ISO_2022_CN, + C_EUC_CN, + C_GB2312, + C_GBK, + C_EUC_TW, + C_BIG5, + C_BIG5_HKSCS, + C_TIS_620, + C_WINDOWS_874, + C_GEORGIAN_PS, + C_TCVN5712_1 +} CharSet; + +typedef gchar *(*CodeConvFunc) (const gchar *inbuf, gint *error); + +struct _CodeConverter +{ + CodeConvFunc code_conv_func; + gchar *src_encoding; + gchar *dest_encoding; +}; + +#define CS_AUTO "AUTO" +#define CS_US_ASCII "US-ASCII" +#define CS_ANSI_X3_4_1968 "ANSI_X3.4-1968" +#define CS_UTF_8 "UTF-8" +#define CS_UTF_7 "UTF-7" +#define CS_ISO_8859_1 "ISO-8859-1" +#define CS_ISO_8859_2 "ISO-8859-2" +#define CS_ISO_8859_3 "ISO-8859-3" +#define CS_ISO_8859_4 "ISO-8859-4" +#define CS_ISO_8859_5 "ISO-8859-5" +#define CS_ISO_8859_6 "ISO-8859-6" +#define CS_ISO_8859_7 "ISO-8859-7" +#define CS_ISO_8859_8 "ISO-8859-8" +#define CS_ISO_8859_9 "ISO-8859-9" +#define CS_ISO_8859_10 "ISO-8859-10" +#define CS_ISO_8859_11 "ISO-8859-11" +#define CS_ISO_8859_13 "ISO-8859-13" +#define CS_ISO_8859_14 "ISO-8859-14" +#define CS_ISO_8859_15 "ISO-8859-15" +#define CS_BALTIC "BALTIC" +#define CS_CP1250 "CP1250" +#define CS_CP1251 "CP1251" +#define CS_CP1252 "CP1252" +#define CS_CP1253 "CP1253" +#define CS_CP1254 "CP1254" +#define CS_CP1255 "CP1255" +#define CS_CP1256 "CP1256" +#define CS_CP1257 "CP1257" +#define CS_CP1258 "CP1258" +#define CS_WINDOWS_1250 "Windows-1250" +#define CS_WINDOWS_1251 "Windows-1251" +#define CS_WINDOWS_1252 "Windows-1252" +#define CS_WINDOWS_1253 "Windows-1253" +#define CS_WINDOWS_1254 "Windows-1254" +#define CS_WINDOWS_1255 "Windows-1255" +#define CS_WINDOWS_1256 "Windows-1256" +#define CS_WINDOWS_1257 "Windows-1257" +#define CS_WINDOWS_1258 "Windows-1258" +#define CS_KOI8_R "KOI8-R" +#define CS_KOI8_T "KOI8-T" +#define CS_KOI8_U "KOI8-U" +#define CS_ISO_2022_JP "ISO-2022-JP" +#define CS_ISO_2022_JP_2 "ISO-2022-JP-2" +#define CS_ISO_2022_JP_3 "ISO-2022-JP-3" +#define CS_EUC_JP "EUC-JP" +#define CS_EUCJP "EUCJP" +#define CS_EUC_JP_MS "EUC-JP-MS" +#define CS_SHIFT_JIS "Shift_JIS" +#define CS_SHIFT__JIS "SHIFT-JIS" +#define CS_SJIS "SJIS" +#define CS_X_SJIS "X-SJIS" +#define CS_ISO_2022_KR "ISO-2022-KR" +#define CS_EUC_KR "EUC-KR" +#define CS_ISO_2022_CN "ISO-2022-CN" +#define CS_EUC_CN "EUC-CN" +#define CS_GB2312 "GB2312" +#define CS_GBK "GBK" +#define CS_X_GBK "X-GBK" +#define CS_EUC_TW "EUC-TW" +#define CS_BIG5 "Big5" +#define CS_BIG5_HKSCS "BIG5-HKSCS" +#define CS_TIS_620 "TIS-620" +#define CS_WINDOWS_874 "Windows-874" +#define CS_GEORGIAN_PS "GEORGIAN-PS" +#define CS_TCVN5712_1 "TCVN5712-1" + +#define C_INTERNAL C_UTF_8 +#define CS_INTERNAL CS_UTF_8 + +//void conv_mb_alnum(gchar *str); + +CharSet conv_guess_ja_encoding (const gchar *str); + +gchar *conv_utf8todisp (const gchar *inbuf, + gint *error); +gchar *conv_localetodisp (const gchar *inbuf, + gint *error); + +CodeConverter *conv_code_converter_new (const gchar *src_encoding, + const gchar *dest_encoding); +void conv_code_converter_destroy (CodeConverter *conv); +gchar *conv_convert (CodeConverter *conv, + const gchar *inbuf); + +#define conv_codeset_strdup(inbuf, src_code, dest_code) \ + (conv_codeset_strdup_full(inbuf, src_code, dest_code, NULL)) + +gchar *conv_codeset_strdup_full (const gchar *inbuf, + const gchar *src_encoding, + const gchar *dest_encoding, + gint *error); + +CodeConvFunc conv_get_code_conv_func (const gchar *src_encoding, + const gchar *dest_encoding); + +gchar *conv_iconv_strdup (const gchar *inbuf, + const gchar *src_encoding, + const gchar *dest_encoding, + gint *error); +gchar *conv_iconv_strdup_with_cd (const gchar *inbuf, + iconv_t cd, + gint *error); + +const gchar *conv_get_charset_str (CharSet charset); +CharSet conv_get_charset_from_str (const gchar *charset); +CharSet conv_get_locale_charset (void); +const gchar *conv_get_locale_charset_str (void); +CharSet conv_get_internal_charset (void); +const gchar *conv_get_internal_charset_str (void); +CharSet conv_get_outgoing_charset (void); +const gchar *conv_get_outgoing_charset_str (void); +gboolean conv_is_multibyte_encoding (CharSet encoding); + +const gchar *conv_get_current_locale (void); +gboolean conv_is_ja_locale (void); + +gchar *conv_unmime_header (const gchar *str, + const gchar *default_encoding); +void conv_encode_header (gchar *dest, + gint len, + const gchar *src, + gint header_len, + gboolean addr_field, + const gchar *out_encoding); + +gint conv_copy_file (const gchar *src, + const gchar *dest, + const gchar *src_encoding); +gint conv_copy_dir (const gchar *src, + const gchar *dest, + const gchar *src_encoding); + +gchar *conv_filename_from_utf8 (const gchar *utf8_file); +gchar *conv_filename_to_utf8 (const gchar *fs_file); + +#endif /* __CODECONV_H__ */ diff --git a/libsylph/defs.h b/libsylph/defs.h new file mode 100644 index 00000000..9683c28d --- /dev/null +++ b/libsylph/defs.h @@ -0,0 +1,122 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __DEFS_H__ +#define __DEFS_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#ifdef G_OS_WIN32 +# include +#endif + +#if HAVE_PATHS_H +# include +#endif + +#if HAVE_SYS_PARAM_H +# include +#endif + +#define INBOX_DIR "inbox" +#define OUTBOX_DIR "sent" +#define QUEUE_DIR "queue" +#define DRAFT_DIR "draft" +#define TRASH_DIR "trash" +#ifdef G_OS_WIN32 +# define RC_DIR "Sylpheed" +#else +# define RC_DIR ".sylpheed-2.0" +#endif +#define OLD_RC_DIR ".sylpheed" +#define NEWS_CACHE_DIR "newscache" +#define IMAP_CACHE_DIR "imapcache" +#define MIME_TMP_DIR "mimetmp" +#define COMMON_RC "sylpheedrc" +#define ACCOUNT_RC "accountrc" +#define FILTER_RC "filterrc" +#define FILTER_LIST "filter.xml" +#define FILTER_HEADER_RC "filterheaderrc" +#define CUSTOM_HEADER_RC "customheaderrc" +#define DISPLAY_HEADER_RC "dispheaderrc" +#define MENU_RC "menurc" +#define ACTIONS_RC "actionsrc" +#define COMMAND_HISTORY "command_history" +#define TEMPLATE_DIR "templates" +#define TMP_DIR "tmp" +#define UIDL_DIR "uidl" +#define NEWSGROUP_LIST ".newsgroup_list" +#define ADDRESS_BOOK "addressbook.xml" +#define MANUAL_HTML_INDEX "sylpheed.html" +#define FAQ_HTML_INDEX "sylpheed-faq.html" +#define HOMEPAGE_URI "http://sylpheed.good-day.net/" +#define FOLDER_LIST "folderlist.xml" +#define CACHE_FILE ".sylpheed_cache" +#define MARK_FILE ".sylpheed_mark" +#define CACHE_VERSION 0x21 +#define MARK_VERSION 2 + +#ifdef G_OS_WIN32 +# define DEFAULT_SIGNATURE "signature.txt" +#else +# define DEFAULT_SIGNATURE ".signature" +#endif +#define DEFAULT_INC_PATH "/usr/bin/mh/inc" +#define DEFAULT_INC_PROGRAM "inc" +/* #define DEFAULT_INC_PATH "/usr/bin/imget" */ +/* #define DEFAULT_INC_PROGRAM "imget" */ +#define DEFAULT_SENDMAIL_CMD "/usr/sbin/sendmail -t -i" +#define DEFAULT_BROWSER_CMD "mozilla-firefox -remote 'openURL(%s,new-window)'" + +#ifdef _PATH_MAILDIR +# define DEFAULT_SPOOL_PATH _PATH_MAILDIR +#else +# define DEFAULT_SPOOL_PATH "/var/spool/mail" +#endif + +#define BUFFSIZE 8192 + +#ifndef MAXPATHLEN +# define MAXPATHLEN 4095 +#endif + +#define DEFAULT_HEIGHT 460 +#define DEFAULT_FOLDERVIEW_WIDTH 179 +#define DEFAULT_MAINVIEW_WIDTH 600 +#define DEFAULT_SUMMARY_HEIGHT 140 +#define DEFAULT_HEADERVIEW_HEIGHT 40 +#define DEFAULT_COMPOSE_HEIGHT 560 +#define BORDER_WIDTH 2 +#define CTREE_INDENT 18 +#define FOLDER_SPACING 4 +#define MAX_ENTRY_LENGTH 8191 +#define COLOR_DIM 35000 +#define UI_REFRESH_INTERVAL 50000 /* usec */ +#define FOLDER_UPDATE_INTERVAL 1500 /* msec */ +#define PROGRESS_UPDATE_INTERVAL 200 /* msec */ +#define SESSION_TIMEOUT_INTERVAL 60 /* sec */ +#define MAX_HISTORY_SIZE 16 + +#define DEFAULT_MESSAGE_FONT "Monospace 12" + +#endif /* __DEFS_H__ */ diff --git a/libsylph/prefs.c b/libsylph/prefs.c new file mode 100644 index 00000000..4579df4f --- /dev/null +++ b/libsylph/prefs.c @@ -0,0 +1,465 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "codeconv.h" +#include "utils.h" + +typedef enum +{ + DUMMY_PARAM +} DummyEnum; + +void prefs_read_config(PrefParam *param, const gchar *label, + const gchar *rcfile, const gchar *encoding) +{ + FILE *fp; + gchar buf[PREFSBUFSIZE]; + gchar *block_label; + + g_return_if_fail(param != NULL); + g_return_if_fail(label != NULL); + g_return_if_fail(rcfile != NULL); + + debug_print("Reading configuration...\n"); + + prefs_set_default(param); + + if ((fp = g_fopen(rcfile, "rb")) == NULL) { + if (ENOENT != errno) FILE_OP_ERROR(rcfile, "fopen"); + return; + } + + block_label = g_strdup_printf("[%s]", label); + + /* search aiming block */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + gint val; + + if (encoding) { + gchar *conv_str; + + conv_str = conv_codeset_strdup + (buf, encoding, CS_INTERNAL); + if (!conv_str) + conv_str = g_strdup(buf); + val = strncmp + (conv_str, block_label, strlen(block_label)); + g_free(conv_str); + } else + val = strncmp(buf, block_label, strlen(block_label)); + if (val == 0) { + debug_print("Found %s\n", block_label); + break; + } + } + g_free(block_label); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + strretchomp(buf); + /* reached next block */ + if (buf[0] == '[') break; + + if (encoding) { + gchar *conv_str; + + conv_str = conv_codeset_strdup + (buf, encoding, CS_INTERNAL); + if (!conv_str) + conv_str = g_strdup(buf); + prefs_config_parse_one_line(param, conv_str); + g_free(conv_str); + } else + prefs_config_parse_one_line(param, buf); + } + + debug_print("Finished reading configuration.\n"); + fclose(fp); +} + +void prefs_config_parse_one_line(PrefParam *param, const gchar *buf) +{ + gint i; + gint name_len; + const gchar *value; + + for (i = 0; param[i].name != NULL; i++) { + name_len = strlen(param[i].name); + if (g_ascii_strncasecmp(buf, param[i].name, name_len)) + continue; + if (buf[name_len] != '=') + continue; + value = buf + name_len + 1; + /* debug_print("%s = %s\n", param[i].name, value); */ + + switch (param[i].type) { + case P_STRING: + g_free(*((gchar **)param[i].data)); + *((gchar **)param[i].data) = + *value ? g_strdup(value) : NULL; + break; + case P_INT: + *((gint *)param[i].data) = + (gint)atoi(value); + break; + case P_BOOL: + *((gboolean *)param[i].data) = + (*value == '0' || *value == '\0') + ? FALSE : TRUE; + break; + case P_ENUM: + *((DummyEnum *)param[i].data) = + (DummyEnum)atoi(value); + break; + case P_USHORT: + *((gushort *)param[i].data) = + (gushort)atoi(value); + break; + default: + break; + } + } +} + +#define TRY(func) \ +if (!(func)) \ +{ \ + g_warning(_("failed to write configuration to file\n")); \ + if (orig_fp) fclose(orig_fp); \ + prefs_file_close_revert(pfile); \ + g_free(rcpath); \ + g_free(block_label); \ + return; \ +} \ + +void prefs_write_config(PrefParam *param, const gchar *label, + const gchar *rcfile) +{ + FILE *orig_fp; + PrefFile *pfile; + gchar *rcpath; + gchar buf[PREFSBUFSIZE]; + gchar *block_label = NULL; + gboolean block_matched = FALSE; + + g_return_if_fail(param != NULL); + g_return_if_fail(label != NULL); + g_return_if_fail(rcfile != NULL); + + rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL); + if ((orig_fp = g_fopen(rcpath, "rb")) == NULL) { + if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); + } + + if ((pfile = prefs_file_open(rcpath)) == NULL) { + g_warning(_("failed to write configuration to file\n")); + if (orig_fp) fclose(orig_fp); + g_free(rcpath); + return; + } + + block_label = g_strdup_printf("[%s]", label); + + /* search aiming block */ + if (orig_fp) { + while (fgets(buf, sizeof(buf), orig_fp) != NULL) { + gint val; + + val = strncmp(buf, block_label, strlen(block_label)); + if (val == 0) { + debug_print(_("Found %s\n"), block_label); + block_matched = TRUE; + break; + } else + TRY(fputs(buf, pfile->fp) != EOF); + } + } + + TRY(fprintf(pfile->fp, "%s\n", block_label) > 0); + g_free(block_label); + block_label = NULL; + + /* write all param data to file */ + TRY(prefs_file_write_param(pfile, param) == 0); + + if (block_matched) { + while (fgets(buf, sizeof(buf), orig_fp) != NULL) { + /* next block */ + if (buf[0] == '[') { + TRY(fputc('\n', pfile->fp) != EOF && + fputs(buf, pfile->fp) != EOF); + break; + } + } + while (fgets(buf, sizeof(buf), orig_fp) != NULL) + TRY(fputs(buf, pfile->fp) != EOF); + } + + if (orig_fp) fclose(orig_fp); + if (prefs_file_close(pfile) < 0) + g_warning(_("failed to write configuration to file\n")); + g_free(rcpath); + + debug_print(_("Configuration is saved.\n")); +} + +gint prefs_file_write_param(PrefFile *pfile, PrefParam *param) +{ + gint i; + gchar buf[PREFSBUFSIZE]; + + for (i = 0; param[i].name != NULL; i++) { + switch (param[i].type) { + case P_STRING: + g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name, + *((gchar **)param[i].data) ? + *((gchar **)param[i].data) : ""); + break; + case P_INT: + g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, + *((gint *)param[i].data)); + break; + case P_BOOL: + g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, + *((gboolean *)param[i].data)); + break; + case P_ENUM: + g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, + *((DummyEnum *)param[i].data)); + break; + case P_USHORT: + g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, + *((gushort *)param[i].data)); + break; + default: + buf[0] = '\0'; + } + + if (buf[0] != '\0') { + if (fputs(buf, pfile->fp) == EOF) { + perror("fputs"); + return -1; + } + } + } + + return 0; +} + +PrefFile *prefs_file_open(const gchar *path) +{ + PrefFile *pfile; + gchar *tmppath; + FILE *fp; + + g_return_val_if_fail(path != NULL, NULL); + + tmppath = g_strconcat(path, ".tmp", NULL); + if ((fp = g_fopen(tmppath, "wb")) == NULL) { + FILE_OP_ERROR(tmppath, "fopen"); + g_free(tmppath); + return NULL; + } + + if (change_file_mode_rw(fp, tmppath) < 0) + FILE_OP_ERROR(tmppath, "chmod"); + + g_free(tmppath); + + pfile = g_new(PrefFile, 1); + pfile->fp = fp; + pfile->path = g_strdup(path); + + return pfile; +} + +gint prefs_file_close(PrefFile *pfile) +{ + FILE *fp; + gchar *path; + gchar *tmppath; + gchar *bakpath = NULL; + + g_return_val_if_fail(pfile != NULL, -1); + + fp = pfile->fp; + path = pfile->path; + g_free(pfile); + + tmppath = g_strconcat(path, ".tmp", NULL); + if (fclose(fp) == EOF) { + FILE_OP_ERROR(tmppath, "fclose"); + g_unlink(tmppath); + g_free(path); + g_free(tmppath); + return -1; + } + + if (is_file_exist(path)) { + bakpath = g_strconcat(path, ".bak", NULL); + if (rename_force(path, bakpath) < 0) { + FILE_OP_ERROR(path, "rename"); + g_unlink(tmppath); + g_free(path); + g_free(tmppath); + g_free(bakpath); + return -1; + } + } + + if (rename_force(tmppath, path) < 0) { + FILE_OP_ERROR(tmppath, "rename"); + g_unlink(tmppath); + g_free(path); + g_free(tmppath); + g_free(bakpath); + return -1; + } + + g_free(path); + g_free(tmppath); + g_free(bakpath); + return 0; +} + +gint prefs_file_close_revert(PrefFile *pfile) +{ + gchar *tmppath; + + g_return_val_if_fail(pfile != NULL, -1); + + tmppath = g_strconcat(pfile->path, ".tmp", NULL); + fclose(pfile->fp); + if (g_unlink(tmppath) < 0) + FILE_OP_ERROR(tmppath, "unlink"); + g_free(tmppath); + g_free(pfile->path); + g_free(pfile); + + return 0; +} + +void prefs_set_default(PrefParam *param) +{ + gint i; + + g_return_if_fail(param != NULL); + + for (i = 0; param[i].name != NULL; i++) { + if (!param[i].data) continue; + + switch (param[i].type) { + case P_STRING: + if (param[i].defval != NULL) { + if (!g_ascii_strncasecmp(param[i].defval, "ENV_", 4)) { + const gchar *envstr; + gchar *tmp = NULL; + + envstr = g_getenv(param[i].defval + 4); + if (envstr) { + tmp = conv_codeset_strdup + (envstr, + conv_get_locale_charset_str(), + CS_UTF_8); + if (!tmp) { + g_warning("failed to convert character set."); + tmp = g_strdup(envstr); + } + } + *((gchar **)param[i].data) = tmp; + } else if (param[i].defval[0] == '~') + *((gchar **)param[i].data) = + g_strconcat(get_home_dir(), + param[i].defval + 1, + NULL); + else if (param[i].defval[0] != '\0') + *((gchar **)param[i].data) = + g_strdup(param[i].defval); + else + *((gchar **)param[i].data) = NULL; + } else + *((gchar **)param[i].data) = NULL; + break; + case P_INT: + if (param[i].defval != NULL) + *((gint *)param[i].data) = + (gint)atoi(param[i].defval); + else + *((gint *)param[i].data) = 0; + break; + case P_BOOL: + if (param[i].defval != NULL) { + if (!g_ascii_strcasecmp(param[i].defval, "TRUE")) + *((gboolean *)param[i].data) = TRUE; + else + *((gboolean *)param[i].data) = + atoi(param[i].defval) ? TRUE : FALSE; + } else + *((gboolean *)param[i].data) = FALSE; + break; + case P_ENUM: + if (param[i].defval != NULL) + *((DummyEnum*)param[i].data) = + (DummyEnum)atoi(param[i].defval); + else + *((DummyEnum *)param[i].data) = 0; + break; + case P_USHORT: + if (param[i].defval != NULL) + *((gushort *)param[i].data) = + (gushort)atoi(param[i].defval); + else + *((gushort *)param[i].data) = 0; + break; + default: + break; + } + } +} + +void prefs_free(PrefParam *param) +{ + gint i; + + g_return_if_fail(param != NULL); + + for (i = 0; param[i].name != NULL; i++) { + if (!param[i].data) continue; + + switch (param[i].type) { + case P_STRING: + g_free(*((gchar **)param[i].data)); + break; + default: + break; + } + } +} diff --git a/libsylph/prefs.h b/libsylph/prefs.h new file mode 100644 index 00000000..f062d1eb --- /dev/null +++ b/libsylph/prefs.h @@ -0,0 +1,80 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __PREFS_H__ +#define __PREFS_H__ + +#include +#include + +typedef struct _PrefParam PrefParam; +typedef struct _PrefFile PrefFile; + +#define PREFSBUFSIZE 1024 + +#define P_WID(wid) ((gpointer *)(wid)) + +typedef enum +{ + P_STRING, + P_INT, + P_BOOL, + P_ENUM, + P_USHORT, + P_OTHER +} PrefType; + +typedef void (*DataSetFunc) (PrefParam *pparam); +typedef void (*WidgetSetFunc) (PrefParam *pparam); + +struct _PrefParam { + gchar *name; + gchar *defval; + gpointer data; + PrefType type; + gpointer *widget; + DataSetFunc data_set_func; + WidgetSetFunc widget_set_func; +}; + +struct _PrefFile { + FILE *fp; + gchar *path; +}; + +void prefs_read_config (PrefParam *param, + const gchar *label, + const gchar *rcfile, + const gchar *encoding); +void prefs_config_parse_one_line(PrefParam *param, + const gchar *buf); +void prefs_write_config (PrefParam *param, + const gchar *label, + const gchar *rcfile); + +PrefFile *prefs_file_open (const gchar *path); +gint prefs_file_write_param (PrefFile *pfile, + PrefParam *param); +gint prefs_file_close (PrefFile *pfile); +gint prefs_file_close_revert (PrefFile *pfile); + +void prefs_set_default (PrefParam *param); +void prefs_free (PrefParam *param); + +#endif /* __PREFS_H__ */ diff --git a/libsylph/quoted-printable.c b/libsylph/quoted-printable.c new file mode 100644 index 00000000..cea0b704 --- /dev/null +++ b/libsylph/quoted-printable.c @@ -0,0 +1,232 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include + +static gboolean get_hex_value(guchar *out, gchar c1, gchar c2); +static void get_hex_str(gchar *out, guchar ch); + +#define MAX_LINELEN 76 + +#define IS_LBREAK(p) \ + (*(p) == '\0' || *(p) == '\n' || (*(p) == '\r' && *((p) + 1) == '\n')) + +#define SOFT_LBREAK_IF_REQUIRED(n) \ + if (len + (n) > MAX_LINELEN || \ + (len + (n) == MAX_LINELEN && (!IS_LBREAK(inp + 1)))) { \ + *outp++ = '='; \ + *outp++ = '\n'; \ + len = 0; \ + } + +void qp_encode_line(gchar *out, const guchar *in) +{ + const guchar *inp = in; + gchar *outp = out; + guchar ch; + gint len = 0; + + while (*inp != '\0') { + ch = *inp; + + if (IS_LBREAK(inp)) { + *outp++ = '\n'; + len = 0; + if (*inp == '\r') + inp++; + inp++; + } else if (ch == '\t' || ch == ' ') { + if (IS_LBREAK(inp + 1)) { + SOFT_LBREAK_IF_REQUIRED(3); + *outp++ = '='; + get_hex_str(outp, ch); + outp += 2; + len += 3; + inp++; + } else { + SOFT_LBREAK_IF_REQUIRED(1); + *outp++ = *inp++; + len++; + } + } else if ((ch >= 33 && ch <= 60) || (ch >= 62 && ch <= 126)) { + SOFT_LBREAK_IF_REQUIRED(1); + *outp++ = *inp++; + len++; + } else { + SOFT_LBREAK_IF_REQUIRED(3); + *outp++ = '='; + get_hex_str(outp, ch); + outp += 2; + len += 3; + inp++; + } + } + + if (len > 0) + *outp++ = '\n'; + + *outp = '\0'; +} + +gint qp_decode_line(gchar *str) +{ + gchar *inp = str, *outp = str; + + while (*inp != '\0') { + if (*inp == '=') { + if (inp[1] && inp[2] && + get_hex_value((guchar *)outp, inp[1], inp[2]) + == TRUE) { + inp += 3; + } else if (inp[1] == '\0' || g_ascii_isspace(inp[1])) { + /* soft line break */ + break; + } else { + /* broken QP string */ + *outp = *inp++; + } + } else { + *outp = *inp++; + } + outp++; + } + + *outp = '\0'; + + return outp - str; +} + +gint qp_decode_q_encoding(guchar *out, const gchar *in, gint inlen) +{ + const gchar *inp = in; + guchar *outp = out; + + if (inlen < 0) + inlen = G_MAXINT; + + while (inp - in < inlen && *inp != '\0') { + if (*inp == '=' && inp + 3 - in <= inlen) { + if (get_hex_value(outp, inp[1], inp[2]) == TRUE) { + inp += 3; + } else { + *outp = *inp++; + } + } else if (*inp == '_') { + *outp = ' '; + inp++; + } else { + *outp = *inp++; + } + outp++; + } + + *outp = '\0'; + + return outp - out; +} + +gint qp_get_q_encoding_len(const guchar *str) +{ + const guchar *inp = str; + gint len = 0; + + while (*inp != '\0') { + if (*inp == 0x20) + len++; + else if (*inp == '=' || *inp == '?' || *inp == '_' || + *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) + len += 3; + else + len++; + + inp++; + } + + return len; +} + +void qp_q_encode(gchar *out, const guchar *in) +{ + const guchar *inp = in; + gchar *outp = out; + + while (*inp != '\0') { + if (*inp == 0x20) + *outp++ = '_'; + else if (*inp == '=' || *inp == '?' || *inp == '_' || + *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) { + *outp++ = '='; + get_hex_str(outp, *inp); + outp += 2; + } else + *outp++ = *inp; + + inp++; + } + + *outp = '\0'; +} + +#define HEX_TO_INT(val, hex) \ +{ \ + gchar c = hex; \ + \ + if ('0' <= c && c <= '9') { \ + val = c - '0'; \ + } else if ('a' <= c && c <= 'f') { \ + val = c - 'a' + 10; \ + } else if ('A' <= c && c <= 'F') { \ + val = c - 'A' + 10; \ + } else { \ + val = -1; \ + } \ +} + +static gboolean get_hex_value(guchar *out, gchar c1, gchar c2) +{ + gint hi, lo; + + HEX_TO_INT(hi, c1); + HEX_TO_INT(lo, c2); + + if (hi == -1 || lo == -1) + return FALSE; + + *out = (hi << 4) + lo; + return TRUE; +} + +#define INT_TO_HEX(hex, val) \ +{ \ + if ((val) < 10) \ + hex = '0' + (val); \ + else \ + hex = 'A' + (val) - 10; \ +} + +static void get_hex_str(gchar *out, guchar ch) +{ + gchar hex; + + INT_TO_HEX(hex, ch >> 4); + *out++ = hex; + INT_TO_HEX(hex, ch & 0x0f); + *out++ = hex; +} diff --git a/libsylph/quoted-printable.h b/libsylph/quoted-printable.h new file mode 100644 index 00000000..e5abf4f7 --- /dev/null +++ b/libsylph/quoted-printable.h @@ -0,0 +1,36 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2003 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __QUOTED_PRINTABLE_H__ +#define __QUOTED_PRINTABLE_H__ + +#include + +void qp_encode_line (gchar *out, + const guchar *in); +gint qp_decode_line (gchar *str); + +gint qp_decode_q_encoding (guchar *out, + const gchar *in, + gint inlen); +gint qp_get_q_encoding_len (const guchar *str); +void qp_q_encode (gchar *out, + const guchar *in); + +#endif /* __QUOTED_PRINTABLE_H__ */ diff --git a/libsylph/session.c b/libsylph/session.c new file mode 100644 index 00000000..6e7fa4e9 --- /dev/null +++ b/libsylph/session.c @@ -0,0 +1,793 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "session.h" +#include "utils.h" + +static gint session_connect_cb (SockInfo *sock, + gpointer data); +static gint session_close (Session *session); + +static gboolean session_timeout_cb (gpointer data); + +static gboolean session_recv_msg_idle_cb (gpointer data); +static gboolean session_recv_data_idle_cb (gpointer data); + +static gboolean session_read_msg_cb (SockInfo *source, + GIOCondition condition, + gpointer data); +static gboolean session_read_data_cb (SockInfo *source, + GIOCondition condition, + gpointer data); +static gboolean session_write_msg_cb (SockInfo *source, + GIOCondition condition, + gpointer data); +static gboolean session_write_data_cb (SockInfo *source, + GIOCondition condition, + gpointer data); + + +void session_init(Session *session) +{ + session->type = SESSION_UNKNOWN; + session->sock = NULL; + session->server = NULL; + session->port = 0; +#if USE_SSL + session->ssl_type = SSL_NONE; +#endif + session->nonblocking = TRUE; + session->state = SESSION_READY; + session->last_access_time = time(NULL); + + g_get_current_time(&session->tv_prev); + + session->conn_id = 0; + + session->io_tag = 0; + + session->read_buf_p = session->read_buf; + session->read_buf_len = 0; + + session->read_msg_buf = g_string_sized_new(1024); + session->read_data_buf = g_byte_array_new(); + + session->write_buf = NULL; + session->write_buf_p = NULL; + session->write_buf_len = 0; + + session->write_data = NULL; + session->write_data_p = NULL; + session->write_data_len = 0; + + session->timeout_tag = 0; + session->timeout_interval = 0; + + session->data = NULL; +} + +gint session_connect(Session *session, const gchar *server, gushort port) +{ +#ifdef G_OS_UNIX + session->server = g_strdup(server); + session->port = port; + + session->conn_id = sock_connect_async(server, port, session_connect_cb, + session); + if (session->conn_id < 0) { + g_warning("can't connect to server."); + session_close(session); + return -1; + } + + return 0; +#else + SockInfo *sock; + + session->server = g_strdup(server); + session->port = port; + + sock = sock_connect(server, port); + if (sock == NULL) { + g_warning("can't connect to server."); + session_close(session); + return -1; + } + + return session_connect_cb(sock, session); +#endif +} + +static gint session_connect_cb(SockInfo *sock, gpointer data) +{ + Session *session = SESSION(data); + + session->conn_id = 0; + + if (!sock) { + g_warning("can't connect to server."); + session->state = SESSION_ERROR; + return -1; + } + + session->sock = sock; + +#if USE_SSL + if (session->ssl_type == SSL_TUNNEL) { + sock_set_nonblocking_mode(sock, FALSE); + if (!ssl_init_socket(sock)) { + g_warning("can't initialize SSL."); + session->state = SESSION_ERROR; + return -1; + } + } +#endif + + sock_set_nonblocking_mode(sock, session->nonblocking); + + debug_print("session (%p): connected\n", session); + + session->state = SESSION_RECV; + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_msg_cb, + session); + + return 0; +} + +gint session_disconnect(Session *session) +{ + session_close(session); + return 0; +} + +void session_destroy(Session *session) +{ + g_return_if_fail(session != NULL); + g_return_if_fail(session->destroy != NULL); + + session_close(session); + session->destroy(session); + g_free(session->server); + g_string_free(session->read_msg_buf, TRUE); + g_byte_array_free(session->read_data_buf, TRUE); + g_free(session->read_data_terminator); + g_free(session->write_buf); + + debug_print("session (%p): destroyed\n", session); + + g_free(session); +} + +gboolean session_is_connected(Session *session) +{ + return (session->state == SESSION_READY || + session->state == SESSION_SEND || + session->state == SESSION_RECV); +} + +void session_set_access_time(Session *session) +{ + session->last_access_time = time(NULL); +} + +void session_set_timeout(Session *session, guint interval) +{ + if (session->timeout_tag > 0) + g_source_remove(session->timeout_tag); + + session->timeout_interval = interval; + if (interval > 0) + session->timeout_tag = + g_timeout_add(interval, session_timeout_cb, session); + else + session->timeout_tag = 0; +} + +static gboolean session_timeout_cb(gpointer data) +{ + Session *session = SESSION(data); + + g_warning("session timeout.\n"); + + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + session->timeout_tag = 0; + session->state = SESSION_TIMEOUT; + + return FALSE; +} + +void session_set_recv_message_notify(Session *session, + RecvMsgNotify notify_func, gpointer data) +{ + session->recv_msg_notify = notify_func; + session->recv_msg_notify_data = data; +} + +void session_set_recv_data_progressive_notify + (Session *session, + RecvDataProgressiveNotify notify_func, + gpointer data) +{ + session->recv_data_progressive_notify = notify_func, + session->recv_data_progressive_notify_data = data; +} + +void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func, + gpointer data) +{ + session->recv_data_notify = notify_func; + session->recv_data_notify_data = data; +} + +void session_set_send_data_progressive_notify + (Session *session, + SendDataProgressiveNotify notify_func, + gpointer data) +{ + session->send_data_progressive_notify = notify_func; + session->send_data_progressive_notify_data = data; +} + +void session_set_send_data_notify(Session *session, SendDataNotify notify_func, + gpointer data) +{ + session->send_data_notify = notify_func; + session->send_data_notify_data = data; +} + +static gint session_close(Session *session) +{ + g_return_val_if_fail(session != NULL, -1); + +#ifdef G_OS_UNIX + if (session->conn_id > 0) { + sock_connect_async_cancel(session->conn_id); + session->conn_id = 0; + debug_print("session (%p): connection cancelled\n", session); + } +#endif + + session_set_timeout(session, 0); + + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + if (session->sock) { + sock_close(session->sock); + session->sock = NULL; + session->state = SESSION_DISCONNECTED; + debug_print("session (%p): closed\n", session); + } + + return 0; +} + +#if USE_SSL +gint session_start_tls(Session *session) +{ + gboolean nb_mode; + + nb_mode = sock_is_nonblocking_mode(session->sock); + + if (nb_mode) + sock_set_nonblocking_mode(session->sock, FALSE); + + if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) { + g_warning("can't start TLS session.\n"); + if (nb_mode) + sock_set_nonblocking_mode(session->sock, TRUE); + return -1; + } + + if (nb_mode) + sock_set_nonblocking_mode(session->sock, session->nonblocking); + + return 0; +} +#endif + +gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg) +{ + gboolean ret; + + g_return_val_if_fail(session->write_buf == NULL, -1); + g_return_val_if_fail(msg != NULL, -1); + g_return_val_if_fail(msg[0] != '\0', -1); + + session->state = SESSION_SEND; + session->write_buf = g_strconcat(msg, "\r\n", NULL); + session->write_buf_p = session->write_buf; + session->write_buf_len = strlen(msg) + 2; + + ret = session_write_msg_cb(session->sock, G_IO_OUT, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_OUT, + session_write_msg_cb, session); + else if (session->state == SESSION_ERROR) + return -1; + + return 0; +} + +gint session_recv_msg(Session *session) +{ + g_return_val_if_fail(session->read_msg_buf->len == 0, -1); + + session->state = SESSION_RECV; + + if (session->read_buf_len > 0) + g_idle_add(session_recv_msg_idle_cb, session); + else + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_msg_cb, session); + + return 0; +} + +static gboolean session_recv_msg_idle_cb(gpointer data) +{ + Session *session = SESSION(data); + gboolean ret; + + ret = session_read_msg_cb(session->sock, G_IO_IN, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_msg_cb, session); + + return FALSE; +} + +gint session_send_data(Session *session, const guchar *data, guint size) +{ + gboolean ret; + + g_return_val_if_fail(session->write_data == NULL, -1); + g_return_val_if_fail(data != NULL, -1); + g_return_val_if_fail(size != 0, -1); + + session->state = SESSION_SEND; + + session->write_data = data; + session->write_data_p = session->write_data; + session->write_data_len = size; + g_get_current_time(&session->tv_prev); + + ret = session_write_data_cb(session->sock, G_IO_OUT, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_OUT, + session_write_data_cb, + session); + else if (session->state == SESSION_ERROR) + return -1; + + return 0; +} + +gint session_recv_data(Session *session, guint size, const gchar *terminator) +{ + g_return_val_if_fail(session->read_data_buf->len == 0, -1); + + session->state = SESSION_RECV; + + g_free(session->read_data_terminator); + session->read_data_terminator = g_strdup(terminator); + g_get_current_time(&session->tv_prev); + + if (session->read_buf_len > 0) + g_idle_add(session_recv_data_idle_cb, session); + else + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_data_cb, session); + + return 0; +} + +static gboolean session_recv_data_idle_cb(gpointer data) +{ + Session *session = SESSION(data); + gboolean ret; + + ret = session_read_data_cb(session->sock, G_IO_IN, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_data_cb, session); + + return FALSE; +} + +static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, + gpointer data) +{ + Session *session = SESSION(data); + gchar buf[SESSION_BUFFSIZE]; + gint line_len; + gchar *newline; + gchar *msg; + gint ret; + + g_return_val_if_fail(condition == G_IO_IN, FALSE); + + session_set_timeout(session, session->timeout_interval); + + if (session->read_buf_len == 0) { + gint read_len; + + read_len = sock_read(session->sock, session->read_buf, + SESSION_BUFFSIZE - 1); + + if (read_len == 0) { + g_warning("sock_read: received EOF\n"); + session->state = SESSION_EOF; + return FALSE; + } + + if (read_len < 0) { + switch (errno) { + case EAGAIN: + return TRUE; + default: + g_warning("sock_read: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return FALSE; + } + } + + session->read_buf_len = read_len; + } + + if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len)) + != NULL) + line_len = newline - session->read_buf_p + 1; + else + line_len = session->read_buf_len; + + if (line_len == 0) + return TRUE; + + memcpy(buf, session->read_buf_p, line_len); + buf[line_len] = '\0'; + + g_string_append(session->read_msg_buf, buf); + + session->read_buf_len -= line_len; + if (session->read_buf_len == 0) + session->read_buf_p = session->read_buf; + else + session->read_buf_p += line_len; + + /* incomplete read */ + if (buf[line_len - 1] != '\n') + return TRUE; + + /* complete */ + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + /* callback */ + msg = g_strdup(session->read_msg_buf->str); + strretchomp(msg); + g_string_truncate(session->read_msg_buf, 0); + + ret = session->recv_msg(session, msg); + session->recv_msg_notify(session, msg, session->recv_msg_notify_data); + + g_free(msg); + + if (ret < 0) + session->state = SESSION_ERROR; + + return FALSE; +} + +static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, + gpointer data) +{ + Session *session = SESSION(data); + GByteArray *data_buf; + gint terminator_len; + gboolean complete = FALSE; + guint data_len; + gint ret; + + g_return_val_if_fail(condition == G_IO_IN, FALSE); + + session_set_timeout(session, session->timeout_interval); + + if (session->read_buf_len == 0) { + gint read_len; + + read_len = sock_read(session->sock, session->read_buf, + SESSION_BUFFSIZE); + + if (read_len == 0) { + g_warning("sock_read: received EOF\n"); + session->state = SESSION_EOF; + return FALSE; + } + + if (read_len < 0) { + switch (errno) { + case EAGAIN: + return TRUE; + default: + g_warning("sock_read: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return FALSE; + } + } + + session->read_buf_len = read_len; + } + + data_buf = session->read_data_buf; + terminator_len = strlen(session->read_data_terminator); + + if (session->read_buf_len == 0) + return TRUE; + + g_byte_array_append(data_buf, session->read_buf_p, + session->read_buf_len); + + session->read_buf_len = 0; + session->read_buf_p = session->read_buf; + + /* check if data is terminated */ + if (data_buf->len >= terminator_len) { + if (memcmp(data_buf->data, session->read_data_terminator, + terminator_len) == 0) + complete = TRUE; + else if (data_buf->len >= terminator_len + 2 && + memcmp(data_buf->data + data_buf->len - + (terminator_len + 2), "\r\n", 2) == 0 && + memcmp(data_buf->data + data_buf->len - + terminator_len, session->read_data_terminator, + terminator_len) == 0) + complete = TRUE; + } + + /* incomplete read */ + if (!complete) { + GTimeVal tv_cur; + + g_get_current_time(&tv_cur); + if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || + tv_cur.tv_usec - session->tv_prev.tv_usec > + UI_REFRESH_INTERVAL) { + session->recv_data_progressive_notify + (session, data_buf->len, 0, + session->recv_data_progressive_notify_data); + g_get_current_time(&session->tv_prev); + } + return TRUE; + } + + /* complete */ + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + data_len = data_buf->len - terminator_len; + + /* callback */ + ret = session->recv_data_finished(session, (gchar *)data_buf->data, + data_len); + + g_byte_array_set_size(data_buf, 0); + + session->recv_data_notify(session, data_len, + session->recv_data_notify_data); + + if (ret < 0) + session->state = SESSION_ERROR; + + return FALSE; +} + +static gint session_write_buf(Session *session) +{ + gint write_len; + gint to_write_len; + + g_return_val_if_fail(session->write_buf != NULL, -1); + g_return_val_if_fail(session->write_buf_p != NULL, -1); + g_return_val_if_fail(session->write_buf_len > 0, -1); + + to_write_len = session->write_buf_len - + (session->write_buf_p - session->write_buf); + to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); + + write_len = sock_write(session->sock, session->write_buf_p, + to_write_len); + + if (write_len < 0) { + switch (errno) { + case EAGAIN: + write_len = 0; + break; + default: + g_warning("sock_write: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return -1; + } + } + + /* incomplete write */ + if (session->write_buf_p - session->write_buf + write_len < + session->write_buf_len) { + session->write_buf_p += write_len; + return 1; + } + + g_free(session->write_buf); + session->write_buf = NULL; + session->write_buf_p = NULL; + session->write_buf_len = 0; + + return 0; +} + +static gint session_write_data(Session *session) +{ + gint write_len; + gint to_write_len; + + g_return_val_if_fail(session->write_data != NULL, -1); + g_return_val_if_fail(session->write_data_p != NULL, -1); + g_return_val_if_fail(session->write_data_len > 0, -1); + + to_write_len = session->write_data_len - + (session->write_data_p - session->write_data); + to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); + + write_len = sock_write(session->sock, session->write_data_p, + to_write_len); + + if (write_len < 0) { + switch (errno) { + case EAGAIN: + write_len = 0; + break; + default: + g_warning("sock_write: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return -1; + } + } + + /* incomplete write */ + if (session->write_data_p - session->write_data + write_len < + session->write_data_len) { + session->write_data_p += write_len; + return 1; + } + + session->write_data = NULL; + session->write_data_p = NULL; + session->write_data_len = 0; + + return 0; +} + +static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, + gpointer data) +{ + Session *session = SESSION(data); + gint ret; + + g_return_val_if_fail(condition == G_IO_OUT, FALSE); + g_return_val_if_fail(session->write_buf != NULL, FALSE); + g_return_val_if_fail(session->write_buf_p != NULL, FALSE); + g_return_val_if_fail(session->write_buf_len > 0, FALSE); + + ret = session_write_buf(session); + + if (ret < 0) { + session->state = SESSION_ERROR; + return FALSE; + } else if (ret > 0) + return TRUE; + + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + session_recv_msg(session); + + return FALSE; +} + +static gboolean session_write_data_cb(SockInfo *source, + GIOCondition condition, gpointer data) +{ + Session *session = SESSION(data); + guint write_data_len; + gint ret; + + g_return_val_if_fail(condition == G_IO_OUT, FALSE); + g_return_val_if_fail(session->write_data != NULL, FALSE); + g_return_val_if_fail(session->write_data_p != NULL, FALSE); + g_return_val_if_fail(session->write_data_len > 0, FALSE); + + write_data_len = session->write_data_len; + + ret = session_write_data(session); + + if (ret < 0) { + session->state = SESSION_ERROR; + return FALSE; + } else if (ret > 0) { + GTimeVal tv_cur; + + g_get_current_time(&tv_cur); + if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || + tv_cur.tv_usec - session->tv_prev.tv_usec > + UI_REFRESH_INTERVAL) { + session_set_timeout(session, session->timeout_interval); + session->send_data_progressive_notify + (session, + session->write_data_p - session->write_data, + write_data_len, + session->send_data_progressive_notify_data); + g_get_current_time(&session->tv_prev); + } + return TRUE; + } + + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + /* callback */ + ret = session->send_data_finished(session, write_data_len); + session->send_data_notify(session, write_data_len, + session->send_data_notify_data); + + return FALSE; +} diff --git a/libsylph/session.h b/libsylph/session.h new file mode 100644 index 00000000..9ed0eaf4 --- /dev/null +++ b/libsylph/session.h @@ -0,0 +1,205 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include +#include + +#include "socket.h" + +#define SESSION_BUFFSIZE 4096 + +typedef struct _Session Session; + +#define SESSION(obj) ((Session *)obj) + +typedef enum { + SESSION_UNKNOWN, + SESSION_IMAP, + SESSION_NEWS, + SESSION_SMTP, + SESSION_POP3 +} SessionType; + +typedef enum { + SESSION_READY, + SESSION_SEND, + SESSION_RECV, + SESSION_EOF, + SESSION_TIMEOUT, + SESSION_ERROR, + SESSION_DISCONNECTED +} SessionState; + +typedef enum +{ + SESSION_MSG_NORMAL, + SESSION_MSG_SEND_DATA, + SESSION_MSG_RECV_DATA, + SESSION_MSG_CONTROL, + SESSION_MSG_ERROR, + SESSION_MSG_UNKNOWN +} SessionMsgType; + +typedef gint (*RecvMsgNotify) (Session *session, + const gchar *msg, + gpointer user_data); +typedef gint (*RecvDataProgressiveNotify) (Session *session, + guint cur_len, + guint total_len, + gpointer user_data); +typedef gint (*RecvDataNotify) (Session *session, + guint len, + gpointer user_data); +typedef gint (*SendDataProgressiveNotify) (Session *session, + guint cur_len, + guint total_len, + gpointer user_data); +typedef gint (*SendDataNotify) (Session *session, + guint len, + gpointer user_data); + +struct _Session +{ + SessionType type; + + SockInfo *sock; + + gchar *server; + gushort port; + +#if USE_SSL + SSLType ssl_type; +#endif + + gboolean nonblocking; + + SessionState state; + + time_t last_access_time; + GTimeVal tv_prev; + + gint conn_id; + + gint io_tag; + + gchar read_buf[SESSION_BUFFSIZE]; + gchar *read_buf_p; + gint read_buf_len; + + GString *read_msg_buf; + GByteArray *read_data_buf; + gchar *read_data_terminator; + + /* buffer for short messages */ + gchar *write_buf; + gchar *write_buf_p; + gint write_buf_len; + + /* buffer for large data */ + const guchar *write_data; + const guchar *write_data_p; + gint write_data_len; + + guint timeout_tag; + guint timeout_interval; + + gpointer data; + + /* virtual methods to parse server responses */ + gint (*recv_msg) (Session *session, + const gchar *msg); + + gint (*send_data_finished) (Session *session, + guint len); + gint (*recv_data_finished) (Session *session, + guchar *data, + guint len); + + void (*destroy) (Session *session); + + /* notification functions */ + RecvMsgNotify recv_msg_notify; + RecvDataProgressiveNotify recv_data_progressive_notify; + RecvDataNotify recv_data_notify; + SendDataProgressiveNotify send_data_progressive_notify; + SendDataNotify send_data_notify; + + gpointer recv_msg_notify_data; + gpointer recv_data_progressive_notify_data; + gpointer recv_data_notify_data; + gpointer send_data_progressive_notify_data; + gpointer send_data_notify_data; +}; + +void session_init (Session *session); +gint session_connect (Session *session, + const gchar *server, + gushort port); +gint session_disconnect (Session *session); +void session_destroy (Session *session); +gboolean session_is_connected (Session *session); + +void session_set_access_time (Session *session); + +void session_set_timeout (Session *session, + guint interval); + +void session_set_recv_message_notify (Session *session, + RecvMsgNotify notify_func, + gpointer data); +void session_set_recv_data_progressive_notify + (Session *session, + RecvDataProgressiveNotify notify_func, + gpointer data); +void session_set_recv_data_notify (Session *session, + RecvDataNotify notify_func, + gpointer data); +void session_set_send_data_progressive_notify + (Session *session, + SendDataProgressiveNotify notify_func, + gpointer data); +void session_set_send_data_notify (Session *session, + SendDataNotify notify_func, + gpointer data); + +#if USE_SSL +gint session_start_tls (Session *session); +#endif + +gint session_send_msg (Session *session, + SessionMsgType type, + const gchar *msg); +gint session_recv_msg (Session *session); +gint session_send_data (Session *session, + const guchar *data, + guint size); +gint session_recv_data (Session *session, + guint size, + const gchar *terminator); + +#endif /* __SESSION_H__ */ diff --git a/libsylph/socket.c b/libsylph/socket.c new file mode 100644 index 00000000..17063ab1 --- /dev/null +++ b/libsylph/socket.c @@ -0,0 +1,1397 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#ifdef G_OS_WIN32 +# include +#else +# if HAVE_SYS_WAIT_H +# include +# endif +# include +# include +# include +# include +# include +#endif /* G_OS_WIN32 */ +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_SELECT_H +# include +#endif + +#include "socket.h" +#if USE_SSL +# include "ssl.h" +#endif + +#define BUFFSIZE 8192 + +typedef gint (*SockAddrFunc) (GList *addr_list, + gpointer data); + +typedef struct _SockConnectData SockConnectData; +typedef struct _SockLookupData SockLookupData; +typedef struct _SockAddrData SockAddrData; +typedef struct _SockSource SockSource; + +struct _SockConnectData { + gint id; + gchar *hostname; + gushort port; + GList *addr_list; + GList *cur_addr; + SockLookupData *lookup_data; + GIOChannel *channel; + guint io_tag; + SockConnectFunc func; + gpointer data; +}; + +struct _SockLookupData { + gchar *hostname; + pid_t child_pid; + GIOChannel *channel; + guint io_tag; + SockAddrFunc func; + gpointer data; +}; + +struct _SockAddrData { + gint family; + gint socktype; + gint protocol; + gint addr_len; + struct sockaddr *addr; +}; + +struct _SockSource { + GSource parent; + SockInfo *sock; +}; + +static guint io_timeout = 60; + +static GList *sock_connect_data_list = NULL; + +static gboolean sock_prepare (GSource *source, + gint *timeout); +static gboolean sock_check (GSource *source); +static gboolean sock_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); + +GSourceFuncs sock_watch_funcs = { + sock_prepare, + sock_check, + sock_dispatch, + NULL +}; + +static gint sock_connect_with_timeout (gint sock, + const struct sockaddr *serv_addr, + gint addrlen, + guint timeout_secs); + +#ifndef INET6 +static gint sock_connect_by_hostname (gint sock, + const gchar *hostname, + gushort port); +#else +static gint sock_connect_by_getaddrinfo (const gchar *hostname, + gushort port); +#endif + +#ifdef G_OS_UNIX +static void sock_address_list_free (GList *addr_list); + +static gboolean sock_connect_async_cb (GIOChannel *source, + GIOCondition condition, + gpointer data); +static gint sock_connect_async_get_address_info_cb + (GList *addr_list, + gpointer data); + +static gint sock_connect_address_list_async (SockConnectData *conn_data); + +static gboolean sock_get_address_info_async_cb (GIOChannel *source, + GIOCondition condition, + gpointer data); +static SockLookupData *sock_get_address_info_async + (const gchar *hostname, + gushort port, + SockAddrFunc func, + gpointer data); +static gint sock_get_address_info_async_cancel (SockLookupData *lookup_data); +#endif /* G_OS_UNIX */ + + +gint sock_init(void) +{ +#ifdef G_OS_WIN32 + WSADATA wsadata; + gint result; + + result = WSAStartup(MAKEWORD(2, 2), &wsadata); + if (result != NO_ERROR) { + g_warning("WSAStartup() failed\n"); + return -1; + } +#endif + return 0; +} + +gint sock_cleanup(void) +{ +#ifdef G_OS_WIN32 + WSACleanup(); +#endif + return 0; +} + +gint sock_set_io_timeout(guint sec) +{ + io_timeout = sec; + return 0; +} + +gint fd_connect_unix(const gchar *path) +{ +#ifdef G_OS_UNIX + gint sock; + struct sockaddr_un addr; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + perror("sock_connect_unix(): socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + fd_close(sock); + return -1; + } + + return sock; +#else + return -1; +#endif +} + +gint fd_open_unix(const gchar *path) +{ +#ifdef G_OS_UNIX + gint sock; + struct sockaddr_un addr; + + sock = socket(PF_UNIX, SOCK_STREAM, 0); + + if (sock < 0) { + perror("sock_open_unix(): socket"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + fd_close(sock); + return -1; + } + + if (listen(sock, 1) < 0) { + perror("listen"); + fd_close(sock); + return -1; + } + + return sock; +#else + return -1; +#endif +} + +gint fd_accept(gint sock) +{ +#ifdef G_OS_UNIX + struct sockaddr_in caddr; + guint caddr_len; + + caddr_len = sizeof(caddr); + return accept(sock, (struct sockaddr *)&caddr, &caddr_len); +#else + return -1; +#endif +} + + +static gint set_nonblocking_mode(gint fd, gboolean nonblock) +{ +#ifdef G_OS_UNIX + gint flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + perror("fcntl"); + return -1; + } + + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + + return fcntl(fd, F_SETFL, flags); +#else + return -1; +#endif +} + +gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock) +{ + g_return_val_if_fail(sock != NULL, -1); + + return set_nonblocking_mode(sock->sock, nonblock); +} + +static gboolean is_nonblocking_mode(gint fd) +{ +#ifdef G_OS_UNIX + gint flags; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + perror("fcntl"); + return FALSE; + } + + return ((flags & O_NONBLOCK) != 0); +#else + return FALSE; +#endif +} + +gboolean sock_is_nonblocking_mode(SockInfo *sock) +{ + g_return_val_if_fail(sock != NULL, FALSE); + + return is_nonblocking_mode(sock->sock); +} + + +static gboolean sock_prepare(GSource *source, gint *timeout) +{ + *timeout = 1; + return FALSE; +} + +static gboolean sock_check(GSource *source) +{ + SockInfo *sock = ((SockSource *)source)->sock; + struct timeval timeout = {0, 0}; + fd_set fds; + GIOCondition condition = sock->condition; + +#if USE_SSL + if (sock->ssl) { + if (condition & G_IO_IN) { + if (SSL_pending(sock->ssl) > 0) + return TRUE; + if (SSL_want_write(sock->ssl)) + condition |= G_IO_OUT; + } + + if (condition & G_IO_OUT) { + if (SSL_want_read(sock->ssl)) + condition |= G_IO_IN; + } + } +#endif + + FD_ZERO(&fds); + FD_SET(sock->sock, &fds); + + select(sock->sock + 1, + (condition & G_IO_IN) ? &fds : NULL, + (condition & G_IO_OUT) ? &fds : NULL, + NULL, &timeout); + + return FD_ISSET(sock->sock, &fds) != 0; +} + +static gboolean sock_dispatch(GSource *source, GSourceFunc callback, + gpointer user_data) +{ + SockInfo *sock = ((SockSource *)source)->sock; + + return sock->callback(sock, sock->condition, sock->data); +} + +static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + SockInfo *sock = (SockInfo *)data; + + if ((condition & sock->condition) == 0) + return TRUE; + + return sock->callback(sock, sock->condition, sock->data); +} + +guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func, + gpointer data) +{ + sock->callback = func; + sock->condition = condition; + sock->data = data; + +#if USE_SSL + if (sock->ssl) { + GSource *source; + + source = g_source_new(&sock_watch_funcs, sizeof(SockSource)); + ((SockSource *)source)->sock = sock; + g_source_set_priority(source, G_PRIORITY_DEFAULT); + g_source_set_can_recurse(source, FALSE); + return g_source_attach(source, NULL); + } +#endif + + return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock); +} + +static gint fd_check_io(gint fd, GIOCondition cond) +{ + struct timeval timeout; + fd_set fds; + + if (is_nonblocking_mode(fd)) + return 0; + + timeout.tv_sec = io_timeout; + timeout.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + if (cond == G_IO_IN) { + select(fd + 1, &fds, NULL, NULL, + io_timeout > 0 ? &timeout : NULL); + } else { + select(fd + 1, NULL, &fds, NULL, + io_timeout > 0 ? &timeout : NULL); + } + + if (FD_ISSET(fd, &fds)) { + return 0; + } else { + g_warning("Socket IO timeout\n"); + return -1; + } +} + +#ifdef G_OS_UNIX +static sigjmp_buf jmpenv; + +static void timeout_handler(gint sig) +{ + siglongjmp(jmpenv, 1); +} +#endif + +static gint sock_connect_with_timeout(gint sock, + const struct sockaddr *serv_addr, + gint addrlen, + guint timeout_secs) +{ + gint ret; +#ifdef G_OS_UNIX + void (*prev_handler)(gint); + + alarm(0); + prev_handler = signal(SIGALRM, timeout_handler); + if (sigsetjmp(jmpenv, 1)) { + alarm(0); + signal(SIGALRM, prev_handler); + errno = ETIMEDOUT; + return -1; + } + alarm(timeout_secs); +#endif + + ret = connect(sock, serv_addr, addrlen); + +#ifdef G_OS_UNIX + alarm(0); + signal(SIGALRM, prev_handler); +#endif + + return ret; +} + +struct hostent *my_gethostbyname(const gchar *hostname) +{ + struct hostent *hp; +#ifdef G_OS_UNIX + void (*prev_handler)(gint); + + alarm(0); + prev_handler = signal(SIGALRM, timeout_handler); + if (sigsetjmp(jmpenv, 1)) { + alarm(0); + signal(SIGALRM, prev_handler); + fprintf(stderr, "%s: host lookup timed out.\n", hostname); + errno = 0; + return NULL; + } + alarm(io_timeout); +#endif + + if ((hp = gethostbyname(hostname)) == NULL) { +#ifdef G_OS_UNIX + alarm(0); + signal(SIGALRM, prev_handler); +#endif + fprintf(stderr, "%s: unknown host.\n", hostname); + errno = 0; + return NULL; + } + +#ifdef G_OS_UNIX + alarm(0); + signal(SIGALRM, prev_handler); +#endif + + return hp; +} + +#ifndef INET6 +static gint my_inet_aton(const gchar *hostname, struct in_addr *inp) +{ +#if HAVE_INET_ATON + return inet_aton(hostname, inp); +#else +#if HAVE_INET_ADDR + guint32 inaddr; + + inaddr = inet_addr(hostname); + if (inaddr != -1) { + memcpy(inp, &inaddr, sizeof(inaddr)); + return 1; + } else + return 0; +#else + return 0; +#endif +#endif /* HAVE_INET_ATON */ +} + +static gint sock_connect_by_hostname(gint sock, const gchar *hostname, + gushort port) +{ + struct hostent *hp; + struct sockaddr_in ad; + + memset(&ad, 0, sizeof(ad)); + ad.sin_family = AF_INET; + ad.sin_port = htons(port); + + if (!my_inet_aton(hostname, &ad.sin_addr)) { + if ((hp = my_gethostbyname(hostname)) == NULL) { + fprintf(stderr, "%s: unknown host.\n", hostname); + errno = 0; + return -1; + } + + if (hp->h_length != 4 && hp->h_length != 8) { + fprintf(stderr, "illegal address length received for host %s\n", hostname); + errno = 0; + return -1; + } + + memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); + } + + return sock_connect_with_timeout(sock, (struct sockaddr *)&ad, + sizeof(ad), io_timeout); +} + +#else /* INET6 */ +static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort port) +{ + gint sock = -1, gai_error; + struct addrinfo hints, *res, *ai; + gchar port_str[6]; + + memset(&hints, 0, sizeof(hints)); + /* hints.ai_flags = AI_CANONNAME; */ + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + /* convert port from integer to string. */ + g_snprintf(port_str, sizeof(port_str), "%d", port); + + if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) { + fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n", + hostname, port_str, gai_strerror(gai_error)); + return -1; + } + + for (ai = res; ai != NULL; ai = ai->ai_next) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) + continue; + + if (sock_connect_with_timeout + (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0) + break; + + fd_close(sock); + } + + if (res != NULL) + freeaddrinfo(res); + + if (ai == NULL) + return -1; + + return sock; +} +#endif /* !INET6 */ + +SockInfo *sock_connect(const gchar *hostname, gushort port) +{ +#ifdef G_OS_WIN32 + SOCKET sock; +#else + gint sock; +#endif + SockInfo *sockinfo; + +#ifdef INET6 + if ((sock = sock_connect_by_getaddrinfo(hostname, port)) < 0) + return NULL; +#else +#ifdef G_OS_WIN32 + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { + g_warning("socket() failed: %ld\n", WSAGetLastError()); +#else + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); +#endif /* G_OS_WIN32 */ + return NULL; + } + + if (sock_connect_by_hostname(sock, hostname, port) < 0) { + if (errno != 0) perror("connect"); + fd_close(sock); + return NULL; + } +#endif /* INET6 */ + + sockinfo = g_new0(SockInfo, 1); + sockinfo->sock = sock; + sockinfo->sock_ch = g_io_channel_unix_new(sock); + sockinfo->hostname = g_strdup(hostname); + sockinfo->port = port; + sockinfo->state = CONN_ESTABLISHED; + + g_usleep(100000); + + return sockinfo; +} + +#ifdef G_OS_UNIX +static void sock_address_list_free(GList *addr_list) +{ + GList *cur; + + for (cur = addr_list; cur != NULL; cur = cur->next) { + SockAddrData *addr_data = (SockAddrData *)cur->data; + g_free(addr_data->addr); + g_free(addr_data); + } + + g_list_free(addr_list); +} + +/* asynchronous TCP connection */ + +static gboolean sock_connect_async_cb(GIOChannel *source, + GIOCondition condition, gpointer data) +{ + SockConnectData *conn_data = (SockConnectData *)data; + gint fd; + gint val; + guint len; + SockInfo *sockinfo; + + fd = g_io_channel_unix_get_fd(source); + + conn_data->io_tag = 0; + conn_data->channel = NULL; + g_io_channel_unref(source); + + len = sizeof(val); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) { + perror("getsockopt"); + fd_close(fd); + sock_connect_address_list_async(conn_data); + return FALSE; + } + + if (val != 0) { + fd_close(fd); + sock_connect_address_list_async(conn_data); + return FALSE; + } + + sockinfo = g_new0(SockInfo, 1); + sockinfo->sock = fd; + sockinfo->sock_ch = g_io_channel_unix_new(fd); + sockinfo->hostname = g_strdup(conn_data->hostname); + sockinfo->port = conn_data->port; + sockinfo->state = CONN_ESTABLISHED; + + conn_data->func(sockinfo, conn_data->data); + + sock_connect_async_cancel(conn_data->id); + + return FALSE; +} + +static gint sock_connect_async_get_address_info_cb(GList *addr_list, + gpointer data) +{ + SockConnectData *conn_data = (SockConnectData *)data; + + conn_data->addr_list = addr_list; + conn_data->cur_addr = addr_list; + conn_data->lookup_data = NULL; + + return sock_connect_address_list_async(conn_data); +} + +gint sock_connect_async(const gchar *hostname, gushort port, + SockConnectFunc func, gpointer data) +{ + static gint id = 1; + SockConnectData *conn_data; + + conn_data = g_new0(SockConnectData, 1); + conn_data->id = id++; + conn_data->hostname = g_strdup(hostname); + conn_data->port = port; + conn_data->addr_list = NULL; + conn_data->cur_addr = NULL; + conn_data->io_tag = 0; + conn_data->func = func; + conn_data->data = data; + + conn_data->lookup_data = sock_get_address_info_async + (hostname, port, sock_connect_async_get_address_info_cb, + conn_data); + + if (conn_data->lookup_data == NULL) { + g_free(conn_data->hostname); + g_free(conn_data); + return -1; + } + + sock_connect_data_list = g_list_append(sock_connect_data_list, + conn_data); + + return conn_data->id; +} + +gint sock_connect_async_cancel(gint id) +{ + SockConnectData *conn_data = NULL; + GList *cur; + + for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) { + if (((SockConnectData *)cur->data)->id == id) { + conn_data = (SockConnectData *)cur->data; + break; + } + } + + if (conn_data) { + sock_connect_data_list = g_list_remove(sock_connect_data_list, + conn_data); + + if (conn_data->lookup_data) + sock_get_address_info_async_cancel + (conn_data->lookup_data); + + if (conn_data->io_tag > 0) + g_source_remove(conn_data->io_tag); + if (conn_data->channel) { + g_io_channel_shutdown(conn_data->channel, FALSE, NULL); + g_io_channel_unref(conn_data->channel); + } + + sock_address_list_free(conn_data->addr_list); + g_free(conn_data->hostname); + g_free(conn_data); + } else { + g_warning("sock_connect_async_cancel: id %d not found.\n", id); + return -1; + } + + return 0; +} + +static gint sock_connect_address_list_async(SockConnectData *conn_data) +{ + SockAddrData *addr_data; + gint sock = -1; + + for (; conn_data->cur_addr != NULL; + conn_data->cur_addr = conn_data->cur_addr->next) { + addr_data = (SockAddrData *)conn_data->cur_addr->data; + + if ((sock = socket(addr_data->family, addr_data->socktype, + addr_data->protocol)) < 0) { + perror("socket"); + continue; + } + + set_nonblocking_mode(sock, TRUE); + + if (connect(sock, addr_data->addr, addr_data->addr_len) < 0) { + if (EINPROGRESS == errno) { + break; + } else { + perror("connect"); + fd_close(sock); + } + } else + break; + } + + if (conn_data->cur_addr == NULL) { + g_warning("sock_connect_address_list_async: " + "connection to %s:%d failed\n", + conn_data->hostname, conn_data->port); + conn_data->func(NULL, conn_data->data); + sock_connect_async_cancel(conn_data->id); + return -1; + } + + conn_data->cur_addr = conn_data->cur_addr->next; + + conn_data->channel = g_io_channel_unix_new(sock); + conn_data->io_tag = g_io_add_watch(conn_data->channel, G_IO_OUT, + sock_connect_async_cb, conn_data); + + return 0; +} + +/* asynchronous DNS lookup */ + +static gboolean sock_get_address_info_async_cb(GIOChannel *source, + GIOCondition condition, + gpointer data) +{ + SockLookupData *lookup_data = (SockLookupData *)data; + GList *addr_list = NULL; + SockAddrData *addr_data; + gsize bytes_read; + gint ai_member[4]; + struct sockaddr *addr; + + for (;;) { + if (g_io_channel_read(source, (gchar *)ai_member, + sizeof(ai_member), &bytes_read) + != G_IO_ERROR_NONE) { + g_warning("sock_get_address_info_async_cb: " + "address length read error\n"); + break; + } + + if (bytes_read == 0 || bytes_read != sizeof(ai_member)) + break; + + if (ai_member[0] == AF_UNSPEC) { + g_warning("DNS lookup failed\n"); + break; + } + + addr = g_malloc(ai_member[3]); + if (g_io_channel_read(source, (gchar *)addr, ai_member[3], + &bytes_read) + != G_IO_ERROR_NONE) { + g_warning("sock_get_address_info_async_cb: " + "address data read error\n"); + g_free(addr); + break; + } + + if (bytes_read != ai_member[3]) { + g_warning("sock_get_address_info_async_cb: " + "incomplete address data\n"); + g_free(addr); + break; + } + + addr_data = g_new0(SockAddrData, 1); + addr_data->family = ai_member[0]; + addr_data->socktype = ai_member[1]; + addr_data->protocol = ai_member[2]; + addr_data->addr_len = ai_member[3]; + addr_data->addr = addr; + + addr_list = g_list_append(addr_list, addr_data); + } + + g_io_channel_shutdown(source, FALSE, NULL); + g_io_channel_unref(source); + + kill(lookup_data->child_pid, SIGKILL); + waitpid(lookup_data->child_pid, NULL, 0); + + lookup_data->func(addr_list, lookup_data->data); + + g_free(lookup_data->hostname); + g_free(lookup_data); + + return FALSE; +} + +static SockLookupData *sock_get_address_info_async(const gchar *hostname, + gushort port, + SockAddrFunc func, + gpointer data) +{ + SockLookupData *lookup_data = NULL; + gint pipe_fds[2]; + pid_t pid; + + if (pipe(pipe_fds) < 0) { + perror("pipe"); + func(NULL, data); + return NULL; + } + + if ((pid = fork()) < 0) { + perror("fork"); + func(NULL, data); + return NULL; + } + + /* child process */ + if (pid == 0) { +#ifdef INET6 + gint gai_err; + struct addrinfo hints, *res, *ai; + gchar port_str[6]; +#else /* !INET6 */ + struct hostent *hp; + gchar **addr_list_p; + struct sockaddr_in ad; +#endif /* INET6 */ + gint ai_member[4] = {AF_UNSPEC, 0, 0, 0}; + + close(pipe_fds[0]); + +#ifdef INET6 + memset(&hints, 0, sizeof(hints)); + /* hints.ai_flags = AI_CANONNAME; */ + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + g_snprintf(port_str, sizeof(port_str), "%d", port); + + gai_err = getaddrinfo(hostname, port_str, &hints, &res); + if (gai_err != 0) { + g_warning("getaddrinfo for %s:%s failed: %s\n", + hostname, port_str, gai_strerror(gai_err)); + fd_write_all(pipe_fds[1], (gchar *)ai_member, + sizeof(ai_member)); + close(pipe_fds[1]); + _exit(1); + } + + for (ai = res; ai != NULL; ai = ai->ai_next) { + ai_member[0] = ai->ai_family; + ai_member[1] = ai->ai_socktype; + ai_member[2] = ai->ai_protocol; + ai_member[3] = ai->ai_addrlen; + + fd_write_all(pipe_fds[1], (gchar *)ai_member, + sizeof(ai_member)); + fd_write_all(pipe_fds[1], (gchar *)ai->ai_addr, + ai->ai_addrlen); + } + + if (res != NULL) + freeaddrinfo(res); +#else /* !INET6 */ + hp = my_gethostbyname(hostname); + if (hp == NULL || hp->h_addrtype != AF_INET) { + fd_write_all(pipe_fds[1], (gchar *)ai_member, + sizeof(ai_member)); + close(pipe_fds[1]); + _exit(1); + } + + ai_member[0] = AF_INET; + ai_member[1] = SOCK_STREAM; + ai_member[2] = IPPROTO_TCP; + ai_member[3] = sizeof(ad); + + memset(&ad, 0, sizeof(ad)); + ad.sin_family = AF_INET; + ad.sin_port = htons(port); + + for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL; + addr_list_p++) { + memcpy(&ad.sin_addr, *addr_list_p, hp->h_length); + fd_write_all(pipe_fds[1], (gchar *)ai_member, + sizeof(ai_member)); + fd_write_all(pipe_fds[1], (gchar *)&ad, sizeof(ad)); + } +#endif /* INET6 */ + + close(pipe_fds[1]); + + _exit(0); + } else { + close(pipe_fds[1]); + + lookup_data = g_new0(SockLookupData, 1); + lookup_data->hostname = g_strdup(hostname); + lookup_data->child_pid = pid; + lookup_data->func = func; + lookup_data->data = data; + + lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]); + lookup_data->io_tag = g_io_add_watch + (lookup_data->channel, G_IO_IN, + sock_get_address_info_async_cb, lookup_data); + } + + return lookup_data; +} + +static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data) +{ + if (lookup_data->io_tag > 0) + g_source_remove(lookup_data->io_tag); + if (lookup_data->channel) { + g_io_channel_shutdown(lookup_data->channel, FALSE, NULL); + g_io_channel_unref(lookup_data->channel); + } + + if (lookup_data->child_pid > 0) { + kill(lookup_data->child_pid, SIGKILL); + waitpid(lookup_data->child_pid, NULL, 0); + } + + g_free(lookup_data->hostname); + g_free(lookup_data); + + return 0; +} +#endif /* G_OS_UNIX */ + + +gint sock_printf(SockInfo *sock, const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE]; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + return sock_write_all(sock, buf, strlen(buf)); +} + +gint sock_read(SockInfo *sock, gchar *buf, gint len) +{ + g_return_val_if_fail(sock != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_read(sock->ssl, buf, len); +#endif + return fd_read(sock->sock, buf, len); +} + +gint fd_read(gint fd, gchar *buf, gint len) +{ + if (fd_check_io(fd, G_IO_IN) < 0) + return -1; + +#ifdef G_OS_WIN32 + return recv(fd, buf, len, 0); +#else + return read(fd, buf, len); +#endif +} + +#if USE_SSL +gint ssl_read(SSL *ssl, gchar *buf, gint len) +{ + gint err, ret; + + if (SSL_pending(ssl) == 0) { + if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) + return -1; + } + + ret = SSL_read(ssl, buf, len); + + switch ((err = SSL_get_error(ssl, ret))) { + case SSL_ERROR_NONE: + return ret; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EAGAIN; + return -1; + case SSL_ERROR_ZERO_RETURN: + return 0; + default: + g_warning("SSL_read() returned error %d, ret = %d\n", err, ret); + if (ret == 0) + return 0; + return -1; + } +} +#endif + +gint sock_write(SockInfo *sock, const gchar *buf, gint len) +{ + g_return_val_if_fail(sock != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_write(sock->ssl, buf, len); +#endif + return fd_write(sock->sock, buf, len); +} + +gint fd_write(gint fd, const gchar *buf, gint len) +{ + if (fd_check_io(fd, G_IO_OUT) < 0) + return -1; + +#ifdef G_OS_WIN32 + return send(fd, buf, len, 0); +#else + return write(fd, buf, len); +#endif +} + +#if USE_SSL +gint ssl_write(SSL *ssl, const gchar *buf, gint len) +{ + gint ret; + + ret = SSL_write(ssl, buf, len); + + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_NONE: + return ret; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EAGAIN; + return -1; + default: + return -1; + } +} +#endif + +gint sock_write_all(SockInfo *sock, const gchar *buf, gint len) +{ + g_return_val_if_fail(sock != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_write_all(sock->ssl, buf, len); +#endif + return fd_write_all(sock->sock, buf, len); +} + +gint fd_write_all(gint fd, const gchar *buf, gint len) +{ + gint n, wrlen = 0; + + while (len) { + n = fd_write(fd, buf, len); + if (n <= 0) + return -1; + len -= n; + wrlen += n; + buf += n; + } + + return wrlen; +} + +#if USE_SSL +gint ssl_write_all(SSL *ssl, const gchar *buf, gint len) +{ + gint n, wrlen = 0; + + while (len) { + n = ssl_write(ssl, buf, len); + if (n <= 0) + return -1; + len -= n; + wrlen += n; + buf += n; + } + + return wrlen; +} +#endif + +gint fd_recv(gint fd, gchar *buf, gint len, gint flags) +{ + if (fd_check_io(fd, G_IO_IN) < 0) + return -1; + + return recv(fd, buf, len, flags); +} + +gint fd_gets(gint fd, gchar *buf, gint len) +{ + gchar *newline, *bp = buf; + gint n; + + if (--len < 1) + return -1; + do { + if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0) + return -1; + if ((newline = memchr(bp, '\n', n)) != NULL) + n = newline - bp + 1; + if ((n = fd_read(fd, bp, n)) < 0) + return -1; + bp += n; + len -= n; + } while (!newline && len); + + *bp = '\0'; + return bp - buf; +} + +#if USE_SSL +gint ssl_gets(SSL *ssl, gchar *buf, gint len) +{ + gchar *newline, *bp = buf; + gint n; + + if (--len < 1) + return -1; + do { + if ((n = ssl_peek(ssl, bp, len)) <= 0) + return -1; + if ((newline = memchr(bp, '\n', n)) != NULL) + n = newline - bp + 1; + if ((n = ssl_read(ssl, bp, n)) < 0) + return -1; + bp += n; + len -= n; + } while (!newline && len); + + *bp = '\0'; + return bp - buf; +} +#endif + +gint sock_gets(SockInfo *sock, gchar *buf, gint len) +{ + g_return_val_if_fail(sock != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_gets(sock->ssl, buf, len); +#endif + return fd_gets(sock->sock, buf, len); +} + +gint fd_getline(gint fd, gchar **line) +{ + gchar buf[BUFFSIZE]; + gchar *str = NULL; + gint len; + gulong size = 0; + gulong cur_offset = 0; + + while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) { + size += len; + str = g_realloc(str, size + 1); + memcpy(str + cur_offset, buf, len + 1); + cur_offset += len; + if (buf[len - 1] == '\n') + break; + } + + *line = str; + + if (!str) + return -1; + else + return (gint)size; +} + +#if USE_SSL +gint ssl_getline(SSL *ssl, gchar **line) +{ + gchar buf[BUFFSIZE]; + gchar *str = NULL; + gint len; + gulong size = 0; + gulong cur_offset = 0; + + while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) { + size += len; + str = g_realloc(str, size + 1); + memcpy(str + cur_offset, buf, len + 1); + cur_offset += len; + if (buf[len - 1] == '\n') + break; + } + + *line = str; + + if (!str) + return -1; + else + return (gint)size; +} +#endif + +gint sock_getline(SockInfo *sock, gchar **line) +{ + g_return_val_if_fail(sock != NULL, -1); + g_return_val_if_fail(line != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_getline(sock->ssl, line); +#endif + return fd_getline(sock->sock, line); +} + +gint sock_puts(SockInfo *sock, const gchar *buf) +{ + gint ret; + + if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0) + return ret; + return sock_write_all(sock, "\r\n", 2); +} + +/* peek at the socket data without actually reading it */ +#if USE_SSL +gint ssl_peek(SSL *ssl, gchar *buf, gint len) +{ + gint err, ret; + + if (SSL_pending(ssl) == 0) { + if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) + return -1; + } + + ret = SSL_peek(ssl, buf, len); + + switch ((err = SSL_get_error(ssl, ret))) { + case SSL_ERROR_NONE: + return ret; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + errno = EAGAIN; + return -1; + case SSL_ERROR_ZERO_RETURN: + return 0; + default: + g_warning("SSL_peek() returned error %d, ret = %d\n", err, ret); + if (ret == 0) + return 0; + return -1; + } +} +#endif + +gint sock_peek(SockInfo *sock, gchar *buf, gint len) +{ + g_return_val_if_fail(sock != NULL, -1); + +#if USE_SSL + if (sock->ssl) + return ssl_peek(sock->ssl, buf, len); +#endif + return fd_recv(sock->sock, buf, len, MSG_PEEK); +} + +gint sock_close(SockInfo *sock) +{ + if (!sock) + return 0; + +#if USE_SSL + if (sock->ssl) + ssl_done_socket(sock); +#endif + + if (sock->sock_ch) { + g_io_channel_shutdown(sock->sock_ch, FALSE, NULL); + g_io_channel_unref(sock->sock_ch); + } + + g_free(sock->hostname); + g_free(sock); + + return 0; +} + +gint fd_close(gint fd) +{ +#ifdef G_OS_WIN32 + return closesocket(fd); +#else + return close(fd); +#endif +} diff --git a/libsylph/socket.h b/libsylph/socket.h new file mode 100644 index 00000000..5721a304 --- /dev/null +++ b/libsylph/socket.h @@ -0,0 +1,124 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __SOCKET_H__ +#define __SOCKET_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#if HAVE_NETDB_H +# include +#endif + +typedef struct _SockInfo SockInfo; + +#if USE_SSL +# include "ssl.h" +#endif + +typedef enum +{ + CONN_READY, + CONN_LOOKUPSUCCESS, + CONN_ESTABLISHED, + CONN_LOOKUPFAILED, + CONN_FAILED +} ConnectionState; + +typedef gint (*SockConnectFunc) (SockInfo *sock, + gpointer data); +typedef gboolean (*SockFunc) (SockInfo *sock, + GIOCondition condition, + gpointer data); + +struct _SockInfo +{ + gint sock; +#if USE_SSL + SSL *ssl; +#endif + GIOChannel *sock_ch; + + gchar *hostname; + gushort port; + ConnectionState state; + gpointer data; + + SockFunc callback; + GIOCondition condition; +}; + +gint sock_init (void); +gint sock_cleanup (void); + +gint sock_set_io_timeout (guint sec); + +gint sock_set_nonblocking_mode (SockInfo *sock, gboolean nonblock); +gboolean sock_is_nonblocking_mode (SockInfo *sock); + +guint sock_add_watch (SockInfo *sock, GIOCondition condition, + SockFunc func, gpointer data); + +struct hostent *my_gethostbyname (const gchar *hostname); + +SockInfo *sock_connect (const gchar *hostname, gushort port); +#ifdef G_OS_UNIX +gint sock_connect_async (const gchar *hostname, gushort port, + SockConnectFunc func, gpointer data); +gint sock_connect_async_cancel (gint id); +#endif + +/* Basic I/O functions */ +gint sock_printf (SockInfo *sock, const gchar *format, ...) + G_GNUC_PRINTF(2, 3); +gint sock_read (SockInfo *sock, gchar *buf, gint len); +gint sock_write (SockInfo *sock, const gchar *buf, gint len); +gint sock_write_all (SockInfo *sock, const gchar *buf, gint len); +gint sock_gets (SockInfo *sock, gchar *buf, gint len); +gint sock_getline (SockInfo *sock, gchar **line); +gint sock_puts (SockInfo *sock, const gchar *buf); +gint sock_peek (SockInfo *sock, gchar *buf, gint len); +gint sock_close (SockInfo *sock); + +/* Functions to directly work on FD. They are needed for pipes */ +gint fd_connect_unix (const gchar *path); +gint fd_open_unix (const gchar *path); +gint fd_accept (gint sock); + +gint fd_read (gint sock, gchar *buf, gint len); +gint fd_write (gint sock, const gchar *buf, gint len); +gint fd_write_all (gint sock, const gchar *buf, gint len); +gint fd_gets (gint sock, gchar *buf, gint len); +gint fd_getline (gint sock, gchar **line); +gint fd_close (gint sock); + +/* Functions for SSL */ +#if USE_SSL +gint ssl_read (SSL *ssl, gchar *buf, gint len); +gint ssl_write (SSL *ssl, const gchar *buf, gint len); +gint ssl_write_all (SSL *ssl, const gchar *buf, gint len); +gint ssl_gets (SSL *ssl, gchar *buf, gint len); +gint ssl_getline (SSL *ssl, gchar **line); +gint ssl_peek (SSL *ssl, gchar *buf, gint len); +#endif + +#endif /* __SOCKET_H__ */ diff --git a/libsylph/ssl.c b/libsylph/ssl.c new file mode 100644 index 00000000..b57c60a9 --- /dev/null +++ b/libsylph/ssl.c @@ -0,0 +1,175 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_SSL + +#include "defs.h" + +#include +#include + +#include "utils.h" +#include "ssl.h" + +static SSL_CTX *ssl_ctx_SSLv23; +static SSL_CTX *ssl_ctx_TLSv1; + +void ssl_init(void) +{ + gchar *certs_dir; + + SSL_library_init(); + SSL_load_error_strings(); + + certs_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "certs", NULL); + if (!is_dir_exist(certs_dir)) { + debug_print("%s doesn't exist, or not a directory.\n", + certs_dir); + g_free(certs_dir); + certs_dir = NULL; + } + + ssl_ctx_SSLv23 = SSL_CTX_new(SSLv23_client_method()); + if (ssl_ctx_SSLv23 == NULL) { + debug_print(_("SSLv23 not available\n")); + } else { + debug_print(_("SSLv23 available\n")); + if (certs_dir && + !SSL_CTX_load_verify_locations(ssl_ctx_SSLv23, NULL, + certs_dir)) + g_warning("SSLv23 SSL_CTX_load_verify_locations failed.\n"); + } + + ssl_ctx_TLSv1 = SSL_CTX_new(TLSv1_client_method()); + if (ssl_ctx_TLSv1 == NULL) { + debug_print(_("TLSv1 not available\n")); + } else { + debug_print(_("TLSv1 available\n")); + if (certs_dir && + !SSL_CTX_load_verify_locations(ssl_ctx_TLSv1, NULL, + certs_dir)) + g_warning("TLSv1 SSL_CTX_load_verify_locations failed.\n"); + } + + g_free(certs_dir); +} + +void ssl_done(void) +{ + if (ssl_ctx_SSLv23) { + SSL_CTX_free(ssl_ctx_SSLv23); + } + + if (ssl_ctx_TLSv1) { + SSL_CTX_free(ssl_ctx_TLSv1); + } +} + +gboolean ssl_init_socket(SockInfo *sockinfo) +{ + return ssl_init_socket_with_method(sockinfo, SSL_METHOD_SSLv23); +} + +gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method) +{ + X509 *server_cert; + gint ret; + + switch (method) { + case SSL_METHOD_SSLv23: + if (!ssl_ctx_SSLv23) { + g_warning(_("SSL method not available\n")); + return FALSE; + } + sockinfo->ssl = SSL_new(ssl_ctx_SSLv23); + break; + case SSL_METHOD_TLSv1: + if (!ssl_ctx_TLSv1) { + g_warning(_("SSL method not available\n")); + return FALSE; + } + sockinfo->ssl = SSL_new(ssl_ctx_TLSv1); + break; + default: + g_warning(_("Unknown SSL method *PROGRAM BUG*\n")); + return FALSE; + break; + } + + if (sockinfo->ssl == NULL) { + g_warning(_("Error creating ssl context\n")); + return FALSE; + } + + SSL_set_fd(sockinfo->ssl, sockinfo->sock); + if ((ret = SSL_connect(sockinfo->ssl)) == -1) { + g_warning(_("SSL connect failed (%s)\n"), + ERR_error_string(ERR_get_error(), NULL)); + return FALSE; + } + + /* Get the cipher */ + + debug_print(_("SSL connection using %s\n"), + SSL_get_cipher(sockinfo->ssl)); + + /* Get server's certificate (note: beware of dynamic allocation) */ + + if ((server_cert = SSL_get_peer_certificate(sockinfo->ssl)) != NULL) { + gchar *str; + glong verify_result; + + debug_print(_("Server certificate:\n")); + + if ((str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0)) != NULL) { + debug_print(_(" Subject: %s\n"), str); + g_free(str); + } + + if ((str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0)) != NULL) { + debug_print(_(" Issuer: %s\n"), str); + g_free(str); + } + + verify_result = SSL_get_verify_result(sockinfo->ssl); + if (verify_result == X509_V_OK) + debug_print("SSL verify OK\n"); + else + g_warning("%s: SSL certificate verify failed (%ld: %s)\n", + sockinfo->hostname, verify_result, + X509_verify_cert_error_string(verify_result)); + + X509_free(server_cert); + } + + return TRUE; +} + +void ssl_done_socket(SockInfo *sockinfo) +{ + if (sockinfo->ssl) { + SSL_free(sockinfo->ssl); + } +} + +#endif /* USE_SSL */ diff --git a/libsylph/ssl.h b/libsylph/ssl.h new file mode 100644 index 00000000..6c13ddac --- /dev/null +++ b/libsylph/ssl.h @@ -0,0 +1,58 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2002 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __SSL_H__ +#define __SSL_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if USE_SSL + +#include +#include +#include +#include +#include +#include + +#include "socket.h" + +typedef enum { + SSL_METHOD_SSLv23, + SSL_METHOD_TLSv1 +} SSLMethod; + +typedef enum { + SSL_NONE, + SSL_TUNNEL, + SSL_STARTTLS +} SSLType; + +void ssl_init (void); +void ssl_done (void); +gboolean ssl_init_socket (SockInfo *sockinfo); +gboolean ssl_init_socket_with_method (SockInfo *sockinfo, + SSLMethod method); +void ssl_done_socket (SockInfo *sockinfo); + +#endif /* USE_SSL */ + +#endif /* __SSL_H__ */ diff --git a/libsylph/stringtable.c b/libsylph/stringtable.c new file mode 100644 index 00000000..da5e0ea4 --- /dev/null +++ b/libsylph/stringtable.c @@ -0,0 +1,163 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2001 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include + +#include "stringtable.h" +#include "utils.h" + +/* alfons - hashed string table (I wasn't content with GStringChunk; + * can't recall why :-) */ + +#if 0 +#define XXX_DEBUG \ + debug_print +#else +#define XXX_DEBUG \ + if (0) debug_print +#endif + +typedef struct StringEntry_ { + gint ref_count; + gchar *string; +} StringEntry; + +static StringEntry *string_entry_new(const gchar *str) +{ + StringEntry *entry; + + entry = g_new0(StringEntry, 1); + entry->ref_count = 1; + entry->string = g_strdup(str); + return entry; +} + +static void string_entry_free(StringEntry *entry) +{ + g_return_if_fail(entry != NULL); + + g_free(entry->string); + g_free(entry); +} + +StringTable *string_table_new(void) +{ + StringTable *strtable; + + strtable = g_new0(StringTable, 1); + g_return_val_if_fail(strtable != NULL, NULL); + strtable->hash_table = g_hash_table_new(g_str_hash, g_str_equal); + g_return_val_if_fail(strtable->hash_table, NULL); + return strtable; +} + +gchar *string_table_lookup_string(StringTable *table, const gchar *str) +{ + StringEntry *entry; + + entry = g_hash_table_lookup(table->hash_table, str); + + if (entry) { + return entry->string; + } else { + return NULL; + } +} + +gchar *string_table_insert_string(StringTable *table, const gchar *str) +{ + StringEntry *entry; + + entry = g_hash_table_lookup(table->hash_table, str); + + if (entry) { + entry->ref_count++; + XXX_DEBUG ("ref++ for %s (%d)\n", entry->string, + entry->ref_count); + } else { + entry = string_entry_new(str); + XXX_DEBUG ("inserting %s\n", str); + /* insert entry->string instead of str, since it can be + * invalid pointer after this. */ + g_hash_table_insert(table->hash_table, entry->string, entry); + } + + return entry->string; +} + +void string_table_free_string(StringTable *table, const gchar *str) +{ + StringEntry *entry; + + entry = g_hash_table_lookup(table->hash_table, str); + + if (entry) { + entry->ref_count--; + if (entry->ref_count <= 0) { + XXX_DEBUG ("refcount of string %s dropped to zero\n", + entry->string); + g_hash_table_remove(table->hash_table, str); + string_entry_free(entry); + } else { + XXX_DEBUG ("ref-- for %s (%d)\n", entry->string, + entry->ref_count); + } + } +} + +static gboolean string_table_remove_for_each_fn(gchar *key, StringEntry *entry, + gpointer user_data) +{ + g_return_val_if_fail(key != NULL, TRUE); + g_return_val_if_fail(entry != NULL, TRUE); + + string_entry_free(entry); + + return TRUE; +} + +void string_table_free(StringTable *table) +{ + g_return_if_fail(table != NULL); + g_return_if_fail(table->hash_table != NULL); + + g_hash_table_foreach_remove(table->hash_table, + (GHRFunc)string_table_remove_for_each_fn, + NULL); + g_hash_table_destroy(table->hash_table); + g_free(table); +} + +static void string_table_stats_for_each_fn(gchar *key, StringEntry *entry, + guint *totals) +{ + if (entry->ref_count > 1) { + *totals += strlen(key) * (entry->ref_count - 1); + } +} + +void string_table_get_stats(StringTable *table) +{ + guint totals = 0; + + g_hash_table_foreach(table->hash_table, + (GHFunc)string_table_stats_for_each_fn, &totals); + XXX_DEBUG ("TOTAL UNSPILLED %d (%dK)\n", totals, totals / 1024); +} diff --git a/libsylph/stringtable.h b/libsylph/stringtable.h new file mode 100644 index 00000000..337ef2c7 --- /dev/null +++ b/libsylph/stringtable.h @@ -0,0 +1,38 @@ +/* + * sylpheed -- a gtk+ based, lightweight, and fast e-mail client + * copyright (c) 1999-2001 hiroyuki yamamoto + * + * 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; either version 2 of the license, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef STRINGTABLE_H__ +#define STRINGTABLE_H__ + +#include + +typedef struct { + GHashTable *hash_table; +} StringTable; + +StringTable *string_table_new (void); +void string_table_free (StringTable *table); + +gchar *string_table_lookup_string (StringTable *table, const gchar *str); +gchar *string_table_insert_string (StringTable *table, const gchar *str); +void string_table_free_string (StringTable *table, const gchar *str); + +void string_table_get_stats (StringTable *table); + +#endif /* STRINGTABLE_H__ */ diff --git a/libsylph/unmime.c b/libsylph/unmime.c new file mode 100644 index 00000000..23d787a3 --- /dev/null +++ b/libsylph/unmime.c @@ -0,0 +1,134 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "codeconv.h" +#include "base64.h" +#include "quoted-printable.h" + +#define ENCODED_WORD_BEGIN "=?" +#define ENCODED_WORD_END "?=" + +/* Decodes headers based on RFC2045 and RFC2047. */ + +gchar *unmime_header(const gchar *encoded_str) +{ + const gchar *p = encoded_str; + const gchar *eword_begin_p, *encoding_begin_p, *text_begin_p, + *eword_end_p; + gchar charset[32]; + gchar encoding; + gchar *conv_str; + GString *outbuf; + gchar *out_str; + gsize out_len; + + outbuf = g_string_sized_new(strlen(encoded_str) * 2); + + while (*p != '\0') { + gchar *decoded_text = NULL; + gint len; + + eword_begin_p = strstr(p, ENCODED_WORD_BEGIN); + if (!eword_begin_p) { + g_string_append(outbuf, p); + break; + } + encoding_begin_p = strchr(eword_begin_p + 2, '?'); + if (!encoding_begin_p) { + g_string_append(outbuf, p); + break; + } + text_begin_p = strchr(encoding_begin_p + 1, '?'); + if (!text_begin_p) { + g_string_append(outbuf, p); + break; + } + eword_end_p = strstr(text_begin_p + 1, ENCODED_WORD_END); + if (!eword_end_p) { + g_string_append(outbuf, p); + break; + } + + if (p == encoded_str) { + g_string_append_len(outbuf, p, eword_begin_p - p); + p = eword_begin_p; + } else { + /* ignore spaces between encoded words */ + const gchar *sp; + + for (sp = p; sp < eword_begin_p; sp++) { + if (!g_ascii_isspace(*sp)) { + g_string_append_len + (outbuf, p, eword_begin_p - p); + p = eword_begin_p; + break; + } + } + } + + len = MIN(sizeof(charset) - 1, + encoding_begin_p - (eword_begin_p + 2)); + memcpy(charset, eword_begin_p + 2, len); + charset[len] = '\0'; + encoding = g_ascii_toupper(*(encoding_begin_p + 1)); + + if (encoding == 'B') { + decoded_text = g_malloc + (eword_end_p - (text_begin_p + 1) + 1); + len = base64_decode(decoded_text, text_begin_p + 1, + eword_end_p - (text_begin_p + 1)); + decoded_text[len] = '\0'; + } else if (encoding == 'Q') { + decoded_text = g_malloc + (eword_end_p - (text_begin_p + 1) + 1); + len = qp_decode_q_encoding + (decoded_text, text_begin_p + 1, + eword_end_p - (text_begin_p + 1)); + } else { + g_string_append_len(outbuf, p, eword_end_p + 2 - p); + p = eword_end_p + 2; + continue; + } + + /* convert to UTF-8 */ + conv_str = conv_codeset_strdup(decoded_text, charset, NULL); + if (!conv_str) + conv_str = conv_utf8todisp(decoded_text, NULL); + g_string_append(outbuf, conv_str); + g_free(conv_str); + + g_free(decoded_text); + + p = eword_end_p + 2; + } + + out_str = outbuf->str; + out_len = outbuf->len; + g_string_free(outbuf, FALSE); + + return g_realloc(out_str, out_len + 1); +} diff --git a/libsylph/unmime.h b/libsylph/unmime.h new file mode 100644 index 00000000..be9f390e --- /dev/null +++ b/libsylph/unmime.h @@ -0,0 +1,27 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __UNMIME_H__ +#define __UNMIME_H__ + +#include + +gchar *unmime_header (const gchar *encoded_str); + +#endif /* __UNMIME_H__ */ diff --git a/libsylph/utils.c b/libsylph/utils.c new file mode 100644 index 00000000..dc72b814 --- /dev/null +++ b/libsylph/utils.c @@ -0,0 +1,3436 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "defs.h" + +#include +#include +#include +#include +#include +#include + +#if (HAVE_WCTYPE_H && HAVE_WCHAR_H) +# include +# include +#endif +#include +#include +#include +#include +#include +#if HAVE_SYS_WAIT_H +# include +#endif +#include +#include + +#ifdef G_OS_WIN32 +# include +# include +#endif + +#include "utils.h" +#include "socket.h" + +#define BUFFSIZE 8192 + +static gboolean debug_mode = FALSE; + + +#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX) +gint g_chdir(const gchar *path) +{ +#ifdef G_OS_WIN32 + if (G_WIN32_HAVE_WIDECHAR_API()) { + wchar_t *wpath; + gint retval; + gint save_errno; + + wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); + if (wpath == NULL) { + errno = EINVAL; + return -1; + } + + retval = _wchdir(wpath); + save_errno = errno; + + g_free(wpath); + + errno = save_errno; + return retval; + } else { + gchar *cp_path; + gint retval; + gint save_errno; + + cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); + if (cp_path == NULL) { + errno = EINVAL; + return -1; + } + + retval = chdir(cp_path); + save_errno = errno; + + g_free(cp_path); + + errno = save_errno; + return retval; + } +#else + return chdir(path); +#endif +} + +gint g_chmod(const gchar *path, gint mode) +{ +#ifdef G_OS_WIN32 + if (G_WIN32_HAVE_WIDECHAR_API()) { + wchar_t *wpath; + gint retval; + gint save_errno; + + wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); + if (wpath == NULL) { + errno = EINVAL; + return -1; + } + + retval = _wchmod(wpath, mode); + save_errno = errno; + + g_free(wpath); + + errno = save_errno; + return retval; + } else { + gchar *cp_path; + gint retval; + gint save_errno; + + cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); + if (cp_path == NULL) { + errno = EINVAL; + return -1; + } + + retval = chmod(cp_path, mode); + save_errno = errno; + + g_free(cp_path); + + errno = save_errno; + return retval; + } +#else + return chmod(path, mode); +#endif +} +#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */ + +void list_free_strings(GList *list) +{ + list = g_list_first(list); + + while (list != NULL) { + g_free(list->data); + list = list->next; + } +} + +void slist_free_strings(GSList *list) +{ + while (list != NULL) { + g_free(list->data); + list = list->next; + } +} + +static void hash_free_strings_func(gpointer key, gpointer value, gpointer data) +{ + g_free(key); +} + +void hash_free_strings(GHashTable *table) +{ + g_hash_table_foreach(table, hash_free_strings_func, NULL); +} + +static void hash_free_value_mem_func(gpointer key, gpointer value, + gpointer data) +{ + g_free(value); +} + +void hash_free_value_mem(GHashTable *table) +{ + g_hash_table_foreach(table, hash_free_value_mem_func, NULL); +} + +gint str_case_equal(gconstpointer v, gconstpointer v2) +{ + return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0; +} + +guint str_case_hash(gconstpointer key) +{ + const gchar *p = key; + guint h = *p; + + if (h) { + h = g_ascii_tolower(h); + for (p += 1; *p != '\0'; p++) + h = (h << 5) - h + g_ascii_tolower(*p); + } + + return h; +} + +void ptr_array_free_strings(GPtrArray *array) +{ + gint i; + gchar *str; + + g_return_if_fail(array != NULL); + + for (i = 0; i < array->len; i++) { + str = g_ptr_array_index(array, i); + g_free(str); + } +} + +gboolean str_find(const gchar *haystack, const gchar *needle) +{ + return strstr(haystack, needle) != NULL ? TRUE : FALSE; +} + +gboolean str_case_find(const gchar *haystack, const gchar *needle) +{ + return strcasestr(haystack, needle) != NULL ? TRUE : FALSE; +} + +gboolean str_find_equal(const gchar *haystack, const gchar *needle) +{ + return strcmp(haystack, needle) == 0; +} + +gboolean str_case_find_equal(const gchar *haystack, const gchar *needle) +{ + return g_ascii_strcasecmp(haystack, needle) == 0; +} + +gint to_number(const gchar *nstr) +{ + register const gchar *p; + + if (*nstr == '\0') return -1; + + for (p = nstr; *p != '\0'; p++) + if (!g_ascii_isdigit(*p)) return -1; + + return atoi(nstr); +} + +/* convert integer into string, + nstr must be not lower than 11 characters length */ +gchar *itos_buf(gchar *nstr, gint n) +{ + g_snprintf(nstr, 11, "%d", n); + return nstr; +} + +/* convert integer into string */ +gchar *itos(gint n) +{ + static gchar nstr[11]; + + return itos_buf(nstr, n); +} + +gchar *to_human_readable(off_t size) +{ + static gchar str[10]; + + if (size < 1024) + g_snprintf(str, sizeof(str), _("%dB"), (gint)size); + else if (size >> 10 < 1024) + g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10)); + else if (size >> 20 < 1024) + g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20)); + else + g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30)); + + return str; +} + +/* strcmp with NULL-checking */ +gint strcmp2(const gchar *s1, const gchar *s2) +{ + if (s1 == NULL || s2 == NULL) + return -1; + else + return strcmp(s1, s2); +} + +/* compare paths */ +gint path_cmp(const gchar *s1, const gchar *s2) +{ + gint len1, len2; +#ifdef G_OS_WIN32 + gchar *s1_, *s2_; +#endif + + if (s1 == NULL || s2 == NULL) return -1; + if (*s1 == '\0' || *s2 == '\0') return -1; + + len1 = strlen(s1); + len2 = strlen(s2); + +#ifdef G_OS_WIN32 + Xstrdup_a(s1_, s1, return -1); + Xstrdup_a(s2_, s2, return -1); + subst_char(s1_, '/', G_DIR_SEPARATOR); + subst_char(s2_, '/', G_DIR_SEPARATOR); + if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--; + if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--; + + return strncmp(s1_, s2_, MAX(len1, len2)); +#else + if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--; + if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--; + + return strncmp(s1, s2, MAX(len1, len2)); +#endif +} + +/* remove trailing return code */ +gchar *strretchomp(gchar *str) +{ + register gchar *s; + + if (!*str) return str; + + for (s = str + strlen(str) - 1; + s >= str && (*s == '\n' || *s == '\r'); + s--) + *s = '\0'; + + return str; +} + +/* remove trailing character */ +gchar *strtailchomp(gchar *str, gchar tail_char) +{ + register gchar *s; + + if (!*str) return str; + if (tail_char == '\0') return str; + + for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--) + *s = '\0'; + + return str; +} + +/* remove CR (carriage return) */ +gchar *strcrchomp(gchar *str) +{ + register gchar *s; + + if (!*str) return str; + + s = str + strlen(str) - 1; + if (*s == '\n' && s > str && *(s - 1) == '\r') { + *(s - 1) = '\n'; + *s = '\0'; + } + + return str; +} + +/* Similar to `strstr' but this function ignores the case of both strings. */ +gchar *strcasestr(const gchar *haystack, const gchar *needle) +{ + register size_t haystack_len, needle_len; + + haystack_len = strlen(haystack); + needle_len = strlen(needle); + + if (haystack_len < needle_len || needle_len == 0) + return NULL; + + while (haystack_len >= needle_len) { + if (!g_ascii_strncasecmp(haystack, needle, needle_len)) + return (gchar *)haystack; + else { + haystack++; + haystack_len--; + } + } + + return NULL; +} + +gpointer my_memmem(gconstpointer haystack, size_t haystacklen, + gconstpointer needle, size_t needlelen) +{ + const gchar *haystack_ = (const gchar *)haystack; + const gchar *needle_ = (const gchar *)needle; + const gchar *haystack_cur = (const gchar *)haystack; + + if (needlelen == 1) + return memchr(haystack_, *needle_, haystacklen); + + while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen)) + != NULL) { + if (haystacklen - (haystack_cur - haystack_) < needlelen) + break; + if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0) + return (gpointer)haystack_cur; + else + haystack_cur++; + } + + return NULL; +} + +/* Copy no more than N characters of SRC to DEST, with NULL terminating. */ +gchar *strncpy2(gchar *dest, const gchar *src, size_t n) +{ + register const gchar *s = src; + register gchar *d = dest; + + while (--n && *s) + *d++ = *s++; + *d = '\0'; + + return dest; +} + +#if !HAVE_ISWALNUM +int iswalnum(wint_t wc) +{ + return g_ascii_isalnum((int)wc); +} +#endif + +#if !HAVE_ISWSPACE +int iswspace(wint_t wc) +{ + return g_ascii_isspace((int)wc); +} +#endif + +#if !HAVE_TOWLOWER +wint_t towlower(wint_t wc) +{ + if (wc >= L'A' && wc <= L'Z') + return wc + L'a' - L'A'; + + return wc; +} +#endif + +#if !HAVE_WCSLEN +size_t wcslen(const wchar_t *s) +{ + size_t len = 0; + + while (*s != L'\0') + ++len, ++s; + + return len; +} +#endif + +#if !HAVE_WCSCPY +/* Copy SRC to DEST. */ +wchar_t *wcscpy(wchar_t *dest, const wchar_t *src) +{ + wint_t c; + wchar_t *s = dest; + + do { + c = *src++; + *dest++ = c; + } while (c != L'\0'); + + return s; +} +#endif + +#if !HAVE_WCSNCPY +/* Copy no more than N wide-characters of SRC to DEST. */ +wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n) +{ + wint_t c; + wchar_t *s = dest; + + do { + c = *src++; + *dest++ = c; + if (--n == 0) + return s; + } while (c != L'\0'); + + /* zero fill */ + do + *dest++ = L'\0'; + while (--n > 0); + + return s; +} +#endif + +/* Duplicate S, returning an identical malloc'd string. */ +wchar_t *wcsdup(const wchar_t *s) +{ + wchar_t *new_str; + + if (s) { + new_str = g_new(wchar_t, wcslen(s) + 1); + wcscpy(new_str, s); + } else + new_str = NULL; + + return new_str; +} + +/* Duplicate no more than N wide-characters of S, + returning an identical malloc'd string. */ +wchar_t *wcsndup(const wchar_t *s, size_t n) +{ + wchar_t *new_str; + + if (s) { + new_str = g_new(wchar_t, n + 1); + wcsncpy(new_str, s, n); + new_str[n] = (wchar_t)0; + } else + new_str = NULL; + + return new_str; +} + +wchar_t *strdup_mbstowcs(const gchar *s) +{ + wchar_t *new_str; + + if (s) { + new_str = g_new(wchar_t, strlen(s) + 1); + if (mbstowcs(new_str, s, strlen(s) + 1) < 0) { + g_free(new_str); + new_str = NULL; + } else + new_str = g_realloc(new_str, + sizeof(wchar_t) * (wcslen(new_str) + 1)); + } else + new_str = NULL; + + return new_str; +} + +gchar *strdup_wcstombs(const wchar_t *s) +{ + gchar *new_str; + size_t len; + + if (s) { + len = wcslen(s) * MB_CUR_MAX + 1; + new_str = g_new(gchar, len); + if (wcstombs(new_str, s, len) < 0) { + g_free(new_str); + new_str = NULL; + } else + new_str = g_realloc(new_str, strlen(new_str) + 1); + } else + new_str = NULL; + + return new_str; +} + +/* Compare S1 and S2, ignoring case. */ +gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n) +{ + wint_t c1; + wint_t c2; + + while (n--) { + c1 = towlower(*s1++); + c2 = towlower(*s2++); + if (c1 != c2) + return c1 - c2; + else if (c1 == 0 && c2 == 0) + break; + } + + return 0; +} + +/* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */ +wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle) +{ + register size_t haystack_len, needle_len; + + haystack_len = wcslen(haystack); + needle_len = wcslen(needle); + + if (haystack_len < needle_len || needle_len == 0) + return NULL; + + while (haystack_len >= needle_len) { + if (!wcsncasecmp(haystack, needle, needle_len)) + return (wchar_t *)haystack; + else { + haystack++; + haystack_len--; + } + } + + return NULL; +} + +gint get_mbs_len(const gchar *s) +{ + const gchar *p = s; + gint mb_len; + gint len = 0; + + if (!p) + return -1; + + while (*p != '\0') { + mb_len = g_utf8_skip[*(guchar *)p]; + if (mb_len == 0) + break; + else + len++; + + p += mb_len; + } + + return len; +} + +/* Examine if next block is non-ASCII string */ +gboolean is_next_nonascii(const gchar *s) +{ + const gchar *p; + + /* skip head space */ + for (p = s; *p != '\0' && g_ascii_isspace(*p); p++) + ; + for (; *p != '\0' && !g_ascii_isspace(*p); p++) { + if (*(guchar *)p > 127 || *(guchar *)p < 32) + return TRUE; + } + + return FALSE; +} + +gint get_next_word_len(const gchar *s) +{ + gint len = 0; + + for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++) + ; + + return len; +} + +/* compare subjects */ +gint subject_compare(const gchar *s1, const gchar *s2) +{ + gchar *str1, *str2; + + if (!s1 || !s2) return -1; + if (!*s1 || !*s2) return -1; + + Xstrdup_a(str1, s1, return -1); + Xstrdup_a(str2, s2, return -1); + + trim_subject_for_compare(str1); + trim_subject_for_compare(str2); + + if (!*str1 || !*str2) return -1; + + return strcmp(str1, str2); +} + +gint subject_compare_for_sort(const gchar *s1, const gchar *s2) +{ + gchar *str1, *str2; + + if (!s1 || !s2) return -1; + + Xstrdup_a(str1, s1, return -1); + Xstrdup_a(str2, s2, return -1); + + trim_subject_for_sort(str1); + trim_subject_for_sort(str2); + + return g_ascii_strcasecmp(str1, str2); +} + +void trim_subject_for_compare(gchar *str) +{ + gchar *srcp; + + eliminate_parenthesis(str, '[', ']'); + eliminate_parenthesis(str, '(', ')'); + g_strstrip(str); + + while (!g_ascii_strncasecmp(str, "Re:", 3)) { + srcp = str + 3; + while (g_ascii_isspace(*srcp)) srcp++; + memmove(str, srcp, strlen(srcp) + 1); + } +} + +void trim_subject_for_sort(gchar *str) +{ + gchar *srcp; + + g_strstrip(str); + + while (!g_ascii_strncasecmp(str, "Re:", 3)) { + srcp = str + 3; + while (g_ascii_isspace(*srcp)) srcp++; + memmove(str, srcp, strlen(srcp) + 1); + } +} + +void trim_subject(gchar *str) +{ + register gchar *srcp, *destp; + gchar op, cl; + gint in_brace; + + destp = str; + while (!g_ascii_strncasecmp(destp, "Re:", 3)) { + destp += 3; + while (g_ascii_isspace(*destp)) destp++; + } + + if (*destp == '[') { + op = '['; + cl = ']'; + } else if (*destp == '(') { + op = '('; + cl = ')'; + } else + return; + + srcp = destp + 1; + in_brace = 1; + while (*srcp) { + if (*srcp == op) + in_brace++; + else if (*srcp == cl) + in_brace--; + srcp++; + if (in_brace == 0) + break; + } + while (g_ascii_isspace(*srcp)) srcp++; + memmove(destp, srcp, strlen(srcp) + 1); +} + +void eliminate_parenthesis(gchar *str, gchar op, gchar cl) +{ + register gchar *srcp, *destp; + gint in_brace; + + srcp = destp = str; + + while ((destp = strchr(destp, op))) { + in_brace = 1; + srcp = destp + 1; + while (*srcp) { + if (*srcp == op) + in_brace++; + else if (*srcp == cl) + in_brace--; + srcp++; + if (in_brace == 0) + break; + } + while (g_ascii_isspace(*srcp)) srcp++; + memmove(destp, srcp, strlen(srcp) + 1); + } +} + +void extract_parenthesis(gchar *str, gchar op, gchar cl) +{ + register gchar *srcp, *destp; + gint in_brace; + + srcp = destp = str; + + while ((srcp = strchr(destp, op))) { + if (destp > str) + *destp++ = ' '; + memmove(destp, srcp + 1, strlen(srcp)); + in_brace = 1; + while(*destp) { + if (*destp == op) + in_brace++; + else if (*destp == cl) + in_brace--; + + if (in_brace == 0) + break; + + destp++; + } + } + *destp = '\0'; +} + +void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr, + gchar op, gchar cl) +{ + register gchar *srcp, *destp; + gint in_brace; + gboolean in_quote = FALSE; + + srcp = destp = str; + + while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) { + if (destp > str) + *destp++ = ' '; + memmove(destp, srcp + 1, strlen(srcp)); + in_brace = 1; + while(*destp) { + if (*destp == op && !in_quote) + in_brace++; + else if (*destp == cl && !in_quote) + in_brace--; + else if (*destp == quote_chr) + in_quote ^= TRUE; + + if (in_brace == 0) + break; + + destp++; + } + } + *destp = '\0'; +} + +void eliminate_quote(gchar *str, gchar quote_chr) +{ + register gchar *srcp, *destp; + + srcp = destp = str; + + while ((destp = strchr(destp, quote_chr))) { + if ((srcp = strchr(destp + 1, quote_chr))) { + srcp++; + while (g_ascii_isspace(*srcp)) srcp++; + memmove(destp, srcp, strlen(srcp) + 1); + } else { + *destp = '\0'; + break; + } + } +} + +void extract_quote(gchar *str, gchar quote_chr) +{ + register gchar *p; + + if ((str = strchr(str, quote_chr))) { + if ((p = strchr(str + 1, quote_chr))) { + *p = '\0'; + memmove(str, str + 1, p - str); + } + } +} + +void eliminate_address_comment(gchar *str) +{ + register gchar *srcp, *destp; + gint in_brace; + + srcp = destp = str; + + while ((destp = strchr(destp, '"'))) { + if ((srcp = strchr(destp + 1, '"'))) { + srcp++; + if (*srcp == '@') { + destp = srcp + 1; + } else { + while (g_ascii_isspace(*srcp)) srcp++; + memmove(destp, srcp, strlen(srcp) + 1); + } + } else { + *destp = '\0'; + break; + } + } + + srcp = destp = str; + + while ((destp = strchr_with_skip_quote(destp, '"', '('))) { + in_brace = 1; + srcp = destp + 1; + while (*srcp) { + if (*srcp == '(') + in_brace++; + else if (*srcp == ')') + in_brace--; + srcp++; + if (in_brace == 0) + break; + } + while (g_ascii_isspace(*srcp)) srcp++; + memmove(destp, srcp, strlen(srcp) + 1); + } +} + +gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) +{ + gboolean in_quote = FALSE; + + while (*str) { + if (*str == c && !in_quote) + return (gchar *)str; + if (*str == quote_chr) + in_quote ^= TRUE; + str++; + } + + return NULL; +} + +gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) +{ + gboolean in_quote = FALSE; + const gchar *p; + + p = str + strlen(str) - 1; + while (p >= str) { + if (*p == c && !in_quote) + return (gchar *)p; + if (*p == quote_chr) + in_quote ^= TRUE; + p--; + } + + return NULL; +} + +void extract_address(gchar *str) +{ + eliminate_address_comment(str); + if (strchr_with_skip_quote(str, '"', '<')) + extract_parenthesis_with_skip_quote(str, '"', '<', '>'); + g_strstrip(str); +} + +void extract_list_id_str(gchar *str) +{ + if (strchr_with_skip_quote(str, '"', '<')) + extract_parenthesis_with_skip_quote(str, '"', '<', '>'); + g_strstrip(str); +} + +GSList *address_list_append(GSList *addr_list, const gchar *str) +{ + gchar *work; + gchar *workp; + + if (!str) return addr_list; + + Xstrdup_a(work, str, return addr_list); + + eliminate_address_comment(work); + workp = work; + + while (workp && *workp) { + gchar *p, *next; + + if ((p = strchr_with_skip_quote(workp, '"', ','))) { + *p = '\0'; + next = p + 1; + } else + next = NULL; + + if (strchr_with_skip_quote(workp, '"', '<')) + extract_parenthesis_with_skip_quote + (workp, '"', '<', '>'); + + g_strstrip(workp); + if (*workp) + addr_list = g_slist_append(addr_list, g_strdup(workp)); + + workp = next; + } + + return addr_list; +} + +GSList *references_list_prepend(GSList *msgid_list, const gchar *str) +{ + const gchar *strp; + + if (!str) return msgid_list; + strp = str; + + while (strp && *strp) { + const gchar *start, *end; + gchar *msgid; + + if ((start = strchr(strp, '<')) != NULL) { + end = strchr(start + 1, '>'); + if (!end) break; + } else + break; + + msgid = g_strndup(start + 1, end - start - 1); + g_strstrip(msgid); + if (*msgid) + msgid_list = g_slist_prepend(msgid_list, msgid); + else + g_free(msgid); + + strp = end + 1; + } + + return msgid_list; +} + +GSList *references_list_append(GSList *msgid_list, const gchar *str) +{ + GSList *list; + + list = references_list_prepend(NULL, str); + list = g_slist_reverse(list); + msgid_list = g_slist_concat(msgid_list, list); + + return msgid_list; +} + +GSList *newsgroup_list_append(GSList *group_list, const gchar *str) +{ + gchar *work; + gchar *workp; + + if (!str) return group_list; + + Xstrdup_a(work, str, return group_list); + + workp = work; + + while (workp && *workp) { + gchar *p, *next; + + if ((p = strchr_with_skip_quote(workp, '"', ','))) { + *p = '\0'; + next = p + 1; + } else + next = NULL; + + g_strstrip(workp); + if (*workp) + group_list = g_slist_append(group_list, + g_strdup(workp)); + + workp = next; + } + + return group_list; +} + +GList *add_history(GList *list, const gchar *str) +{ + GList *old; + + g_return_val_if_fail(str != NULL, list); + + old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2); + if (old) { + g_free(old->data); + list = g_list_remove(list, old->data); + } else if (g_list_length(list) >= MAX_HISTORY_SIZE) { + GList *last; + + last = g_list_last(list); + if (last) { + g_free(last->data); + g_list_remove(list, last->data); + } + } + + list = g_list_prepend(list, g_strdup(str)); + + return list; +} + +void remove_return(gchar *str) +{ + register gchar *p = str; + + while (*p) { + if (*p == '\n' || *p == '\r') + memmove(p, p + 1, strlen(p)); + else + p++; + } +} + +void remove_space(gchar *str) +{ + register gchar *p = str; + register gint spc; + + while (*p) { + spc = 0; + while (g_ascii_isspace(*(p + spc))) + spc++; + if (spc) + memmove(p, p + spc, strlen(p + spc) + 1); + else + p++; + } +} + +void unfold_line(gchar *str) +{ + register gchar *p = str; + register gint spc; + + while (*p) { + if (*p == '\n' || *p == '\r') { + *p++ = ' '; + spc = 0; + while (g_ascii_isspace(*(p + spc))) + spc++; + if (spc) + memmove(p, p + spc, strlen(p + spc) + 1); + } else + p++; + } +} + +void subst_char(gchar *str, gchar orig, gchar subst) +{ + register gchar *p = str; + + while (*p) { + if (*p == orig) + *p = subst; + p++; + } +} + +void subst_chars(gchar *str, gchar *orig, gchar subst) +{ + register gchar *p = str; + + while (*p) { + if (strchr(orig, *p) != NULL) + *p = subst; + p++; + } +} + +void subst_null(gchar *str, gint len, gchar subst) +{ + register gchar *p = str; + + while (len--) { + if (*p == '\0') + *p = subst; + p++; + } +} + +void subst_for_filename(gchar *str) +{ + subst_chars(str, " \t\r\n\"'/\\", '_'); +} + +gboolean is_header_line(const gchar *str) +{ + if (str[0] == ':') return FALSE; + + while (*str != '\0' && *str != ' ') { + if (*str == ':') + return TRUE; + str++; + } + + return FALSE; +} + +gboolean is_ascii_str(const gchar *str) +{ + const guchar *p = (const guchar *)str; + + while (*p != '\0') { + if (*p != '\t' && *p != ' ' && + *p != '\r' && *p != '\n' && + (*p < 32 || *p >= 127)) + return FALSE; + p++; + } + + return TRUE; +} + +gint get_quote_level(const gchar *str) +{ + const gchar *first_pos; + const gchar *last_pos; + const gchar *p = str; + gint quote_level = -1; + + /* speed up line processing by only searching to the last '>' */ + if ((first_pos = strchr(str, '>')) != NULL) { + /* skip a line if it contains a '<' before the initial '>' */ + if (memchr(str, '<', first_pos - str) != NULL) + return -1; + last_pos = strrchr(first_pos, '>'); + } else + return -1; + + while (p <= last_pos) { + while (p < last_pos) { + if (g_ascii_isspace(*p)) + p++; + else + break; + } + + if (*p == '>') + quote_level++; + else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) { + /* any characters are allowed except '-' and space */ + while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) && + p < last_pos) + p++; + if (*p == '>') + quote_level++; + else + break; + } + + p++; + } + + return quote_level; +} + +gint check_line_length(const gchar *str, gint max_chars, gint *line) +{ + const gchar *p = str, *q; + gint cur_line = 0, len; + + while ((q = strchr(p, '\n')) != NULL) { + len = q - p + 1; + if (len > max_chars) { + if (line) + *line = cur_line; + return -1; + } + p = q + 1; + ++cur_line; + } + + len = strlen(p); + if (len > max_chars) { + if (line) + *line = cur_line; + return -1; + } + + return 0; +} + +gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle) +{ + register guint haystack_len, needle_len; + gboolean in_squote = FALSE, in_dquote = FALSE; + + haystack_len = strlen(haystack); + needle_len = strlen(needle); + + if (haystack_len < needle_len || needle_len == 0) + return NULL; + + while (haystack_len >= needle_len) { + if (!in_squote && !in_dquote && + !strncmp(haystack, needle, needle_len)) + return (gchar *)haystack; + + /* 'foo"bar"' -> foo"bar" + "foo'bar'" -> foo'bar' */ + if (*haystack == '\'') { + if (in_squote) + in_squote = FALSE; + else if (!in_dquote) + in_squote = TRUE; + } else if (*haystack == '\"') { + if (in_dquote) + in_dquote = FALSE; + else if (!in_squote) + in_dquote = TRUE; + } + + haystack++; + haystack_len--; + } + + return NULL; +} + +gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl) +{ + const gchar *p; + gchar quote_chr = '"'; + gint in_brace; + gboolean in_quote = FALSE; + + p = str; + + if ((p = strchr_with_skip_quote(p, quote_chr, op))) { + p++; + in_brace = 1; + while (*p) { + if (*p == op && !in_quote) + in_brace++; + else if (*p == cl && !in_quote) + in_brace--; + else if (*p == quote_chr) + in_quote ^= TRUE; + + if (in_brace == 0) + return (gchar *)p; + + p++; + } + } + + return NULL; +} + +gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl, + gint max_tokens) +{ + GSList *string_list = NULL, *slist; + gchar **str_array; + const gchar *s_op, *s_cl; + guint i, n = 1; + + g_return_val_if_fail(str != NULL, NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + s_op = strchr_with_skip_quote(str, '"', op); + if (!s_op) return NULL; + str = s_op; + s_cl = strchr_parenthesis_close(str, op, cl); + if (s_cl) { + do { + guint len; + gchar *new_string; + + str++; + len = s_cl - str; + new_string = g_new(gchar, len + 1); + strncpy(new_string, str, len); + new_string[len] = 0; + string_list = g_slist_prepend(string_list, new_string); + n++; + str = s_cl + 1; + + while (*str && g_ascii_isspace(*str)) str++; + if (*str != op) { + string_list = g_slist_prepend(string_list, + g_strdup("")); + n++; + s_op = strchr_with_skip_quote(str, '"', op); + if (!--max_tokens || !s_op) break; + str = s_op; + } else + s_op = str; + s_cl = strchr_parenthesis_close(str, op, cl); + } while (--max_tokens && s_cl); + } + + str_array = g_new(gchar*, n); + + i = n - 1; + + str_array[i--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[i--] = slist->data; + + g_slist_free(string_list); + + return str_array; +} + +gchar **strsplit_with_quote(const gchar *str, const gchar *delim, + gint max_tokens) +{ + GSList *string_list = NULL, *slist; + gchar **str_array, *s, *new_str; + guint i, n = 1, len; + + g_return_val_if_fail(str != NULL, NULL); + g_return_val_if_fail(delim != NULL, NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + s = strstr_with_skip_quote(str, delim); + if (s) { + guint delimiter_len = strlen(delim); + + do { + len = s - str; + new_str = g_strndup(str, len); + + if (new_str[0] == '\'' || new_str[0] == '\"') { + if (new_str[len - 1] == new_str[0]) { + new_str[len - 1] = '\0'; + memmove(new_str, new_str + 1, len - 1); + } + } + string_list = g_slist_prepend(string_list, new_str); + n++; + str = s + delimiter_len; + s = strstr_with_skip_quote(str, delim); + } while (--max_tokens && s); + } + + if (*str) { + new_str = g_strdup(str); + if (new_str[0] == '\'' || new_str[0] == '\"') { + len = strlen(str); + if (new_str[len - 1] == new_str[0]) { + new_str[len - 1] = '\0'; + memmove(new_str, new_str + 1, len - 1); + } + } + string_list = g_slist_prepend(string_list, new_str); + n++; + } + + str_array = g_new(gchar*, n); + + i = n - 1; + + str_array[i--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[i--] = slist->data; + + g_slist_free(string_list); + + return str_array; +} + +gchar *get_abbrev_newsgroup_name(const gchar *group, gint len) +{ + gchar *abbrev_group; + gchar *ap; + const gchar *p = group; + const gchar *last; + + last = group + strlen(group); + abbrev_group = ap = g_malloc(strlen(group) + 1); + + while (*p) { + while (*p == '.') + *ap++ = *p++; + if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) { + *ap++ = *p++; + while (*p != '.') p++; + } else { + strcpy(ap, p); + return abbrev_group; + } + } + + *ap = '\0'; + return abbrev_group; +} + +gchar *trim_string(const gchar *str, gint len) +{ + const gchar *p = str; + gint mb_len; + gchar *new_str; + gint new_len = 0; + + if (!str) return NULL; + if (strlen(str) <= len) + return g_strdup(str); + if (g_utf8_validate(str, -1, NULL) == FALSE) + return g_strdup(str); + + while (*p != '\0') { + mb_len = g_utf8_skip[*(guchar *)p]; + if (mb_len == 0) + break; + else if (new_len + mb_len > len) + break; + + new_len += mb_len; + p += mb_len; + } + + Xstrndup_a(new_str, str, new_len, return g_strdup(str)); + return g_strconcat(new_str, "...", NULL); +} + +gchar *trim_string_before(const gchar *str, gint len) +{ + const gchar *p = str; + gint mb_len; + gint new_len; + + if (!str) return NULL; + if ((new_len = strlen(str)) <= len) + return g_strdup(str); + if (g_utf8_validate(str, -1, NULL) == FALSE) + return g_strdup(str); + + while (*p != '\0') { + mb_len = g_utf8_skip[*(guchar *)p]; + if (mb_len == 0) + break; + + new_len -= mb_len; + p += mb_len; + + if (new_len <= len) + break; + } + + return g_strconcat("...", p, NULL); +} + +GList *uri_list_extract_filenames(const gchar *uri_list) +{ + GList *result = NULL; + const gchar *p, *q; + gchar *file; + + p = uri_list; + + while (p) { + if (*p != '#') { + while (g_ascii_isspace(*p)) p++; + if (!strncmp(p, "file:", 5)) { + p += 5; + while (*p == '/' && *(p + 1) == '/') p++; + q = p; + while (*q && *q != '\n' && *q != '\r') q++; + + if (q > p) { + q--; + while (q > p && g_ascii_isspace(*q)) + q--; + file = g_malloc(q - p + 2); + strncpy(file, p, q - p + 1); + file[q - p + 1] = '\0'; + decode_uri(file, file); + result = g_list_append(result,file); + } + } + } + p = strchr(p, '\n'); + if (p) p++; + } + + return result; +} + +#define HEX_TO_INT(val, hex) \ +{ \ + gchar c = hex; \ + \ + if ('0' <= c && c <= '9') { \ + val = c - '0'; \ + } else if ('a' <= c && c <= 'f') { \ + val = c - 'a' + 10; \ + } else if ('A' <= c && c <= 'F') { \ + val = c - 'A' + 10; \ + } else { \ + val = 0; \ + } \ +} + +/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped + * characters. + */ +static gint axtoi(const gchar *hex_str) +{ + gint hi, lo; + + HEX_TO_INT(hi, hex_str[0]); + HEX_TO_INT(lo, hex_str[1]); + + return (hi << 4) + lo; +} + +gboolean is_uri_string(const gchar *str) +{ + return (g_ascii_strncasecmp(str, "http://", 7) == 0 || + g_ascii_strncasecmp(str, "https://", 8) == 0 || + g_ascii_strncasecmp(str, "ftp://", 6) == 0 || + g_ascii_strncasecmp(str, "www.", 4) == 0); +} + +gchar *get_uri_path(const gchar *uri) +{ + if (g_ascii_strncasecmp(uri, "http://", 7) == 0) + return (gchar *)(uri + 7); + else if (g_ascii_strncasecmp(uri, "https://", 8) == 0) + return (gchar *)(uri + 8); + else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0) + return (gchar *)(uri + 6); + else + return (gchar *)uri; +} + +gint get_uri_len(const gchar *str) +{ + const gchar *p; + + if (is_uri_string(str)) { + for (p = str; *p != '\0'; p++) { + if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p)) + break; + } + return p - str; + } + + return 0; +} + +/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by + * plusses, and escape characters are used) + * Note: decoded_uri and encoded_uri can point the same location + */ +void decode_uri(gchar *decoded_uri, const gchar *encoded_uri) +{ + gchar *dec = decoded_uri; + const gchar *enc = encoded_uri; + + while (*enc) { + if (*enc == '%') { + enc++; + if (isxdigit((guchar)enc[0]) && + isxdigit((guchar)enc[1])) { + *dec = axtoi(enc); + dec++; + enc += 2; + } + } else { + if (*enc == '+') + *dec = ' '; + else + *dec = *enc; + dec++; + enc++; + } + } + + *dec = '\0'; +} + +gchar *encode_uri(const gchar *filename) +{ + gchar *uri; + + uri = g_filename_to_uri(filename, NULL, NULL); + if (!uri) + uri = g_strconcat("file://", filename, NULL); + + return uri; +} + +gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc, + gchar **subject, gchar **body) +{ + gchar *tmp_mailto; + gchar *p; + + Xstrdup_a(tmp_mailto, mailto, return -1); + + if (!strncmp(tmp_mailto, "mailto:", 7)) + tmp_mailto += 7; + + p = strchr(tmp_mailto, '?'); + if (p) { + *p = '\0'; + p++; + } + + if (to && !*to) + *to = g_strdup(tmp_mailto); + + while (p) { + gchar *field, *value; + + field = p; + + p = strchr(p, '='); + if (!p) break; + *p = '\0'; + p++; + + value = p; + + p = strchr(p, '&'); + if (p) { + *p = '\0'; + p++; + } + + if (*value == '\0') continue; + + if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) { + *cc = g_strdup(value); + } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) { + *bcc = g_strdup(value); + } else if (subject && !*subject && + !g_ascii_strcasecmp(field, "subject")) { + *subject = g_malloc(strlen(value) + 1); + decode_uri(*subject, value); + } else if (body && !*body && + !g_ascii_strcasecmp(field, "body")) { + *body = g_malloc(strlen(value) + 1); + decode_uri(*body, value); + } + } + + return 0; +} + +const gchar *get_home_dir(void) +{ +#ifdef G_OS_WIN32 + static const gchar *home_dir = NULL; + + if (!home_dir) { + home_dir = g_get_home_dir(); + if (!home_dir) + home_dir = "C:\\Sylpheed"; + } + + return home_dir; +#else + return g_get_home_dir(); +#endif +} + +const gchar *get_rc_dir(void) +{ + static gchar *rc_dir = NULL; + + if (!rc_dir) +#ifdef G_OS_WIN32 + rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, + "Application Data", G_DIR_SEPARATOR_S, + RC_DIR, NULL); +#else + rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, + RC_DIR, NULL); +#endif + + return rc_dir; +} + +const gchar *get_old_rc_dir(void) +{ + static gchar *old_rc_dir = NULL; + + if (!old_rc_dir) + old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, + OLD_RC_DIR, NULL); + + return old_rc_dir; +} + +const gchar *get_mail_base_dir(void) +{ +#ifdef G_OS_WIN32 + static gchar *mail_base_dir = NULL; + + if (!mail_base_dir) + mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + "Mailboxes", NULL); + + return mail_base_dir; +#else + return get_home_dir(); +#endif +} + +const gchar *get_news_cache_dir(void) +{ + static gchar *news_cache_dir = NULL; + + if (!news_cache_dir) + news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + NEWS_CACHE_DIR, NULL); + + return news_cache_dir; +} + +const gchar *get_imap_cache_dir(void) +{ + static gchar *imap_cache_dir = NULL; + + if (!imap_cache_dir) + imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + IMAP_CACHE_DIR, NULL); + + return imap_cache_dir; +} + +const gchar *get_mime_tmp_dir(void) +{ + static gchar *mime_tmp_dir = NULL; + + if (!mime_tmp_dir) + mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + MIME_TMP_DIR, NULL); + + return mime_tmp_dir; +} + +const gchar *get_template_dir(void) +{ + static gchar *template_dir = NULL; + + if (!template_dir) + template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + TEMPLATE_DIR, NULL); + + return template_dir; +} + +const gchar *get_tmp_dir(void) +{ + static gchar *tmp_dir = NULL; + + if (!tmp_dir) + tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, + TMP_DIR, NULL); + + return tmp_dir; +} + +gchar *get_tmp_file(void) +{ + gchar *tmp_file; + static guint32 id = 0; + + tmp_file = g_strdup_printf("%s%ctmpfile.%08x", + get_tmp_dir(), G_DIR_SEPARATOR, id++); + + return tmp_file; +} + +const gchar *get_domain_name(void) +{ +#ifdef G_OS_UNIX + static gchar *domain_name = NULL; + + if (!domain_name) { + gchar buf[128] = ""; + struct hostent *hp; + + if (gethostname(buf, sizeof(buf)) < 0) { + perror("gethostname"); + domain_name = "unknown"; + } else { + buf[sizeof(buf) - 1] = '\0'; + if ((hp = my_gethostbyname(buf)) == NULL) { + perror("gethostbyname"); + domain_name = g_strdup(buf); + } else { + domain_name = g_strdup(hp->h_name); + } + } + + debug_print("domain name = %s\n", domain_name); + } + + return domain_name; +#else + return "unknown"; +#endif +} + +off_t get_file_size(const gchar *file) +{ + struct stat s; + + if (g_stat(file, &s) < 0) { + FILE_OP_ERROR(file, "stat"); + return -1; + } + + return s.st_size; +} + +off_t get_file_size_as_crlf(const gchar *file) +{ + FILE *fp; + off_t size = 0; + gchar buf[BUFFSIZE]; + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + while (fgets(buf, sizeof(buf), fp) != NULL) { + strretchomp(buf); + size += strlen(buf) + 2; + } + + if (ferror(fp)) { + FILE_OP_ERROR(file, "fgets"); + size = -1; + } + + fclose(fp); + + return size; +} + +off_t get_left_file_size(FILE *fp) +{ + glong pos; + glong end; + off_t size; + + if ((pos = ftell(fp)) < 0) { + perror("ftell"); + return -1; + } + if (fseek(fp, 0L, SEEK_END) < 0) { + perror("fseek"); + return -1; + } + if ((end = ftell(fp)) < 0) { + perror("fseek"); + return -1; + } + size = end - pos; + if (fseek(fp, pos, SEEK_SET) < 0) { + perror("fseek"); + return -1; + } + + return size; +} + +gboolean file_exist(const gchar *file, gboolean allow_fifo) +{ + struct stat s; + + if (file == NULL) + return FALSE; + + if (g_stat(file, &s) < 0) { + if (ENOENT != errno) FILE_OP_ERROR(file, "stat"); + return FALSE; + } + + if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode))) + return TRUE; + + return FALSE; +} + +gboolean is_dir_exist(const gchar *dir) +{ + if (dir == NULL) + return FALSE; + + return g_file_test(dir, G_FILE_TEST_IS_DIR); +} + +gboolean is_file_entry_exist(const gchar *file) +{ + if (file == NULL) + return FALSE; + + return g_file_test(file, G_FILE_TEST_EXISTS); +} + +gboolean dirent_is_regular_file(struct dirent *d) +{ +#ifdef HAVE_DIRENT_D_TYPE + if (d->d_type == DT_REG) + return TRUE; + else if (d->d_type != DT_UNKNOWN) + return FALSE; +#endif + + return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR); +} + +gboolean dirent_is_directory(struct dirent *d) +{ +#ifdef HAVE_DIRENT_D_TYPE + if (d->d_type == DT_DIR) + return TRUE; + else if (d->d_type != DT_UNKNOWN) + return FALSE; +#endif + + return g_file_test(d->d_name, G_FILE_TEST_IS_DIR); +} + +gint change_dir(const gchar *dir) +{ + gchar *prevdir = NULL; + + if (debug_mode) + prevdir = g_get_current_dir(); + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + if (debug_mode) g_free(prevdir); + return -1; + } else if (debug_mode) { + gchar *cwd; + + cwd = g_get_current_dir(); + if (strcmp(prevdir, cwd) != 0) + g_print("current dir: %s\n", cwd); + g_free(cwd); + g_free(prevdir); + } + + return 0; +} + +gint make_dir(const gchar *dir) +{ + if (g_mkdir(dir, S_IRWXU) < 0) { + FILE_OP_ERROR(dir, "mkdir"); + return -1; + } + if (g_chmod(dir, S_IRWXU) < 0) + FILE_OP_ERROR(dir, "chmod"); + + return 0; +} + +gint make_dir_hier(const gchar *dir) +{ + gchar *parent_dir; + const gchar *p; + + for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) { + parent_dir = g_strndup(dir, p - dir); + if (*parent_dir != '\0') { + if (!is_dir_exist(parent_dir)) { + if (make_dir(parent_dir) < 0) { + g_free(parent_dir); + return -1; + } + } + } + g_free(parent_dir); + } + + if (!is_dir_exist(dir)) { + if (make_dir(dir) < 0) + return -1; + } + + return 0; +} + +gint remove_all_files(const gchar *dir) +{ + GDir *dp; + const gchar *dir_name; + gchar *prev_dir; + + prev_dir = g_get_current_dir(); + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + g_free(prev_dir); + return -1; + } + + if ((dp = g_dir_open(".", 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", dir); + g_free(prev_dir); + return -1; + } + + while ((dir_name = g_dir_read_name(dp)) != NULL) { + if (g_unlink(dir_name) < 0) + FILE_OP_ERROR(dir_name, "unlink"); + } + + g_dir_close(dp); + + if (g_chdir(prev_dir) < 0) { + FILE_OP_ERROR(prev_dir, "chdir"); + g_free(prev_dir); + return -1; + } + + g_free(prev_dir); + + return 0; +} + +gint remove_numbered_files(const gchar *dir, guint first, guint last) +{ + GDir *dp; + const gchar *dir_name; + gchar *prev_dir; + gint file_no; + + prev_dir = g_get_current_dir(); + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + g_free(prev_dir); + return -1; + } + + if ((dp = g_dir_open(".", 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", dir); + g_free(prev_dir); + return -1; + } + + while ((dir_name = g_dir_read_name(dp)) != NULL) { + file_no = to_number(dir_name); + if (file_no > 0 && first <= file_no && file_no <= last) { + if (is_dir_exist(dir_name)) + continue; + if (g_unlink(dir_name) < 0) + FILE_OP_ERROR(dir_name, "unlink"); + } + } + + g_dir_close(dp); + + if (g_chdir(prev_dir) < 0) { + FILE_OP_ERROR(prev_dir, "chdir"); + g_free(prev_dir); + return -1; + } + + g_free(prev_dir); + + return 0; +} + +gint remove_all_numbered_files(const gchar *dir) +{ + return remove_numbered_files(dir, 0, UINT_MAX); +} + +gint remove_expired_files(const gchar *dir, guint hours) +{ + GDir *dp; + const gchar *dir_name; + struct stat s; + gchar *prev_dir; + gint file_no; + time_t mtime, now, expire_time; + + prev_dir = g_get_current_dir(); + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + g_free(prev_dir); + return -1; + } + + if ((dp = g_dir_open(".", 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", dir); + g_free(prev_dir); + return -1; + } + + now = time(NULL); + expire_time = hours * 60 * 60; + + while ((dir_name = g_dir_read_name(dp)) != NULL) { + file_no = to_number(dir_name); + if (file_no > 0) { + if (g_stat(dir_name, &s) < 0) { + FILE_OP_ERROR(dir_name, "stat"); + continue; + } + if (S_ISDIR(s.st_mode)) + continue; + mtime = MAX(s.st_mtime, s.st_atime); + if (now - mtime > expire_time) { + if (g_unlink(dir_name) < 0) + FILE_OP_ERROR(dir_name, "unlink"); + } + } + } + + g_dir_close(dp); + + if (g_chdir(prev_dir) < 0) { + FILE_OP_ERROR(prev_dir, "chdir"); + g_free(prev_dir); + return -1; + } + + g_free(prev_dir); + + return 0; +} + +static gint remove_dir_recursive_real(const gchar *dir) +{ + struct stat s; + GDir *dp; + const gchar *dir_name; + gchar *prev_dir; + + if (g_stat(dir, &s) < 0) { + FILE_OP_ERROR(dir, "stat"); + if (ENOENT == errno) return 0; + return -1; + } + + if (!S_ISDIR(s.st_mode)) { + if (g_unlink(dir) < 0) { + FILE_OP_ERROR(dir, "unlink"); + return -1; + } + + return 0; + } + + prev_dir = g_get_current_dir(); + /* g_print("prev_dir = %s\n", prev_dir); */ + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + g_free(prev_dir); + return -1; + } + + if ((dp = g_dir_open(".", 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", dir); + g_chdir(prev_dir); + g_free(prev_dir); + return -1; + } + + /* remove all files in the directory */ + while ((dir_name = g_dir_read_name(dp)) != NULL) { + /* g_print("removing %s\n", dir_name); */ + + if (is_dir_exist(dir_name)) { + if (remove_dir_recursive_real(dir_name) < 0) { + g_warning("can't remove directory\n"); + return -1; + } + } else { + if (g_unlink(dir_name) < 0) + FILE_OP_ERROR(dir_name, "unlink"); + } + } + + g_dir_close(dp); + + if (g_chdir(prev_dir) < 0) { + FILE_OP_ERROR(prev_dir, "chdir"); + g_free(prev_dir); + return -1; + } + + g_free(prev_dir); + + if (g_rmdir(dir) < 0) { + FILE_OP_ERROR(dir, "rmdir"); + return -1; + } + + return 0; +} + +gint remove_dir_recursive(const gchar *dir) +{ + gchar *cur_dir; + gint ret; + + cur_dir = g_get_current_dir(); + + if (g_chdir(dir) < 0) { + FILE_OP_ERROR(dir, "chdir"); + ret = -1; + goto leave; + } + if (g_chdir("..") < 0) { + FILE_OP_ERROR(dir, "chdir"); + ret = -1; + goto leave; + } + + ret = remove_dir_recursive_real(dir); + +leave: + if (is_dir_exist(cur_dir)) { + if (g_chdir(cur_dir) < 0) { + FILE_OP_ERROR(cur_dir, "chdir"); + } + } + + g_free(cur_dir); + + return ret; +} + +gint rename_force(const gchar *oldpath, const gchar *newpath) +{ +#ifndef G_OS_UNIX + if (!is_file_entry_exist(oldpath)) { + errno = ENOENT; + return -1; + } + if (is_file_exist(newpath)) { + if (g_unlink(newpath) < 0) + FILE_OP_ERROR(newpath, "unlink"); + } +#endif + return g_rename(oldpath, newpath); +} + +gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup) +{ + FILE *src_fp, *dest_fp; + gint n_read; + gchar buf[BUFSIZ]; + gchar *dest_bak = NULL; + gboolean err = FALSE; + + if ((src_fp = g_fopen(src, "rb")) == NULL) { + FILE_OP_ERROR(src, "fopen"); + return -1; + } + if (is_file_exist(dest)) { + dest_bak = g_strconcat(dest, ".bak", NULL); + if (rename_force(dest, dest_bak) < 0) { + FILE_OP_ERROR(dest, "rename"); + fclose(src_fp); + g_free(dest_bak); + return -1; + } + } + + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + fclose(src_fp); + if (dest_bak) { + if (rename_force(dest_bak, dest) < 0) + FILE_OP_ERROR(dest_bak, "rename"); + g_free(dest_bak); + } + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning(_("can't change file mode\n")); + } + + while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) { + if (n_read < sizeof(buf) && ferror(src_fp)) + break; + if (fwrite(buf, n_read, 1, dest_fp) < 1) { + g_warning(_("writing to %s failed.\n"), dest); + fclose(dest_fp); + fclose(src_fp); + g_unlink(dest); + if (dest_bak) { + if (rename_force(dest_bak, dest) < 0) + FILE_OP_ERROR(dest_bak, "rename"); + g_free(dest_bak); + } + return -1; + } + } + + if (ferror(src_fp)) { + FILE_OP_ERROR(src, "fread"); + err = TRUE; + } + fclose(src_fp); + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); + err = TRUE; + } + + if (err) { + g_unlink(dest); + if (dest_bak) { + if (rename_force(dest_bak, dest) < 0) + FILE_OP_ERROR(dest_bak, "rename"); + g_free(dest_bak); + } + return -1; + } + + if (keep_backup == FALSE && dest_bak) + g_unlink(dest_bak); + + g_free(dest_bak); + + return 0; +} + +gint copy_dir(const gchar *src, const gchar *dest) +{ + GDir *dir; + const gchar *dir_name; + gchar *src_file; + gchar *dest_file; + + if ((dir = g_dir_open(src, 0, NULL)) == NULL) { + g_warning("failed to open directory: %s\n", src); + return -1; + } + + if (make_dir_hier(dest) < 0) { + g_dir_close(dir); + return -1; + } + + while ((dir_name = g_dir_read_name(dir)) != NULL) { + src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); + dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, + NULL); + if (is_file_exist(src_file)) + copy_file(src_file, dest_file, FALSE); + g_free(dest_file); + g_free(src_file); + } + + g_dir_close(dir); + + return 0; +} + +gint move_file(const gchar *src, const gchar *dest, gboolean overwrite) +{ + if (overwrite == FALSE && is_file_exist(dest)) { + g_warning("move_file(): file %s already exists.", dest); + return -1; + } + + if (rename_force(src, dest) == 0) return 0; + + if (EXDEV != errno) { + FILE_OP_ERROR(src, "rename"); + return -1; + } + + if (copy_file(src, dest, FALSE) < 0) return -1; + + g_unlink(src); + + return 0; +} + +gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) +{ + FILE *dest_fp; + gint n_read; + gint bytes_left, to_read; + gchar buf[BUFSIZ]; + gboolean err = FALSE; + + if (fseek(fp, offset, SEEK_SET) < 0) { + perror("fseek"); + return -1; + } + + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning("can't change file mode\n"); + } + + bytes_left = length; + to_read = MIN(bytes_left, sizeof(buf)); + + while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) { + if (n_read < to_read && ferror(fp)) + break; + if (fwrite(buf, n_read, 1, dest_fp) < 1) { + g_warning(_("writing to %s failed.\n"), dest); + fclose(dest_fp); + g_unlink(dest); + return -1; + } + bytes_left -= n_read; + if (bytes_left == 0) + break; + to_read = MIN(bytes_left, sizeof(buf)); + } + + if (ferror(fp)) { + perror("fread"); + err = TRUE; + } + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); + err = TRUE; + } + + if (err) { + g_unlink(dest); + return -1; + } + + return 0; +} + +/* convert line endings into CRLF. If the last line doesn't end with + * linebreak, add it. + */ +gchar *canonicalize_str(const gchar *str) +{ + const gchar *p; + guint new_len = 0; + gchar *out, *outp; + + for (p = str; *p != '\0'; ++p) { + if (*p != '\r') { + ++new_len; + if (*p == '\n') + ++new_len; + } + } + if (p == str || *(p - 1) != '\n') + new_len += 2; + + out = outp = g_malloc(new_len + 1); + for (p = str; *p != '\0'; ++p) { + if (*p != '\r') { + if (*p == '\n') + *outp++ = '\r'; + *outp++ = *p; + } + } + if (p == str || *(p - 1) != '\n') { + *outp++ = '\r'; + *outp++ = '\n'; + } + *outp = '\0'; + + return out; +} + +gint canonicalize_file(const gchar *src, const gchar *dest) +{ + FILE *src_fp, *dest_fp; + gchar buf[BUFFSIZE]; + gint len; + gboolean err = FALSE; + gboolean last_linebreak = FALSE; + + if ((src_fp = g_fopen(src, "rb")) == NULL) { + FILE_OP_ERROR(src, "fopen"); + return -1; + } + + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + fclose(src_fp); + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning("can't change file mode\n"); + } + + while (fgets(buf, sizeof(buf), src_fp) != NULL) { + gint r = 0; + + len = strlen(buf); + if (len == 0) break; + last_linebreak = FALSE; + + if (buf[len - 1] != '\n') { + last_linebreak = TRUE; + r = fputs(buf, dest_fp); + } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') { + r = fputs(buf, dest_fp); + } else { + if (len > 1) { + r = fwrite(buf, len - 1, 1, dest_fp); + if (r != 1) + r = EOF; + } + if (r != EOF) + r = fputs("\r\n", dest_fp); + } + + if (r == EOF) { + g_warning("writing to %s failed.\n", dest); + fclose(dest_fp); + fclose(src_fp); + g_unlink(dest); + return -1; + } + } + + if (last_linebreak == TRUE) { + if (fputs("\r\n", dest_fp) == EOF) + err = TRUE; + } + + if (ferror(src_fp)) { + FILE_OP_ERROR(src, "fgets"); + err = TRUE; + } + fclose(src_fp); + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); + err = TRUE; + } + + if (err) { + g_unlink(dest); + return -1; + } + + return 0; +} + +gint canonicalize_file_replace(const gchar *file) +{ + gchar *tmp_file; + + tmp_file = get_tmp_file(); + + if (canonicalize_file(file, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (move_file(tmp_file, file, TRUE) < 0) { + g_warning("can't replace %s .\n", file); + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_free(tmp_file); + return 0; +} + +gint uncanonicalize_file(const gchar *src, const gchar *dest) +{ + FILE *src_fp, *dest_fp; + gchar buf[BUFFSIZE]; + gboolean err = FALSE; + + if ((src_fp = g_fopen(src, "rb")) == NULL) { + FILE_OP_ERROR(src, "fopen"); + return -1; + } + + if ((dest_fp = g_fopen(dest, "wb")) == NULL) { + FILE_OP_ERROR(dest, "fopen"); + fclose(src_fp); + return -1; + } + + if (change_file_mode_rw(dest_fp, dest) < 0) { + FILE_OP_ERROR(dest, "chmod"); + g_warning("can't change file mode\n"); + } + + while (fgets(buf, sizeof(buf), src_fp) != NULL) { + strcrchomp(buf); + if (fputs(buf, dest_fp) == EOF) { + g_warning("writing to %s failed.\n", dest); + fclose(dest_fp); + fclose(src_fp); + g_unlink(dest); + return -1; + } + } + + if (ferror(src_fp)) { + FILE_OP_ERROR(src, "fgets"); + err = TRUE; + } + fclose(src_fp); + if (fclose(dest_fp) == EOF) { + FILE_OP_ERROR(dest, "fclose"); + err = TRUE; + } + + if (err) { + g_unlink(dest); + return -1; + } + + return 0; +} + +gint uncanonicalize_file_replace(const gchar *file) +{ + gchar *tmp_file; + + tmp_file = get_tmp_file(); + + if (uncanonicalize_file(file, tmp_file) < 0) { + g_free(tmp_file); + return -1; + } + + if (move_file(tmp_file, file, TRUE) < 0) { + g_warning("can't replace %s .\n", file); + g_unlink(tmp_file); + g_free(tmp_file); + return -1; + } + + g_free(tmp_file); + return 0; +} + +gchar *normalize_newlines(const gchar *str) +{ + const gchar *p = str; + gchar *out, *outp; + + out = outp = g_malloc(strlen(str) + 1); + for (p = str; *p != '\0'; ++p) { + if (*p == '\r') { + if (*(p + 1) != '\n') + *outp++ = '\n'; + } else + *outp++ = *p; + } + + *outp = '\0'; + + return out; +} + +gchar *get_outgoing_rfc2822_str(FILE *fp) +{ + gchar buf[BUFFSIZE]; + GString *str; + gchar *ret; + + str = g_string_new(NULL); + + /* output header part */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + strretchomp(buf); + if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) { + gint next; + + for (;;) { + next = fgetc(fp); + if (next == EOF) + break; + else if (next != ' ' && next != '\t') { + ungetc(next, fp); + break; + } + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + } +#if 0 + } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) { + get_rfc822_date(buf, sizeof(buf)); + g_string_append_printf(str, "Date: %s\r\n", buf); +#endif + } else { + g_string_append(str, buf); + g_string_append(str, "\r\n"); + if (buf[0] == '\0') + break; + } + } + + /* output body part */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + strretchomp(buf); + if (buf[0] == '.') + g_string_append_c(str, '.'); + g_string_append(str, buf); + g_string_append(str, "\r\n"); + } + + ret = str->str; + g_string_free(str, FALSE); + + return ret; +} + +/* + * Create a new boundary in a way that it is very unlikely that this + * will occur in the following text. It would be easy to ensure + * uniqueness if everything is either quoted-printable or base64 + * encoded (note that conversion is allowed), but because MIME bodies + * may be nested, it may happen that the same boundary has already + * been used. We avoid scanning the message for conflicts and hope the + * best. + * + * boundary := 0*69 bcharsnospace + * bchars := bcharsnospace / " " + * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / + * "+" / "_" / "," / "-" / "." / + * "/" / ":" / "=" / "?" + * + * some special characters removed because of buggy MTAs + */ + +gchar *generate_mime_boundary(const gchar *prefix) +{ + static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "1234567890+_./="; + gchar buf_uniq[17]; + gchar buf_date[64]; + gint i; + + for (i = 0; i < sizeof(buf_uniq) - 1; i++) + buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)]; + buf_uniq[i] = '\0'; + + get_rfc822_date(buf_date, sizeof(buf_date)); + subst_char(buf_date, ' ', '_'); + subst_char(buf_date, ',', '_'); + subst_char(buf_date, ':', '_'); + + return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart", + buf_date, buf_uniq); +} + +gint change_file_mode_rw(FILE *fp, const gchar *file) +{ +#if HAVE_FCHMOD + return fchmod(fileno(fp), S_IRUSR|S_IWUSR); +#else + return g_chmod(file, S_IRUSR|S_IWUSR); +#endif +} + +FILE *my_tmpfile(void) +{ +#if HAVE_MKSTEMP + const gchar suffix[] = ".XXXXXX"; + const gchar *tmpdir; + guint tmplen; + const gchar *progname; + guint proglen; + gchar *fname; + gint fd; + FILE *fp; + + tmpdir = get_tmp_dir(); + tmplen = strlen(tmpdir); + progname = g_get_prgname(); + proglen = strlen(progname); + Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix), + return tmpfile()); + + memcpy(fname, tmpdir, tmplen); + fname[tmplen] = G_DIR_SEPARATOR; + memcpy(fname + tmplen + 1, progname, proglen); + memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix)); + + fd = mkstemp(fname); + if (fd < 0) + return tmpfile(); + + g_unlink(fname); + + fp = fdopen(fd, "w+b"); + if (!fp) + close(fd); + else + return fp; +#endif /* HAVE_MKSTEMP */ + + return tmpfile(); +} + +FILE *str_open_as_stream(const gchar *str) +{ + FILE *fp; + size_t len; + + g_return_val_if_fail(str != NULL, NULL); + + fp = my_tmpfile(); + if (!fp) { + FILE_OP_ERROR("str_open_as_stream", "my_tmpfile"); + return NULL; + } + + len = strlen(str); + if (len == 0) return fp; + + if (fwrite(str, len, 1, fp) != 1) { + FILE_OP_ERROR("str_open_as_stream", "fwrite"); + fclose(fp); + return NULL; + } + + rewind(fp); + return fp; +} + +gint str_write_to_file(const gchar *str, const gchar *file) +{ + FILE *fp; + size_t len; + + g_return_val_if_fail(str != NULL, -1); + g_return_val_if_fail(file != NULL, -1); + + if ((fp = g_fopen(file, "wb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return -1; + } + + len = strlen(str); + if (len == 0) { + fclose(fp); + return 0; + } + + if (fwrite(str, len, 1, fp) != 1) { + FILE_OP_ERROR(file, "fwrite"); + fclose(fp); + g_unlink(file); + return -1; + } + + if (fclose(fp) == EOF) { + FILE_OP_ERROR(file, "fclose"); + g_unlink(file); + return -1; + } + + return 0; +} + +gchar *file_read_to_str(const gchar *file) +{ + FILE *fp; + gchar *str; + + g_return_val_if_fail(file != NULL, NULL); + + if ((fp = g_fopen(file, "rb")) == NULL) { + FILE_OP_ERROR(file, "fopen"); + return NULL; + } + + str = file_read_stream_to_str(fp); + + fclose(fp); + + return str; +} + +gchar *file_read_stream_to_str(FILE *fp) +{ + GByteArray *array; + guchar buf[BUFSIZ]; + gint n_read; + gchar *str; + + g_return_val_if_fail(fp != NULL, NULL); + + array = g_byte_array_new(); + + while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { + if (n_read < sizeof(buf) && ferror(fp)) + break; + g_byte_array_append(array, buf, n_read); + } + + if (ferror(fp)) { + FILE_OP_ERROR("file stream", "fread"); + g_byte_array_free(array, TRUE); + return NULL; + } + + buf[0] = '\0'; + g_byte_array_append(array, buf, 1); + str = (gchar *)array->data; + g_byte_array_free(array, FALSE); + + return str; +} + +gint execute_async(gchar *const argv[]) +{ + g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); + + if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, FALSE) == FALSE) { + g_warning("Can't execute command: %s\n", argv[0]); + return -1; + } + + return 0; +} + +gint execute_sync(gchar *const argv[]) +{ + gint status; + + g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); + + if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL, &status, NULL) == FALSE) { + g_warning("Can't execute command: %s\n", argv[0]); + return -1; + } + +#ifdef G_OS_UNIX + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else + return -1; +#else + return status; +#endif +} + +gint execute_command_line(const gchar *cmdline, gboolean async) +{ + gchar **argv; + gint ret; + + debug_print("execute_command_line(): executing: %s\n", cmdline); + + argv = strsplit_with_quote(cmdline, " ", 0); + + if (async) + ret = execute_async(argv); + else + ret = execute_sync(argv); + + g_strfreev(argv); + + return ret; +} + +gchar *get_command_output(const gchar *cmdline) +{ + gchar *child_stdout; + gint status; + + g_return_val_if_fail(cmdline != NULL, NULL); + + debug_print("get_command_output(): executing: %s\n", cmdline); + + if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status, + NULL) == FALSE) { + g_warning("Can't execute command: %s\n", cmdline); + return NULL; + } + + return child_stdout; +} + +gint open_uri(const gchar *uri, const gchar *cmdline) +{ + gchar buf[BUFFSIZE]; + gchar *p; + + g_return_val_if_fail(uri != NULL, -1); + + if (cmdline && + (p = strchr(cmdline, '%')) && *(p + 1) == 's' && + !strchr(p + 2, '%')) + g_snprintf(buf, sizeof(buf), cmdline, uri); + else { + if (cmdline) + g_warning("Open URI command line is invalid " + "(there must be only one '%%s'): %s", + cmdline); + g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri); + } + + execute_command_line(buf, TRUE); + + return 0; +} + +time_t remote_tzoffset_sec(const gchar *zone) +{ + static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT"; + gchar zone3[4]; + gchar *p; + gchar c; + gint iustz; + gint offset; + time_t remoteoffset; + + strncpy(zone3, zone, 3); + zone3[3] = '\0'; + remoteoffset = 0; + + if (sscanf(zone, "%c%d", &c, &offset) == 2 && + (c == '+' || c == '-')) { + remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60; + if (c == '-') + remoteoffset = -remoteoffset; + } else if (!strncmp(zone, "UT" , 2) || + !strncmp(zone, "GMT", 2)) { + remoteoffset = 0; + } else if (strlen(zone3) == 3) { + for (p = ustzstr; *p != '\0'; p += 3) { + if (!g_ascii_strncasecmp(p, zone3, 3)) { + iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8; + remoteoffset = iustz * 3600; + break; + } + } + if (*p == '\0') + return -1; + } else if (strlen(zone3) == 1) { + switch (zone[0]) { + case 'Z': remoteoffset = 0; break; + case 'A': remoteoffset = -1; break; + case 'B': remoteoffset = -2; break; + case 'C': remoteoffset = -3; break; + case 'D': remoteoffset = -4; break; + case 'E': remoteoffset = -5; break; + case 'F': remoteoffset = -6; break; + case 'G': remoteoffset = -7; break; + case 'H': remoteoffset = -8; break; + case 'I': remoteoffset = -9; break; + case 'K': remoteoffset = -10; break; /* J is not used */ + case 'L': remoteoffset = -11; break; + case 'M': remoteoffset = -12; break; + case 'N': remoteoffset = 1; break; + case 'O': remoteoffset = 2; break; + case 'P': remoteoffset = 3; break; + case 'Q': remoteoffset = 4; break; + case 'R': remoteoffset = 5; break; + case 'S': remoteoffset = 6; break; + case 'T': remoteoffset = 7; break; + case 'U': remoteoffset = 8; break; + case 'V': remoteoffset = 9; break; + case 'W': remoteoffset = 10; break; + case 'X': remoteoffset = 11; break; + case 'Y': remoteoffset = 12; break; + default: remoteoffset = 0; break; + } + remoteoffset = remoteoffset * 3600; + } else + return -1; + + return remoteoffset; +} + +time_t tzoffset_sec(time_t *now) +{ + struct tm gmt, *lt; + gint off; + + gmt = *gmtime(now); + lt = localtime(now); + + off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; + + if (lt->tm_year < gmt.tm_year) + off -= 24 * 60; + else if (lt->tm_year > gmt.tm_year) + off += 24 * 60; + else if (lt->tm_yday < gmt.tm_yday) + off -= 24 * 60; + else if (lt->tm_yday > gmt.tm_yday) + off += 24 * 60; + + if (off >= 24 * 60) /* should be impossible */ + off = 23 * 60 + 59; /* if not, insert silly value */ + if (off <= -24 * 60) + off = -(23 * 60 + 59); + + return off * 60; +} + +/* calculate timezone offset */ +gchar *tzoffset(time_t *now) +{ + static gchar offset_string[6]; + struct tm gmt, *lt; + gint off; + gchar sign = '+'; + + gmt = *gmtime(now); + lt = localtime(now); + + off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; + + if (lt->tm_year < gmt.tm_year) + off -= 24 * 60; + else if (lt->tm_year > gmt.tm_year) + off += 24 * 60; + else if (lt->tm_yday < gmt.tm_yday) + off -= 24 * 60; + else if (lt->tm_yday > gmt.tm_yday) + off += 24 * 60; + + if (off < 0) { + sign = '-'; + off = -off; + } + + if (off >= 24 * 60) /* should be impossible */ + off = 23 * 60 + 59; /* if not, insert silly value */ + + sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60); + + return offset_string; +} + +void get_rfc822_date(gchar *buf, gint len) +{ + struct tm *lt; + time_t t; + gchar day[4], mon[4]; + gint dd, hh, mm, ss, yyyy; + + t = time(NULL); + lt = localtime(&t); + + sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n", + day, mon, &dd, &hh, &mm, &ss, &yyyy); + g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s", + day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t)); +} + +/* just a wrapper to suppress the warning of gcc about %c */ +size_t my_strftime(gchar *s, size_t max, const gchar *format, + const struct tm *tm) +{ + return strftime(s, max, format, tm); +} + +static FILE *log_fp = NULL; + +void set_log_file(const gchar *filename) +{ + if (log_fp) return; + log_fp = g_fopen(filename, "wb"); + if (!log_fp) + FILE_OP_ERROR(filename, "fopen"); +} + +void close_log_file(void) +{ + if (log_fp) { + fclose(log_fp); + log_fp = NULL; + } +} + +static guint log_verbosity_count = 0; + +void set_log_verbosity(gboolean verbose) +{ + if (verbose) + log_verbosity_count++; + else if (log_verbosity_count > 0) + log_verbosity_count--; +} + +gboolean get_debug_mode(void) +{ + return debug_mode; +} + +void set_debug_mode(gboolean enable) +{ + debug_mode = enable; +} + +static void log_dummy_func(const gchar *str) +{ +} + +static LogFunc log_print_ui_func = log_dummy_func; +static LogFunc log_message_ui_func = log_dummy_func; +static LogFunc log_warning_ui_func = log_dummy_func; +static LogFunc log_error_ui_func = log_dummy_func; + +static LogFunc log_show_status_func = log_dummy_func; + +void set_log_ui_func(LogFunc print_func, LogFunc message_func, + LogFunc warning_func, LogFunc error_func) +{ + log_print_ui_func = print_func; + log_message_ui_func = message_func; + log_warning_ui_func = warning_func; + log_error_ui_func = error_func; +} + +void set_log_show_status_func(LogFunc status_func) +{ + log_show_status_func = status_func; +} + +void debug_print(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE]; + + if (!debug_mode) return; + + va_start(args, format); + g_vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + + g_print("%s", buf); +} + +#define TIME_LEN 11 + +void log_print(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE + TIME_LEN]; + time_t t; + + time(&t); + strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); + + va_start(args, format); + g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); + va_end(args); + + if (debug_mode) fputs(buf, stdout); + log_print_ui_func(buf); + if (log_fp) { + fputs(buf, log_fp); + fflush(log_fp); + } + if (log_verbosity_count) + log_show_status_func(buf + TIME_LEN); +} + +void log_message(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE + TIME_LEN]; + time_t t; + + time(&t); + strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); + + va_start(args, format); + g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); + va_end(args); + + if (debug_mode) g_message("%s", buf + TIME_LEN); + log_message_ui_func(buf + TIME_LEN); + if (log_fp) { + fwrite(buf, TIME_LEN, 1, log_fp); + fputs("* message: ", log_fp); + fputs(buf + TIME_LEN, log_fp); + fflush(log_fp); + } + log_show_status_func(buf + TIME_LEN); +} + +void log_warning(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE + TIME_LEN]; + time_t t; + + time(&t); + strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); + + va_start(args, format); + g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); + va_end(args); + + g_warning("%s", buf); + log_warning_ui_func(buf + TIME_LEN); + if (log_fp) { + fwrite(buf, TIME_LEN, 1, log_fp); + fputs("** warning: ", log_fp); + fputs(buf + TIME_LEN, log_fp); + fflush(log_fp); + } +} + +void log_error(const gchar *format, ...) +{ + va_list args; + gchar buf[BUFFSIZE + TIME_LEN]; + time_t t; + + time(&t); + strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); + + va_start(args, format); + g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); + va_end(args); + + g_warning("%s", buf); + log_error_ui_func(buf + TIME_LEN); + if (log_fp) { + fwrite(buf, TIME_LEN, 1, log_fp); + fputs("*** error: ", log_fp); + fputs(buf + TIME_LEN, log_fp); + fflush(log_fp); + } +} diff --git a/libsylph/utils.h b/libsylph/utils.h new file mode 100644 index 00000000..fbda26a3 --- /dev/null +++ b/libsylph/utils.h @@ -0,0 +1,493 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __UTILS_H__ +#define __UTILS_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_ALLOCA_H +# include +#endif +#if HAVE_WCHAR_H +# include +#endif + +/* Wrappers for C library function that take pathname arguments. */ +#if GLIB_CHECK_VERSION(2, 6, 0) +# include +#else + +#define g_open open +#define g_rename rename +#define g_mkdir mkdir +#define g_stat stat +#define g_lstat lstat +#define g_unlink unlink +#define g_remove remove +#define g_rmdir rmdir +#define g_fopen fopen +#define g_freopen freopen + +#endif /* GLIB_CHECK_VERSION */ + +#if !GLIB_CHECK_VERSION(2, 7, 0) + +#ifdef G_OS_UNIX +#define g_chdir chdir +#define g_chmod chmod +#else +gint g_chdir (const gchar *path); +gint g_chmod (const gchar *path, + gint mode); +#endif /* G_OS_UNIX */ + +#endif /* !GLIB_CHECK_VERSION */ + +/* The AC_CHECK_SIZEOF() in configure fails for some machines. + * we provide some fallback values here */ +#if !SIZEOF_UNSIGNED_SHORT + #undef SIZEOF_UNSIGNED_SHORT + #define SIZEOF_UNSIGNED_SHORT 2 +#endif +#if !SIZEOF_UNSIGNED_INT + #undef SIZEOF_UNSIGNED_INT + #define SIZEOF_UNSIGNED_INT 4 +#endif +#if !SIZEOF_UNSIGNED_LONG + #undef SIZEOF_UNSIGNED_LONG + #define SIZEOF_UNSIGNED_LONG 4 +#endif + +#ifndef HAVE_U32_TYPEDEF + #undef u32 /* maybe there is a macro with this name */ + typedef guint32 u32; + #define HAVE_U32_TYPEDEF +#endif + +#ifndef BIG_ENDIAN_HOST + #if (G_BYTE_ORDER == G_BIG_ENDIAN) + #define BIG_ENDIAN_HOST 1 + #endif +#endif + +#define CHDIR_RETURN_IF_FAIL(dir) \ +{ \ + if (change_dir(dir) < 0) return; \ +} + +#define CHDIR_RETURN_VAL_IF_FAIL(dir, val) \ +{ \ + if (change_dir(dir) < 0) return val; \ +} + +#define Xalloca(ptr, size, iffail) \ +{ \ + if ((ptr = alloca(size)) == NULL) { \ + g_warning("can't allocate memory\n"); \ + iffail; \ + } \ +} + +#define Xstrdup_a(ptr, str, iffail) \ +{ \ + gchar *__tmp; \ + \ + if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \ + g_warning("can't allocate memory\n"); \ + iffail; \ + } else \ + strcpy(__tmp, str); \ + \ + ptr = __tmp; \ +} + +#define Xstrndup_a(ptr, str, len, iffail) \ +{ \ + gchar *__tmp; \ + \ + if ((__tmp = alloca(len + 1)) == NULL) { \ + g_warning("can't allocate memory\n"); \ + iffail; \ + } else { \ + strncpy(__tmp, str, len); \ + __tmp[len] = '\0'; \ + } \ + \ + ptr = __tmp; \ +} + +#define Xstrcat_a(ptr, str1, str2, iffail) \ +{ \ + gchar *__tmp; \ + gint len1, len2; \ + \ + len1 = strlen(str1); \ + len2 = strlen(str2); \ + if ((__tmp = alloca(len1 + len2 + 1)) == NULL) { \ + g_warning("can't allocate memory\n"); \ + iffail; \ + } else { \ + memcpy(__tmp, str1, len1); \ + memcpy(__tmp + len1, str2, len2 + 1); \ + } \ + \ + ptr = __tmp; \ +} + +#define AUTORELEASE_STR(str, iffail) \ +{ \ + gchar *__str; \ + Xstrdup_a(__str, str, iffail); \ + g_free(str); \ + str = __str; \ +} + +#define FILE_OP_ERROR(file, func) \ +{ \ + fprintf(stderr, "%s: ", file); \ + fflush(stderr); \ + perror(func); \ +} + +typedef void (*LogFunc) (const gchar *str); + +/* for macro expansion */ +#define Str(x) #x +#define Xstr(x) Str(x) + +void list_free_strings (GList *list); +void slist_free_strings (GSList *list); + +void hash_free_strings (GHashTable *table); +void hash_free_value_mem (GHashTable *table); + +gint str_case_equal (gconstpointer v, + gconstpointer v2); +guint str_case_hash (gconstpointer key); + +void ptr_array_free_strings (GPtrArray *array); + +typedef gboolean (*StrFindFunc) (const gchar *haystack, + const gchar *needle); + +gboolean str_find (const gchar *haystack, + const gchar *needle); +gboolean str_case_find (const gchar *haystack, + const gchar *needle); +gboolean str_find_equal (const gchar *haystack, + const gchar *needle); +gboolean str_case_find_equal (const gchar *haystack, + const gchar *needle); + +/* number-string conversion */ +gint to_number (const gchar *nstr); +gchar *itos_buf (gchar *nstr, + gint n); +gchar *itos (gint n); +gchar *to_human_readable (off_t size); + +/* alternative string functions */ +gint strcmp2 (const gchar *s1, + const gchar *s2); +gint path_cmp (const gchar *s1, + const gchar *s2); +gchar *strretchomp (gchar *str); +gchar *strtailchomp (gchar *str, + gchar tail_char); +gchar *strcrchomp (gchar *str); +gchar *strcasestr (const gchar *haystack, + const gchar *needle); +gpointer my_memmem (gconstpointer haystack, + size_t haystacklen, + gconstpointer needle, + size_t needlelen); +gchar *strncpy2 (gchar *dest, + const gchar *src, + size_t n); + +/* wide-character functions */ +#if !HAVE_ISWALNUM +int iswalnum (wint_t wc); +#endif +#if !HAVE_ISWSPACE +int iswspace (wint_t wc); +#endif +#if !HAVE_TOWLOWER +wint_t towlower (wint_t wc); +#endif + +#if !HAVE_WCSLEN +size_t wcslen (const wchar_t *s); +#endif +#if !HAVE_WCSCPY +wchar_t *wcscpy (wchar_t *dest, + const wchar_t *src); +#endif +#if !HAVE_WCSNCPY +wchar_t *wcsncpy (wchar_t *dest, + const wchar_t *src, + size_t n); +#endif + +wchar_t *wcsdup (const wchar_t *s); +wchar_t *wcsndup (const wchar_t *s, + size_t n); +wchar_t *strdup_mbstowcs (const gchar *s); +gchar *strdup_wcstombs (const wchar_t *s); +gint wcsncasecmp (const wchar_t *s1, + const wchar_t *s2, + size_t n); +wchar_t *wcscasestr (const wchar_t *haystack, + const wchar_t *needle); +gint get_mbs_len (const gchar *s); + +gboolean is_next_nonascii (const gchar *s); +gint get_next_word_len (const gchar *s); + +/* functions for string parsing */ +gint subject_compare (const gchar *s1, + const gchar *s2); +gint subject_compare_for_sort (const gchar *s1, + const gchar *s2); +void trim_subject_for_compare (gchar *str); +void trim_subject_for_sort (gchar *str); +void trim_subject (gchar *str); +void eliminate_parenthesis (gchar *str, + gchar op, + gchar cl); +void extract_parenthesis (gchar *str, + gchar op, + gchar cl); + +void extract_parenthesis_with_skip_quote (gchar *str, + gchar quote_chr, + gchar op, + gchar cl); + +void eliminate_quote (gchar *str, + gchar quote_chr); +void extract_quote (gchar *str, + gchar quote_chr); +void eliminate_address_comment (gchar *str); +gchar *strchr_with_skip_quote (const gchar *str, + gint quote_chr, + gint c); +gchar *strrchr_with_skip_quote (const gchar *str, + gint quote_chr, + gint c); +void extract_address (gchar *str); +void extract_list_id_str (gchar *str); + +GSList *address_list_append (GSList *addr_list, + const gchar *str); +GSList *references_list_prepend (GSList *msgid_list, + const gchar *str); +GSList *references_list_append (GSList *msgid_list, + const gchar *str); +GSList *newsgroup_list_append (GSList *group_list, + const gchar *str); + +GList *add_history (GList *list, + const gchar *str); + +void remove_return (gchar *str); +void remove_space (gchar *str); +void unfold_line (gchar *str); +void subst_char (gchar *str, + gchar orig, + gchar subst); +void subst_chars (gchar *str, + gchar *orig, + gchar subst); +void subst_null (gchar *str, + gint len, + gchar subst); +void subst_for_filename (gchar *str); +gboolean is_header_line (const gchar *str); +gboolean is_ascii_str (const gchar *str); +gint get_quote_level (const gchar *str); +gint check_line_length (const gchar *str, + gint max_chars, + gint *line); + +gchar *strstr_with_skip_quote (const gchar *haystack, + const gchar *needle); +gchar *strchr_parenthesis_close (const gchar *str, + gchar op, + gchar cl); + +gchar **strsplit_parenthesis (const gchar *str, + gchar op, + gchar cl, + gint max_tokens); +gchar **strsplit_with_quote (const gchar *str, + const gchar *delim, + gint max_tokens); + +gchar *get_abbrev_newsgroup_name (const gchar *group, + gint len); +gchar *trim_string (const gchar *str, + gint len); +gchar *trim_string_before (const gchar *str, + gint len); + +GList *uri_list_extract_filenames (const gchar *uri_list); +gboolean is_uri_string (const gchar *str); +gchar *get_uri_path (const gchar *uri); +gint get_uri_len (const gchar *str); +void decode_uri (gchar *decoded_uri, + const gchar *encoded_uri); +gchar *encode_uri (const gchar *filename); +gint scan_mailto_url (const gchar *mailto, + gchar **to, + gchar **cc, + gchar **bcc, + gchar **subject, + gchar **body); + +/* return static strings */ +const gchar *get_home_dir (void); +const gchar *get_rc_dir (void); +const gchar *get_old_rc_dir (void); +const gchar *get_mail_base_dir (void); +const gchar *get_news_cache_dir (void); +const gchar *get_imap_cache_dir (void); +const gchar *get_mime_tmp_dir (void); +const gchar *get_template_dir (void); +const gchar *get_tmp_dir (void); +gchar *get_tmp_file (void); +const gchar *get_domain_name (void); + +/* file / directory handling */ +off_t get_file_size (const gchar *file); +off_t get_file_size_as_crlf (const gchar *file); +off_t get_left_file_size (FILE *fp); + +gboolean file_exist (const gchar *file, + gboolean allow_fifo); +gboolean is_dir_exist (const gchar *dir); +gboolean is_file_entry_exist (const gchar *file); +gboolean dirent_is_regular_file (struct dirent *d); +gboolean dirent_is_directory (struct dirent *d); + +#define is_file_exist(file) file_exist(file, FALSE) +#define is_file_or_fifo_exist(file) file_exist(file, TRUE) + +gint change_dir (const gchar *dir); +gint make_dir (const gchar *dir); +gint make_dir_hier (const gchar *dir); +gint remove_all_files (const gchar *dir); +gint remove_numbered_files (const gchar *dir, + guint first, + guint last); +gint remove_all_numbered_files (const gchar *dir); +gint remove_expired_files (const gchar *dir, + guint hours); +gint remove_dir_recursive (const gchar *dir); +gint rename_force (const gchar *oldpath, + const gchar *newpath); +gint copy_file (const gchar *src, + const gchar *dest, + gboolean keep_backup); +gint copy_dir (const gchar *src, + const gchar *dest); +gint move_file (const gchar *src, + const gchar *dest, + gboolean overwrite); +gint copy_file_part (FILE *fp, + off_t offset, + size_t length, + const gchar *dest); + +gchar *canonicalize_str (const gchar *str); +gint canonicalize_file (const gchar *src, + const gchar *dest); +gint canonicalize_file_replace (const gchar *file); +gint uncanonicalize_file (const gchar *src, + const gchar *dest); +gint uncanonicalize_file_replace(const gchar *file); + +gchar *normalize_newlines (const gchar *str); + +gchar *get_outgoing_rfc2822_str (FILE *fp); +gchar *generate_mime_boundary (const gchar *prefix); + +gint change_file_mode_rw (FILE *fp, + const gchar *file); +FILE *my_tmpfile (void); +FILE *str_open_as_stream (const gchar *str); +gint str_write_to_file (const gchar *str, + const gchar *file); +gchar *file_read_to_str (const gchar *file); +gchar *file_read_stream_to_str (FILE *fp); + +/* process execution */ +gint execute_async (gchar *const argv[]); +gint execute_sync (gchar *const argv[]); +gint execute_command_line (const gchar *cmdline, + gboolean async); +gchar *get_command_output (const gchar *cmdline); + +/* open URI with external browser */ +gint open_uri(const gchar *uri, const gchar *cmdline); + +/* time functions */ +time_t remote_tzoffset_sec (const gchar *zone); +time_t tzoffset_sec (time_t *now); +gchar *tzoffset (time_t *now); +void get_rfc822_date (gchar *buf, + gint len); + +size_t my_strftime (gchar *s, + size_t max, + const gchar *format, + const struct tm *tm); + +/* logging */ +void set_log_file (const gchar *filename); +void close_log_file (void); +void set_log_verbosity (gboolean verbose); +gboolean get_debug_mode (void); +void set_debug_mode (gboolean enable); + +void set_log_ui_func (LogFunc print_func, + LogFunc message_func, + LogFunc warning_func, + LogFunc error_func); + +void set_log_show_status_func (LogFunc status_func); + +void debug_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); +void log_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); +void log_message (const gchar *format, ...) G_GNUC_PRINTF(1, 2); +void log_warning (const gchar *format, ...) G_GNUC_PRINTF(1, 2); +void log_error (const gchar *format, ...) G_GNUC_PRINTF(1, 2); + +#endif /* __UTILS_H__ */ diff --git a/libsylph/uuencode.c b/libsylph/uuencode.c new file mode 100644 index 00000000..e0b2e79a --- /dev/null +++ b/libsylph/uuencode.c @@ -0,0 +1,101 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999,2000 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + +#define UUDECODE(c) (c=='`' ? 0 : c - ' ') +#define N64(i) (i & ~63) + +const char uudigit[64] = +{ + '`', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_' +}; + +int touufrombits(unsigned char *out, const unsigned char *in, int inlen) +{ + int len; + + if (inlen > 45) return -1; + len = (inlen * 4 + 2) / 3 + 1; + *out++ = uudigit[inlen]; + + for (; inlen >= 3; inlen -= 3) { + *out++ = uudigit[in[0] >> 2]; + *out++ = uudigit[((in[0] << 4) & 0x30) | (in[1] >> 4)]; + *out++ = uudigit[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; + *out++ = uudigit[in[2] & 0x3f]; + in += 3; + } + + if (inlen > 0) { + *out++ = uudigit[(in[0] >> 2)]; + if (inlen == 1) { + *out++ = uudigit[((in[0] << 4) & 0x30)]; + } else { + *out++ = uudigit[(((in[0] << 4) & 0x30) | (in[1] >> 4))] ; + *out++ = uudigit[((in[1] << 2) & 0x3c)]; + } + } + *out = '\0'; + + return len; +} + +int fromuutobits(char *out, const char *in) +{ + int len, outlen, inlen; + register unsigned char digit1, digit2; + + outlen = UUDECODE(in[0]); + in += 1; + if(outlen < 0 || outlen > 45) + return -2; + if(outlen == 0) + return 0; + inlen = (outlen * 4 + 2) / 3; + len = 0; + + for( ; inlen>0; inlen-=4) { + digit1 = UUDECODE(in[0]); + if (N64(digit1)) return -1; + digit2 = UUDECODE(in[1]); + if (N64(digit2)) return -1; + out[len++] = (digit1 << 2) | (digit2 >> 4); + if (inlen > 2) { + digit1 = UUDECODE(in[2]); + if (N64(digit1)) return -1; + out[len++] = (digit2 << 4) | (digit1 >> 2); + if (inlen > 3) { + digit2 = UUDECODE(in[3]); + if (N64(digit2)) return -1; + out[len++] = (digit1 << 6) | digit2; + } + } + in += 4; + } + + return len == outlen ? len : -3; +} diff --git a/libsylph/uuencode.h b/libsylph/uuencode.h new file mode 100644 index 00000000..3658ebc6 --- /dev/null +++ b/libsylph/uuencode.h @@ -0,0 +1,24 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999,2000 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +void touufrombits(unsigned char *, const unsigned char *, int); +int fromuutobits(char *, const char *); + +#define X_UUENCODE_END_LINE '`' +#define UUENCODE_END_LINE ' ' diff --git a/libsylph/xml.c b/libsylph/xml.c new file mode 100644 index 00000000..62a04829 --- /dev/null +++ b/libsylph/xml.c @@ -0,0 +1,655 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2005 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include + +#include "xml.h" +#include "utils.h" +#include "codeconv.h" + +#define SPARSE_MEMORY +/* if this is defined all attr.names and tag.names are stored + * in a hash table */ +#if defined(SPARSE_MEMORY) +#include "stringtable.h" + +static StringTable *xml_string_table; + +static void xml_string_table_create(void) +{ + if (xml_string_table == NULL) + xml_string_table = string_table_new(); +} +#define XML_STRING_ADD(str) \ + string_table_insert_string(xml_string_table, (str)) +#define XML_STRING_FREE(str) \ + string_table_free_string(xml_string_table, (str)) + +#define XML_STRING_TABLE_CREATE() \ + xml_string_table_create() + +#else /* !SPARSE_MEMORY */ + +#define XML_STRING_ADD(str) \ + g_strdup(str) +#define XML_STRING_FREE(str) \ + g_free(str) + +#define XML_STRING_TABLE_CREATE() + +#endif /* SPARSE_MEMORY */ + +static void xml_free_tag (XMLTag *tag); +static gint xml_get_parenthesis (XMLFile *file, + gchar *buf, + gint len); + +XMLFile *xml_open_file(const gchar *path) +{ + XMLFile *newfile; + + g_return_val_if_fail(path != NULL, NULL); + + XML_STRING_TABLE_CREATE(); + + newfile = g_new(XMLFile, 1); + + newfile->fp = g_fopen(path, "rb"); + if (!newfile->fp) { + g_free(newfile); + return NULL; + } + + newfile->buf = g_string_new(NULL); + newfile->bufp = newfile->buf->str; + + newfile->dtd = NULL; + newfile->encoding = NULL; + newfile->tag_stack = NULL; + newfile->level = 0; + newfile->is_empty_element = FALSE; + + return newfile; +} + +void xml_close_file(XMLFile *file) +{ + g_return_if_fail(file != NULL); + + if (file->fp) fclose(file->fp); + + g_string_free(file->buf, TRUE); + + g_free(file->dtd); + g_free(file->encoding); + + while (file->tag_stack != NULL) + xml_pop_tag(file); + + g_free(file); +} + +static GNode *xml_build_tree(XMLFile *file, GNode *parent, guint level) +{ + GNode *node = NULL; + XMLNode *xmlnode; + XMLTag *tag; + + while (xml_parse_next_tag(file) == 0) { + if (file->level < level) break; + if (file->level == level) { + g_warning("xml_build_tree(): Parse error\n"); + break; + } + + tag = xml_get_current_tag(file); + if (!tag) break; + xmlnode = xml_node_new(xml_copy_tag(tag), NULL); + xmlnode->element = xml_get_element(file); + if (!parent) + node = g_node_new(xmlnode); + else + node = g_node_append_data(parent, xmlnode); + + xml_build_tree(file, node, file->level); + if (file->level == 0) break; + } + + return node; +} + +GNode *xml_parse_file(const gchar *path) +{ + XMLFile *file; + GNode *node; + + file = xml_open_file(path); + g_return_val_if_fail(file != NULL, NULL); + + xml_get_dtd(file); + + node = xml_build_tree(file, NULL, file->level); + + xml_close_file(file); + +#if defined(SPARSE_MEMORY) + if (get_debug_mode()) + string_table_get_stats(xml_string_table); +#endif + + return node; +} + +gint xml_get_dtd(XMLFile *file) +{ + gchar buf[XMLBUFSIZE]; + gchar *bufp = buf; + + if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) return -1; + + if ((*bufp++ == '?') && + (bufp = strcasestr(bufp, "xml")) && + (bufp = strcasestr(bufp + 3, "version")) && + (bufp = strchr(bufp + 7, '?'))) { + file->dtd = g_strdup(buf); + if ((bufp = strcasestr(buf, "encoding=\""))) { + bufp += 9; + extract_quote(bufp, '"'); + file->encoding = g_strdup(bufp); + } else + file->encoding = g_strdup(CS_INTERNAL); + } else { + g_warning("Can't get xml dtd\n"); + return -1; + } + + return 0; +} + +gint xml_parse_next_tag(XMLFile *file) +{ + gchar buf[XMLBUFSIZE]; + gchar *bufp = buf; + gchar *tag_str; + XMLTag *tag; + gint len; + + if (file->is_empty_element == TRUE) { + file->is_empty_element = FALSE; + xml_pop_tag(file); + return 0; + } + + if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) { + g_warning("xml_parse_next_tag(): Can't parse next tag\n"); + return -1; + } + + /* end-tag */ + if (buf[0] == '/') { + if (strcmp(xml_get_current_tag(file)->tag, buf + 1) != 0) { + g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf); + return -1; + } + xml_pop_tag(file); + return 0; + } + + tag = xml_tag_new(NULL); + xml_push_tag(file, tag); + + len = strlen(buf); + if (len > 0 && buf[len - 1] == '/') { + file->is_empty_element = TRUE; + buf[len - 1] = '\0'; + g_strchomp(buf); + } + if (strlen(buf) == 0) { + g_warning("xml_parse_next_tag(): Tag name is empty\n"); + return -1; + } + + while (*bufp != '\0' && !g_ascii_isspace(*bufp)) bufp++; + if (*bufp == '\0') { + tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); + if (tag_str) { + tag->tag = XML_STRING_ADD(tag_str); + g_free(tag_str); + } else + tag->tag = XML_STRING_ADD(buf); + return 0; + } else { + *bufp++ = '\0'; + tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); + if (tag_str) { + tag->tag = XML_STRING_ADD(tag_str); + g_free(tag_str); + } else + tag->tag = XML_STRING_ADD(buf); + } + + /* parse attributes ( name=value ) */ + while (*bufp) { + XMLAttr *attr; + gchar *attr_name; + gchar *attr_value; + gchar *utf8_attr_name; + gchar *utf8_attr_value; + gchar *p; + gchar quote; + + while (g_ascii_isspace(*bufp)) bufp++; + attr_name = bufp; + if ((p = strchr(attr_name, '=')) == NULL) { + g_warning("xml_parse_next_tag(): Syntax error in tag\n"); + return -1; + } + bufp = p; + *bufp++ = '\0'; + while (g_ascii_isspace(*bufp)) bufp++; + + if (*bufp != '"' && *bufp != '\'') { + g_warning("xml_parse_next_tag(): Syntax error in tag\n"); + return -1; + } + quote = *bufp; + bufp++; + attr_value = bufp; + if ((p = strchr(attr_value, quote)) == NULL) { + g_warning("xml_parse_next_tag(): Syntax error in tag\n"); + return -1; + } + bufp = p; + *bufp++ = '\0'; + + g_strchomp(attr_name); + xml_unescape_str(attr_value); + utf8_attr_name = conv_codeset_strdup + (attr_name, file->encoding, CS_INTERNAL); + utf8_attr_value = conv_codeset_strdup + (attr_value, file->encoding, CS_INTERNAL); + if (!utf8_attr_name) + utf8_attr_name = g_strdup(attr_name); + if (!utf8_attr_value) + utf8_attr_value = g_strdup(attr_value); + + attr = xml_attr_new(utf8_attr_name, utf8_attr_value); + xml_tag_add_attr(tag, attr); + + g_free(utf8_attr_value); + g_free(utf8_attr_name); + } + + return 0; +} + +void xml_push_tag(XMLFile *file, XMLTag *tag) +{ + g_return_if_fail(tag != NULL); + + file->tag_stack = g_list_prepend(file->tag_stack, tag); + file->level++; +} + +void xml_pop_tag(XMLFile *file) +{ + XMLTag *tag; + + if (!file->tag_stack) return; + + tag = (XMLTag *)file->tag_stack->data; + + xml_free_tag(tag); + file->tag_stack = g_list_remove(file->tag_stack, tag); + file->level--; +} + +XMLTag *xml_get_current_tag(XMLFile *file) +{ + if (file->tag_stack) + return (XMLTag *)file->tag_stack->data; + else + return NULL; +} + +GList *xml_get_current_tag_attr(XMLFile *file) +{ + XMLTag *tag; + + tag = xml_get_current_tag(file); + if (!tag) return NULL; + + return tag->attr; +} + +gchar *xml_get_element(XMLFile *file) +{ + gchar *str; + gchar *new_str; + gchar *end; + + while ((end = strchr(file->bufp, '<')) == NULL) + if (xml_read_line(file) < 0) return NULL; + + if (end == file->bufp) + return NULL; + + str = g_strndup(file->bufp, end - file->bufp); + /* this is not XML1.0 strict */ + g_strstrip(str); + xml_unescape_str(str); + + file->bufp = end; + xml_truncate_buf(file); + + if (str[0] == '\0') { + g_free(str); + return NULL; + } + + new_str = conv_codeset_strdup(str, file->encoding, CS_INTERNAL); + if (!new_str) + new_str = g_strdup(str); + g_free(str); + + return new_str; +} + +gint xml_read_line(XMLFile *file) +{ + gchar buf[XMLBUFSIZE]; + gint index; + + if (fgets(buf, sizeof(buf), file->fp) == NULL) + return -1; + + index = file->bufp - file->buf->str; + + g_string_append(file->buf, buf); + + file->bufp = file->buf->str + index; + + return 0; +} + +void xml_truncate_buf(XMLFile *file) +{ + gint len; + + len = file->bufp - file->buf->str; + if (len > 0) { + g_string_erase(file->buf, 0, len); + file->bufp = file->buf->str; + } +} + +gboolean xml_compare_tag(XMLFile *file, const gchar *name) +{ + XMLTag *tag; + + tag = xml_get_current_tag(file); + + if (tag && strcmp(tag->tag, name) == 0) + return TRUE; + else + return FALSE; +} + +XMLNode *xml_node_new(XMLTag *tag, const gchar *text) +{ + XMLNode *node; + + node = g_new(XMLNode, 1); + node->tag = tag; + node->element = g_strdup(text); + + return node; +} + +XMLTag *xml_tag_new(const gchar *tag) +{ + XMLTag *new_tag; + + new_tag = g_new(XMLTag, 1); + if (tag) + new_tag->tag = XML_STRING_ADD(tag); + else + new_tag->tag = NULL; + new_tag->attr = NULL; + + return new_tag; +} + +XMLAttr *xml_attr_new(const gchar *name, const gchar *value) +{ + XMLAttr *new_attr; + + new_attr = g_new(XMLAttr, 1); + new_attr->name = XML_STRING_ADD(name); + new_attr->value = g_strdup(value); + + return new_attr; +} + +void xml_tag_add_attr(XMLTag *tag, XMLAttr *attr) +{ + tag->attr = g_list_append(tag->attr, attr); +} + +XMLTag *xml_copy_tag(XMLTag *tag) +{ + XMLTag *new_tag; + XMLAttr *attr; + GList *list; + + new_tag = xml_tag_new(tag->tag); + for (list = tag->attr; list != NULL; list = list->next) { + attr = xml_copy_attr((XMLAttr *)list->data); + xml_tag_add_attr(new_tag, attr); + } + + return new_tag; +} + +XMLAttr *xml_copy_attr(XMLAttr *attr) +{ + return xml_attr_new(attr->name, attr->value); +} + +gint xml_unescape_str(gchar *str) +{ + gchar *start; + gchar *end; + gchar *p = str; + gchar *esc_str; + gchar ch; + gint len; + + while ((start = strchr(p, '&')) != NULL) { + if ((end = strchr(start + 1, ';')) == NULL) { + g_warning("Unescaped `&' appeared\n"); + p = start + 1; + continue; + } + len = end - start + 1; + if (len < 3) { + p = end + 1; + continue; + } + + Xstrndup_a(esc_str, start, len, return -1); + if (!strcmp(esc_str, "<")) + ch = '<'; + else if (!strcmp(esc_str, ">")) + ch = '>'; + else if (!strcmp(esc_str, "&")) + ch = '&'; + else if (!strcmp(esc_str, "'")) + ch = '\''; + else if (!strcmp(esc_str, """)) + ch = '\"'; + else { + p = end + 1; + continue; + } + + *start = ch; + memmove(start + 1, end + 1, strlen(end + 1) + 1); + p = start + 1; + } + + return 0; +} + +gint xml_file_put_escape_str(FILE *fp, const gchar *str) +{ + const gchar *p; + + g_return_val_if_fail(fp != NULL, -1); + + if (!str) return 0; + + for (p = str; *p != '\0'; p++) { + switch (*p) { + case '<': + fputs("<", fp); + break; + case '>': + fputs(">", fp); + break; + case '&': + fputs("&", fp); + break; + case '\'': + fputs("'", fp); + break; + case '\"': + fputs(""", fp); + break; + default: + fputc(*p, fp); + } + } + + return 0; +} + +gint xml_file_put_xml_decl(FILE *fp) +{ + g_return_val_if_fail(fp != NULL, -1); + + fprintf(fp, "\n", CS_INTERNAL); + return 0; +} + +gint xml_file_put_node(FILE *fp, XMLNode *node) +{ + GList *cur; + + g_return_val_if_fail(fp != NULL, -1); + g_return_val_if_fail(node != NULL, -1); + + fprintf(fp, "<%s", node->tag->tag); + + for (cur = node->tag->attr; cur != NULL; cur = cur->next) { + XMLAttr *attr = (XMLAttr *)cur->data; + fprintf(fp, " %s=\"", attr->name); + xml_file_put_escape_str(fp, attr->value); + fputs("\"", fp); + } + + if (node->element) { + fputs(">", fp); + xml_file_put_escape_str(fp, node->element); + fprintf(fp, "\n", node->tag->tag); + } else { + fputs(" />\n", fp); + } + + return 0; +} + +void xml_free_node(XMLNode *node) +{ + if (!node) return; + + xml_free_tag(node->tag); + g_free(node->element); + g_free(node); +} + +static gboolean xml_free_func(GNode *node, gpointer data) +{ + XMLNode *xmlnode = node->data; + + xml_free_node(xmlnode); + return FALSE; +} + +void xml_free_tree(GNode *node) +{ + g_return_if_fail(node != NULL); + + g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_free_func, + NULL); + + g_node_destroy(node); +} + +static void xml_free_tag(XMLTag *tag) +{ + if (!tag) return; + + XML_STRING_FREE(tag->tag); + while (tag->attr != NULL) { + XMLAttr *attr = (XMLAttr *)tag->attr->data; + XML_STRING_FREE(attr->name); + g_free(attr->value); + g_free(attr); + tag->attr = g_list_remove(tag->attr, tag->attr->data); + } + g_free(tag); +} + +static gint xml_get_parenthesis(XMLFile *file, gchar *buf, gint len) +{ + gchar *start; + gchar *end; + + buf[0] = '\0'; + + while ((start = strchr(file->bufp, '<')) == NULL) + if (xml_read_line(file) < 0) return -1; + + start++; + file->bufp = start; + + while ((end = strchr(file->bufp, '>')) == NULL) + if (xml_read_line(file) < 0) return -1; + + strncpy2(buf, file->bufp, MIN(end - file->bufp + 1, len)); + g_strstrip(buf); + file->bufp = end + 1; + xml_truncate_buf(file); + + return 0; +} diff --git a/libsylph/xml.h b/libsylph/xml.h new file mode 100644 index 00000000..b41449a4 --- /dev/null +++ b/libsylph/xml.h @@ -0,0 +1,108 @@ +/* + * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 1999-2004 Hiroyuki Yamamoto + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __XML_H__ +#define __XML_H__ + +#include +#include + +#define XMLBUFSIZE 8192 + +typedef struct _XMLAttr XMLAttr; +typedef struct _XMLTag XMLTag; +typedef struct _XMLNode XMLNode; +typedef struct _XMLFile XMLFile; + +struct _XMLAttr +{ + gchar *name; + gchar *value; +}; + +struct _XMLTag +{ + gchar *tag; + GList *attr; +}; + +struct _XMLNode +{ + XMLTag *tag; + gchar *element; +}; + +struct _XMLFile +{ + FILE *fp; + + GString *buf; + gchar *bufp; + + gchar *dtd; + gchar *encoding; + + GList *tag_stack; + guint level; + + gboolean is_empty_element; +}; + +XMLFile *xml_open_file (const gchar *path); +void xml_close_file (XMLFile *file); +GNode *xml_parse_file (const gchar *path); + +gint xml_get_dtd (XMLFile *file); +gint xml_parse_next_tag (XMLFile *file); +void xml_push_tag (XMLFile *file, + XMLTag *tag); +void xml_pop_tag (XMLFile *file); + +XMLTag *xml_get_current_tag (XMLFile *file); +GList *xml_get_current_tag_attr(XMLFile *file); +gchar *xml_get_element (XMLFile *file); + +gint xml_read_line (XMLFile *file); +void xml_truncate_buf (XMLFile *file); +gboolean xml_compare_tag (XMLFile *file, + const gchar *name); + +XMLNode *xml_node_new (XMLTag *tag, + const gchar *text); +XMLTag *xml_tag_new (const gchar *tag); +XMLAttr *xml_attr_new (const gchar *name, + const gchar *value); +void xml_tag_add_attr (XMLTag *tag, + XMLAttr *attr); + +XMLTag *xml_copy_tag (XMLTag *tag); +XMLAttr *xml_copy_attr (XMLAttr *attr); + +gint xml_unescape_str (gchar *str); +gint xml_file_put_escape_str (FILE *fp, + const gchar *str); + +gint xml_file_put_xml_decl (FILE *fp); +gint xml_file_put_node (FILE *fp, + XMLNode *node); + +void xml_free_node (XMLNode *node); +void xml_free_tree (GNode *node); + +#endif /* __XML_H__ */ diff --git a/src/Makefile.am b/src/Makefile.am index 5da41db1..b81e23cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,6 @@ sylpheed_SOURCES = \ gtkshruler.c gtkshruler.h \ menu.c menu.h \ stock_pixmap.c stock_pixmap.h \ - prefs.c prefs.h \ prefs_ui.c prefs_ui.h \ prefs_common.c prefs_common.h \ prefs_filter.c prefs_filter.h \ @@ -76,17 +75,8 @@ sylpheed_SOURCES = \ grouplistdialog.c grouplistdialog.h \ about.c about.h \ setup.c setup.h \ - utils.c utils.h \ gtkutils.c gtkutils.h \ - codeconv.c codeconv.h \ - unmime.c unmime.h \ - base64.c base64.h \ - quoted-printable.c quoted-printable.h \ - uuencode.c uuencode.h \ md5.c md5.h \ - socket.c socket.h \ - ssl.c ssl.h \ - session.c session.h \ smtp.c smtp.h \ pop.c pop.h \ mh.c mh.h \ @@ -99,7 +89,6 @@ sylpheed_SOURCES = \ nntp.c nntp.h \ news.c news.h \ imap.c imap.h \ - xml.c xml.h \ html.c html.h \ procmime.c procmime.h \ rfc2015.c rfc2015.h \ @@ -108,7 +97,6 @@ sylpheed_SOURCES = \ sigstatus.c sigstatus.h \ simple-gettext.c \ manual.c manual.h \ - stringtable.c stringtable.h \ eggtrayicon.c eggtrayicon.h \ trayicon.c trayicon.h \ quote_fmt_lex.l quote_fmt_lex.h \ @@ -132,14 +120,16 @@ INCLUDES = \ $(GTK_CFLAGS) \ $(GDK_PIXBUF_CFLAGS) \ $(GPGME_CFLAGS) \ - -I$(includedir) + -I$(includedir) \ + -I$(top_srcdir)/libsylph sylpheed_LDADD = \ $(INTLLIBS) \ $(GTK_LIBS) \ $(GPGME_LIBS) \ $(LDAP_LIBS) \ - $(LIBICONV) + $(LIBICONV) \ + ../libsylph/libsylph.la AM_CPPFLAGS = \ -DLOCALEDIR=\""$(localedir)"\" \ diff --git a/src/base64.c b/src/base64.c deleted file mode 100644 index 484cd286..00000000 --- a/src/base64.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2002 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include -#include - -#include "base64.h" - -static const gchar base64char[64] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static const gchar base64val[128] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 -}; - -#define BASE64VAL(c) (isascii((guchar)c) ? base64val[(gint)(c)] : -1) - -void base64_encode(gchar *out, const guchar *in, gint inlen) -{ - const guchar *inp = in; - gchar *outp = out; - - while (inlen >= 3) { - *outp++ = base64char[(inp[0] >> 2) & 0x3f]; - *outp++ = base64char[((inp[0] & 0x03) << 4) | - ((inp[1] >> 4) & 0x0f)]; - *outp++ = base64char[((inp[1] & 0x0f) << 2) | - ((inp[2] >> 6) & 0x03)]; - *outp++ = base64char[inp[2] & 0x3f]; - - inp += 3; - inlen -= 3; - } - - if (inlen > 0) { - *outp++ = base64char[(inp[0] >> 2) & 0x3f]; - if (inlen == 1) { - *outp++ = base64char[(inp[0] & 0x03) << 4]; - *outp++ = '='; - } else { - *outp++ = base64char[((inp[0] & 0x03) << 4) | - ((inp[1] >> 4) & 0x0f)]; - *outp++ = base64char[((inp[1] & 0x0f) << 2)]; - } - *outp++ = '='; - } - - *outp = '\0'; -} - -gint base64_decode(guchar *out, const gchar *in, gint inlen) -{ - const gchar *inp = in; - guchar *outp = out; - gchar buf[4]; - - if (inlen < 0) - inlen = G_MAXINT; - - while (inlen >= 4 && *inp != '\0') { - buf[0] = *inp++; - inlen--; - if (BASE64VAL(buf[0]) == -1) break; - - buf[1] = *inp++; - inlen--; - if (BASE64VAL(buf[1]) == -1) break; - - buf[2] = *inp++; - inlen--; - if (buf[2] != '=' && BASE64VAL(buf[2]) == -1) break; - - buf[3] = *inp++; - inlen--; - if (buf[3] != '=' && BASE64VAL(buf[3]) == -1) break; - - *outp++ = ((BASE64VAL(buf[0]) << 2) & 0xfc) | - ((BASE64VAL(buf[1]) >> 4) & 0x03); - if (buf[2] != '=') { - *outp++ = ((BASE64VAL(buf[1]) & 0x0f) << 4) | - ((BASE64VAL(buf[2]) >> 2) & 0x0f); - if (buf[3] != '=') { - *outp++ = ((BASE64VAL(buf[2]) & 0x03) << 6) | - (BASE64VAL(buf[3]) & 0x3f); - } - } - } - - return outp - out; -} - -Base64Decoder *base64_decoder_new(void) -{ - Base64Decoder *decoder; - - decoder = g_new0(Base64Decoder, 1); - return decoder; -} - -void base64_decoder_free(Base64Decoder *decoder) -{ - g_free(decoder); -} - -gint base64_decoder_decode(Base64Decoder *decoder, - const gchar *in, guchar *out) -{ - gint len, total_len = 0; - gint buf_len; - gchar buf[4]; - - g_return_val_if_fail(decoder != NULL, -1); - g_return_val_if_fail(in != NULL, -1); - g_return_val_if_fail(out != NULL, -1); - - buf_len = decoder->buf_len; - memcpy(buf, decoder->buf, sizeof(buf)); - - for (;;) { - while (buf_len < 4) { - gchar c = *in; - - in++; - if (c == '\0') break; - if (c == '\r' || c == '\n') continue; - if (c != '=' && BASE64VAL(c) == -1) - return -1; - buf[buf_len++] = c; - } - if (buf_len < 4 || buf[0] == '=' || buf[1] == '=') { - decoder->buf_len = buf_len; - memcpy(decoder->buf, buf, sizeof(buf)); - return total_len; - } - len = base64_decode(out, buf, 4); - out += len; - total_len += len; - buf_len = 0; - if (len < 3) { - decoder->buf_len = 0; - return total_len; - } - } -} diff --git a/src/base64.h b/src/base64.h deleted file mode 100644 index 4aa55758..00000000 --- a/src/base64.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2002 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __BASE64_H__ -#define __BASE64_H__ - -#include - -typedef struct _Base64Decoder Base64Decoder; - -struct _Base64Decoder -{ - gint buf_len; - gchar buf[4]; -}; - -void base64_encode (gchar *out, - const guchar *in, - gint inlen); -gint base64_decode (guchar *out, - const gchar *in, - gint inlen); - -Base64Decoder *base64_decoder_new (void); -void base64_decoder_free (Base64Decoder *decoder); -gint base64_decoder_decode (Base64Decoder *decoder, - const gchar *in, - guchar *out); - -#endif /* __BASE64_H__ */ diff --git a/src/codeconv.c b/src/codeconv.c deleted file mode 100644 index 4eb68b78..00000000 --- a/src/codeconv.c +++ /dev/null @@ -1,1982 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "defs.h" - -#include -#include -#include -#include -#include -#include - -#if HAVE_LOCALE_H -# include -#endif - -#include - -#include "codeconv.h" -#include "unmime.h" -#include "base64.h" -#include "quoted-printable.h" -#include "utils.h" -#include "prefs_common.h" - -typedef enum -{ - JIS_ASCII, - JIS_KANJI, - JIS_HWKANA, - JIS_AUXKANJI -} JISState; - -#define SUBST_CHAR '_' -#define ESC '\033' - -#define iseuckanji(c) \ - (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe) -#define iseuchwkana1(c) \ - (((c) & 0xff) == 0x8e) -#define iseuchwkana2(c) \ - (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) -#define iseucaux(c) \ - (((c) & 0xff) == 0x8f) -#define issjiskanji1(c) \ - ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \ - (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xfc)) -#define issjiskanji2(c) \ - ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \ - (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc)) -#define issjishwkana(c) \ - (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) - -#define K_IN() \ - if (state != JIS_KANJI) { \ - *out++ = ESC; \ - *out++ = '$'; \ - *out++ = 'B'; \ - state = JIS_KANJI; \ - } - -#define K_OUT() \ - if (state != JIS_ASCII) { \ - *out++ = ESC; \ - *out++ = '('; \ - *out++ = 'B'; \ - state = JIS_ASCII; \ - } - -#define HW_IN() \ - if (state != JIS_HWKANA) { \ - *out++ = ESC; \ - *out++ = '('; \ - *out++ = 'I'; \ - state = JIS_HWKANA; \ - } - -#define AUX_IN() \ - if (state != JIS_AUXKANJI) { \ - *out++ = ESC; \ - *out++ = '$'; \ - *out++ = '('; \ - *out++ = 'D'; \ - state = JIS_AUXKANJI; \ - } - -static gchar *conv_jistoeuc(const gchar *inbuf, gint *error); -static gchar *conv_euctojis(const gchar *inbuf, gint *error); -static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error); - -static gchar *conv_jistoutf8(const gchar *inbuf, gint *error); -static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error); -static gchar *conv_euctoutf8(const gchar *inbuf, gint *error); -static gchar *conv_anytoutf8(const gchar *inbuf, gint *error); - -static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error); -static gchar *conv_utf8tojis(const gchar *inbuf, gint *error); - -/* static void conv_unreadable_eucjp(gchar *str); */ -static void conv_unreadable_8bit(gchar *str); -/* static void conv_unreadable_latin(gchar *str); */ - -static gchar *conv_jistodisp(const gchar *inbuf, gint *error); -static gchar *conv_sjistodisp(const gchar *inbuf, gint *error); -static gchar *conv_euctodisp(const gchar *inbuf, gint *error); - -static gchar *conv_anytodisp(const gchar *inbuf, gint *error); -static gchar *conv_ustodisp(const gchar *inbuf, gint *error); -static gchar *conv_noconv(const gchar *inbuf, gint *error); - -static gchar *conv_jistoeuc(const gchar *inbuf, gint *error) -{ - gchar *outbuf; - const guchar *in = (guchar *)inbuf; - guchar *out; - JISState state = JIS_ASCII; - gint error_ = 0; - - outbuf = g_malloc(strlen(inbuf) * 2 + 1); - out = (guchar *)outbuf; - - while (*in != '\0') { - if (*in == ESC) { - in++; - if (*in == '$') { - if (*(in + 1) == '@' || *(in + 1) == 'B') { - state = JIS_KANJI; - in += 2; - } else if (*(in + 1) == '(' && - *(in + 2) == 'D') { - state = JIS_AUXKANJI; - in += 3; - } else { - /* unknown escape sequence */ - error_ = -1; - state = JIS_ASCII; - } - } else if (*in == '(') { - if (*(in + 1) == 'B' || *(in + 1) == 'J') { - state = JIS_ASCII; - in += 2; - } else if (*(in + 1) == 'I') { - state = JIS_HWKANA; - in += 2; - } else { - /* unknown escape sequence */ - error_ = -1; - state = JIS_ASCII; - } - } else { - /* unknown escape sequence */ - error_ = -1; - state = JIS_ASCII; - } - } else if (*in == 0x0e) { - state = JIS_HWKANA; - in++; - } else if (*in == 0x0f) { - state = JIS_ASCII; - in++; - } else { - switch (state) { - case JIS_ASCII: - *out++ = *in++; - break; - case JIS_KANJI: - *out++ = *in++ | 0x80; - if (*in == '\0') break; - *out++ = *in++ | 0x80; - break; - case JIS_HWKANA: - *out++ = 0x8e; - *out++ = *in++ | 0x80; - break; - case JIS_AUXKANJI: - *out++ = 0x8f; - *out++ = *in++ | 0x80; - if (*in == '\0') break; - *out++ = *in++ | 0x80; - break; - } - } - } - - *out = '\0'; - - if (error) - *error = error_; - - return outbuf; -} - -#define JIS_HWDAKUTEN 0x5e -#define JIS_HWHANDAKUTEN 0x5f - -static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym) -{ - static guint16 h2z_tbl[] = { - /* 0x20 - 0x2f */ - 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521, - 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543, - /* 0x30 - 0x3f */ - 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d, - 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d, - /* 0x40 - 0x4f */ - 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c, - 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e, - /* 0x50 - 0x5f */ - 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569, - 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c - }; - - static guint16 dakuten_tbl[] = { - /* 0x30 - 0x3f */ - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e, - 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e, - /* 0x40 - 0x4f */ - 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000 - }; - - static guint16 handakuten_tbl[] = { - /* 0x4a - 0x4e */ - 0x2551, 0x2554, 0x2557, 0x255a, 0x255d - }; - - guint16 out_code; - - jis_code &= 0x7f; - sound_sym &= 0x7f; - - if (jis_code < 0x21 || jis_code > 0x5f) - return 0; - - if (sound_sym == JIS_HWDAKUTEN && - jis_code >= 0x36 && jis_code <= 0x4e) { - out_code = dakuten_tbl[jis_code - 0x30]; - if (out_code != 0) { - *outbuf = out_code >> 8; - *(outbuf + 1) = out_code & 0xff; - return 2; - } - } - - if (sound_sym == JIS_HWHANDAKUTEN && - jis_code >= 0x4a && jis_code <= 0x4e) { - out_code = handakuten_tbl[jis_code - 0x4a]; - *outbuf = out_code >> 8; - *(outbuf + 1) = out_code & 0xff; - return 2; - } - - out_code = h2z_tbl[jis_code - 0x20]; - *outbuf = out_code >> 8; - *(outbuf + 1) = out_code & 0xff; - return 1; -} - -static gchar *conv_euctojis(const gchar *inbuf, gint *error) -{ - gchar *outbuf; - const guchar *in = (guchar *)inbuf; - guchar *out; - JISState state = JIS_ASCII; - gint error_ = 0; - - outbuf = g_malloc(strlen(inbuf) * 3 + 4); - out = (guchar *)outbuf; - - while (*in != '\0') { - if (isascii(*in)) { - K_OUT(); - *out++ = *in++; - } else if (iseuckanji(*in)) { - if (iseuckanji(*(in + 1))) { - K_IN(); - *out++ = *in++ & 0x7f; - *out++ = *in++ & 0x7f; - } else { - error_ = -1; - K_OUT(); - *out++ = SUBST_CHAR; - in++; - if (*in != '\0' && !isascii(*in)) { - *out++ = SUBST_CHAR; - in++; - } - } - } else if (iseuchwkana1(*in)) { - if (iseuchwkana2(*(in + 1))) { - if (prefs_common.allow_jisx0201_kana) { - HW_IN(); - in++; - *out++ = *in++ & 0x7f; - } else { - guchar jis_ch[2]; - gint len; - - if (iseuchwkana1(*(in + 2)) && - iseuchwkana2(*(in + 3))) - len = conv_jis_hantozen - (jis_ch, - *(in + 1), *(in + 3)); - else - len = conv_jis_hantozen - (jis_ch, - *(in + 1), '\0'); - if (len == 0) - in += 2; - else { - K_IN(); - in += len * 2; - *out++ = jis_ch[0]; - *out++ = jis_ch[1]; - } - } - } else { - error_ = -1; - K_OUT(); - in++; - if (*in != '\0' && !isascii(*in)) { - *out++ = SUBST_CHAR; - in++; - } - } - } else if (iseucaux(*in)) { - in++; - if (iseuckanji(*in) && iseuckanji(*(in + 1))) { - AUX_IN(); - *out++ = *in++ & 0x7f; - *out++ = *in++ & 0x7f; - } else { - error_ = -1; - K_OUT(); - if (*in != '\0' && !isascii(*in)) { - *out++ = SUBST_CHAR; - in++; - if (*in != '\0' && !isascii(*in)) { - *out++ = SUBST_CHAR; - in++; - } - } - } - } else { - error_ = -1; - K_OUT(); - *out++ = SUBST_CHAR; - in++; - } - } - - K_OUT(); - *out = '\0'; - - if (error) - *error = error_; - - return outbuf; -} - -static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error) -{ - gchar *outbuf; - const guchar *in = (guchar *)inbuf; - guchar *out; - gint error_ = 0; - - outbuf = g_malloc(strlen(inbuf) * 2 + 1); - out = (guchar *)outbuf; - - while (*in != '\0') { - if (isascii(*in)) { - *out++ = *in++; - } else if (issjiskanji1(*in)) { - if (issjiskanji2(*(in + 1))) { - guchar out1 = *in; - guchar out2 = *(in + 1); - guchar row; - - row = out1 < 0xa0 ? 0x70 : 0xb0; - if (out2 < 0x9f) { - out1 = (out1 - row) * 2 - 1; - out2 -= out2 > 0x7f ? 0x20 : 0x1f; - } else { - out1 = (out1 - row) * 2; - out2 -= 0x7e; - } - - *out++ = out1 | 0x80; - *out++ = out2 | 0x80; - in += 2; - } else { - error_ = -1; - *out++ = SUBST_CHAR; - in++; - if (*in != '\0' && !isascii(*in)) { - *out++ = SUBST_CHAR; - in++; - } - } - } else if (issjishwkana(*in)) { - *out++ = 0x8e; - *out++ = *in++; - } else { - error_ = -1; - *out++ = SUBST_CHAR; - in++; - } - } - - *out = '\0'; - - if (error) - *error = error_; - - return outbuf; -} - -static gchar *conv_jistoutf8(const gchar *inbuf, gint *error) -{ - gchar *eucstr, *utf8str; - gint e_error = 0, u_error = 0; - - eucstr = conv_jistoeuc(inbuf, &e_error); - utf8str = conv_euctoutf8(eucstr, &u_error); - g_free(eucstr); - - if (error) - *error = (e_error | u_error); - - return utf8str; -} - -static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error) -{ - gchar *utf8str; - - utf8str = conv_iconv_strdup(inbuf, CS_SHIFT_JIS, CS_UTF_8, error); - if (!utf8str) - utf8str = g_strdup(inbuf); - - return utf8str; -} - -static gchar *conv_euctoutf8(const gchar *inbuf, gint *error) -{ - static iconv_t cd = (iconv_t)-1; - static gboolean iconv_ok = TRUE; - - if (cd == (iconv_t)-1) { - if (!iconv_ok) { - if (error) - *error = -1; - return g_strdup(inbuf); - } - - cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS); - if (cd == (iconv_t)-1) { - cd = iconv_open(CS_UTF_8, CS_EUC_JP); - if (cd == (iconv_t)-1) { - g_warning("conv_euctoutf8(): %s\n", - g_strerror(errno)); - iconv_ok = FALSE; - if (error) - *error = -1; - return g_strdup(inbuf); - } - } - } - - return conv_iconv_strdup_with_cd(inbuf, cd, error); -} - -static gchar *conv_anytoutf8(const gchar *inbuf, gint *error) -{ - switch (conv_guess_ja_encoding(inbuf)) { - case C_ISO_2022_JP: - return conv_jistoutf8(inbuf, error); - case C_SHIFT_JIS: - return conv_sjistoutf8(inbuf, error); - case C_EUC_JP: - return conv_euctoutf8(inbuf, error); - default: - if (error) - *error = 0; - return g_strdup(inbuf); - } -} - -static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error) -{ - static iconv_t cd = (iconv_t)-1; - static gboolean iconv_ok = TRUE; - - if (cd == (iconv_t)-1) { - if (!iconv_ok) { - if (error) - *error = -1; - return g_strdup(inbuf); - } - - cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8); - if (cd == (iconv_t)-1) { - cd = iconv_open(CS_EUC_JP, CS_UTF_8); - if (cd == (iconv_t)-1) { - g_warning("conv_utf8toeuc(): %s\n", - g_strerror(errno)); - iconv_ok = FALSE; - if (error) - *error = -1; - return g_strdup(inbuf); - } - } - } - - return conv_iconv_strdup_with_cd(inbuf, cd, error); -} - -static gchar *conv_utf8tojis(const gchar *inbuf, gint *error) -{ - gchar *eucstr, *jisstr; - gint e_error = 0, j_error = 0; - - eucstr = conv_utf8toeuc(inbuf, &e_error); - jisstr = conv_euctojis(eucstr, &j_error); - g_free(eucstr); - - if (error) - *error = (e_error | j_error); - - return jisstr; -} - -#if 0 -static gchar valid_eucjp_tbl[][96] = { - /* 0xa2a0 - 0xa2ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 }, - - /* 0xa3a0 - 0xa3ff */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, - - /* 0xa4a0 - 0xa4ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* 0xa5a0 - 0xa5ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* 0xa6a0 - 0xa6ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* 0xa7a0 - 0xa7ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - - /* 0xa8a0 - 0xa8ff */ - { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } -}; - -static gboolean isprintableeuckanji(guchar c1, guchar c2) -{ - if (c1 <= 0xa0 || c1 >= 0xf5) - return FALSE; - if (c2 <= 0xa0 || c2 == 0xff) - return FALSE; - - if (c1 >= 0xa9 && c1 <= 0xaf) - return FALSE; - - if (c1 >= 0xa2 && c1 <= 0xa8) - return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0]; - - if (c1 == 0xcf) { - if (c2 >= 0xd4 && c2 <= 0xfe) - return FALSE; - } else if (c1 == 0xf4) { - if (c2 >= 0xa7 && c2 <= 0xfe) - return FALSE; - } - - return TRUE; -} - -static void conv_unreadable_eucjp(gchar *str) -{ - register guchar *p = str; - - while (*p != '\0') { - if (isascii(*p)) { - /* convert CR+LF -> LF */ - if (*p == '\r' && *(p + 1) == '\n') - memmove(p, p + 1, strlen(p)); - /* printable 7 bit code */ - p++; - } else if (iseuckanji(*p)) { - if (isprintableeuckanji(*p, *(p + 1))) { - /* printable euc-jp code */ - p += 2; - } else { - /* substitute unprintable code */ - *p++ = SUBST_CHAR; - if (*p != '\0') { - if (isascii(*p)) - p++; - else - *p++ = SUBST_CHAR; - } - } - } else if (iseuchwkana1(*p)) { - if (iseuchwkana2(*(p + 1))) - /* euc-jp hankaku kana */ - p += 2; - else - *p++ = SUBST_CHAR; - } else if (iseucaux(*p)) { - if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) { - /* auxiliary kanji */ - p += 3; - } else - *p++ = SUBST_CHAR; - } else - /* substitute unprintable 1 byte code */ - *p++ = SUBST_CHAR; - } -} -#endif - -static void conv_unreadable_8bit(gchar *str) -{ - register gchar *p = str; - - while (*p != '\0') { - /* convert CR+LF -> LF */ - if (*p == '\r' && *(p + 1) == '\n') - memmove(p, p + 1, strlen(p)); - else if (!isascii(*(guchar *)p)) *p = SUBST_CHAR; - p++; - } -} - -#if 0 -static void conv_unreadable_latin(gchar *str) -{ - register guchar *p = str; - - while (*p != '\0') { - /* convert CR+LF -> LF */ - if (*p == '\r' && *(p + 1) == '\n') - memmove(p, p + 1, strlen(p)); - else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f) - *p = SUBST_CHAR; - p++; - } -} -#endif - -#define NCV '\0' - -void conv_mb_alnum(gchar *str) -{ - static guchar char_tbl[] = { - /* 0xa0 - 0xaf */ - NCV, ' ', NCV, NCV, ',', '.', NCV, ':', - ';', '?', '!', NCV, NCV, NCV, NCV, NCV, - /* 0xb0 - 0xbf */ - NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, - NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, - /* 0xc0 - 0xcf */ - NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, - NCV, NCV, '(', ')', NCV, NCV, '[', ']', - /* 0xd0 - 0xdf */ - '{', '}', NCV, NCV, NCV, NCV, NCV, NCV, - NCV, NCV, NCV, NCV, '+', '-', NCV, NCV, - /* 0xe0 - 0xef */ - NCV, '=', NCV, '<', '>', NCV, NCV, NCV, - NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV - }; - - register guchar *p = (guchar *)str; - register gint len; - - len = strlen(str); - - while (len > 1) { - if (*p == 0xa3) { - register guchar ch = *(p + 1); - - if (ch >= 0xb0 && ch <= 0xfa) { - /* [a-zA-Z] */ - *p = ch & 0x7f; - p++; - len--; - memmove(p, p + 1, len); - len--; - } else { - p += 2; - len -= 2; - } - } else if (*p == 0xa1) { - register guchar ch = *(p + 1); - - if (ch >= 0xa0 && ch <= 0xef && - NCV != char_tbl[ch - 0xa0]) { - *p = char_tbl[ch - 0xa0]; - p++; - len--; - memmove(p, p + 1, len); - len--; - } else { - p += 2; - len -= 2; - } - } else if (iseuckanji(*p)) { - p += 2; - len -= 2; - } else { - p++; - len--; - } - } -} - -CharSet conv_guess_ja_encoding(const gchar *str) -{ - const guchar *p = (const guchar *)str; - CharSet guessed = C_US_ASCII; - - while (*p != '\0') { - if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) { - if (guessed == C_US_ASCII) - return C_ISO_2022_JP; - p += 2; - } else if (isascii(*p)) { - p++; - } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) { - if (*p >= 0xfd && *p <= 0xfe) - return C_EUC_JP; - else if (guessed == C_SHIFT_JIS) { - if ((issjiskanji1(*p) && - issjiskanji2(*(p + 1))) || - issjishwkana(*p)) - guessed = C_SHIFT_JIS; - else - guessed = C_EUC_JP; - } else - guessed = C_EUC_JP; - p += 2; - } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) { - if (iseuchwkana1(*p) && iseuchwkana2(*(p + 1))) - guessed = C_SHIFT_JIS; - else - return C_SHIFT_JIS; - p += 2; - } else if (issjishwkana(*p)) { - guessed = C_SHIFT_JIS; - p++; - } else { - p++; - } - } - - return guessed; -} - -static gchar *conv_jistodisp(const gchar *inbuf, gint *error) -{ - return conv_jistoutf8(inbuf, error); -} - -static gchar *conv_sjistodisp(const gchar *inbuf, gint *error) -{ - return conv_sjistoutf8(inbuf, error); -} - -static gchar *conv_euctodisp(const gchar *inbuf, gint *error) -{ - return conv_euctoutf8(inbuf, error); -} - -gchar *conv_utf8todisp(const gchar *inbuf, gint *error) -{ - if (g_utf8_validate(inbuf, -1, NULL) == TRUE) { - if (error) - *error = 0; - return g_strdup(inbuf); - } else - return conv_ustodisp(inbuf, error); -} - -static gchar *conv_anytodisp(const gchar *inbuf, gint *error) -{ - gchar *outbuf; - - outbuf = conv_anytoutf8(inbuf, error); - if (g_utf8_validate(outbuf, -1, NULL) != TRUE) { - if (error) - *error = -1; - conv_unreadable_8bit(outbuf); - } - - return outbuf; -} - -static gchar *conv_ustodisp(const gchar *inbuf, gint *error) -{ - gchar *outbuf; - - outbuf = g_strdup(inbuf); - conv_unreadable_8bit(outbuf); - if (error) - *error = 0; - - return outbuf; -} - -gchar *conv_localetodisp(const gchar *inbuf, gint *error) -{ - gchar *str; - - str = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(), - CS_INTERNAL, error); - if (!str) - str = conv_utf8todisp(inbuf, NULL); - - return str; -} - -static gchar *conv_noconv(const gchar *inbuf, gint *error) -{ - if (error) - *error = 0; - return g_strdup(inbuf); -} - -static const gchar * -conv_get_fallback_for_private_encoding(const gchar *encoding) -{ - if (encoding && (encoding[0] == 'X' || encoding[0] == 'x') && - encoding[1] == '-') { - if (!g_ascii_strcasecmp(encoding, CS_X_GBK)) - return CS_GBK; - } - - return encoding; -} - -CodeConverter *conv_code_converter_new(const gchar *src_encoding, - const gchar *dest_encoding) -{ - CodeConverter *conv; - - src_encoding = conv_get_fallback_for_private_encoding(src_encoding); - - conv = g_new0(CodeConverter, 1); - conv->code_conv_func = - conv_get_code_conv_func(src_encoding, dest_encoding); - conv->src_encoding = g_strdup(src_encoding); - conv->dest_encoding = g_strdup(dest_encoding); - - return conv; -} - -void conv_code_converter_destroy(CodeConverter *conv) -{ - g_free(conv->src_encoding); - g_free(conv->dest_encoding); - g_free(conv); -} - -gchar *conv_convert(CodeConverter *conv, const gchar *inbuf) -{ - if (conv->code_conv_func != conv_noconv) - return conv->code_conv_func(inbuf, NULL); - else - return conv_iconv_strdup - (inbuf, conv->src_encoding, conv->dest_encoding, NULL); -} - -gchar *conv_codeset_strdup_full(const gchar *inbuf, - const gchar *src_encoding, - const gchar *dest_encoding, - gint *error) -{ - CodeConvFunc conv_func; - - src_encoding = conv_get_fallback_for_private_encoding(src_encoding); - - conv_func = conv_get_code_conv_func(src_encoding, dest_encoding); - if (conv_func != conv_noconv) - return conv_func(inbuf, error); - - return conv_iconv_strdup(inbuf, src_encoding, dest_encoding, error); -} - -CodeConvFunc conv_get_code_conv_func(const gchar *src_encoding, - const gchar *dest_encoding) -{ - CodeConvFunc code_conv = conv_noconv; - CharSet src_charset; - CharSet dest_charset; - - if (!src_encoding) - src_charset = conv_get_locale_charset(); - else - src_charset = conv_get_charset_from_str(src_encoding); - - /* auto detection mode */ - if (!src_encoding && !dest_encoding) { - if (conv_is_ja_locale()) - return conv_anytodisp; - else - return conv_noconv; - } - - dest_charset = conv_get_charset_from_str(dest_encoding); - - if (dest_charset == C_US_ASCII) - return conv_ustodisp; - - switch (src_charset) { - case C_US_ASCII: - case C_ISO_8859_1: - case C_ISO_8859_2: - case C_ISO_8859_3: - case C_ISO_8859_4: - case C_ISO_8859_5: - case C_ISO_8859_6: - case C_ISO_8859_7: - case C_ISO_8859_8: - case C_ISO_8859_9: - case C_ISO_8859_10: - case C_ISO_8859_11: - case C_ISO_8859_13: - case C_ISO_8859_14: - case C_ISO_8859_15: - break; - case C_ISO_2022_JP: - case C_ISO_2022_JP_2: - case C_ISO_2022_JP_3: - if (dest_charset == C_AUTO) - code_conv = conv_jistodisp; - else if (dest_charset == C_EUC_JP) - code_conv = conv_jistoeuc; - else if (dest_charset == C_UTF_8) - code_conv = conv_jistoutf8; - break; - case C_SHIFT_JIS: - if (dest_charset == C_AUTO) - code_conv = conv_sjistodisp; - else if (dest_charset == C_EUC_JP) - code_conv = conv_sjistoeuc; - else if (dest_charset == C_UTF_8) - code_conv = conv_sjistoutf8; - break; - case C_EUC_JP: - if (dest_charset == C_AUTO) - code_conv = conv_euctodisp; - else if (dest_charset == C_ISO_2022_JP || - dest_charset == C_ISO_2022_JP_2 || - dest_charset == C_ISO_2022_JP_3) - code_conv = conv_euctojis; - else if (dest_charset == C_UTF_8) - code_conv = conv_euctoutf8; - break; - case C_UTF_8: - if (dest_charset == C_EUC_JP) - code_conv = conv_utf8toeuc; - else if (dest_charset == C_ISO_2022_JP || - dest_charset == C_ISO_2022_JP_2 || - dest_charset == C_ISO_2022_JP_3) - code_conv = conv_utf8tojis; - break; - default: - break; - } - - return code_conv; -} - -gchar *conv_iconv_strdup(const gchar *inbuf, - const gchar *src_code, const gchar *dest_code, - gint *error) -{ - iconv_t cd; - gchar *outbuf; - - if (!src_code) - src_code = conv_get_locale_charset_str(); - if (!dest_code) - dest_code = CS_INTERNAL; - - cd = iconv_open(dest_code, src_code); - if (cd == (iconv_t)-1) { - if (error) - *error = -1; - return NULL; - } - - outbuf = conv_iconv_strdup_with_cd(inbuf, cd, error); - - iconv_close(cd); - - return outbuf; -} - -gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd, gint *error) -{ - const gchar *inbuf_p; - gchar *outbuf; - gchar *outbuf_p; - size_t in_size; - size_t in_left; - size_t out_size; - size_t out_left; - size_t n_conv; - size_t len; - gint error_ = 0; - - inbuf_p = inbuf; - in_size = strlen(inbuf); - in_left = in_size; - out_size = (in_size + 1) * 2; - outbuf = g_malloc(out_size); - outbuf_p = outbuf; - out_left = out_size; - -#define EXPAND_BUF() \ -{ \ - len = outbuf_p - outbuf; \ - out_size *= 2; \ - outbuf = g_realloc(outbuf, out_size); \ - outbuf_p = outbuf + len; \ - out_left = out_size - len; \ -} - - while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left, - &outbuf_p, &out_left)) == (size_t)-1) { - if (EILSEQ == errno) { - /* g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno)); */ - error_ = -1; - inbuf_p++; - in_left--; - if (out_left == 0) { - EXPAND_BUF(); - } - *outbuf_p++ = SUBST_CHAR; - out_left--; - } else if (EINVAL == errno) { - error_ = -1; - break; - } else if (E2BIG == errno) { - EXPAND_BUF(); - } else { - g_warning("conv_iconv_strdup(): %s\n", - g_strerror(errno)); - error_ = -1; - break; - } - } - - while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) == - (size_t)-1) { - if (E2BIG == errno) { - EXPAND_BUF(); - } else { - g_warning("conv_iconv_strdup(): %s\n", - g_strerror(errno)); - error_ = -1; - break; - } - } - -#undef EXPAND_BUF - - len = outbuf_p - outbuf; - outbuf = g_realloc(outbuf, len + 1); - outbuf[len] = '\0'; - - if (error) - *error = error_; - - return outbuf; -} - -static const struct { - CharSet charset; - gchar *const name; -} charsets[] = { - {C_US_ASCII, CS_US_ASCII}, - {C_US_ASCII, CS_ANSI_X3_4_1968}, - {C_UTF_8, CS_UTF_8}, - {C_UTF_7, CS_UTF_7}, - {C_ISO_8859_1, CS_ISO_8859_1}, - {C_ISO_8859_2, CS_ISO_8859_2}, - {C_ISO_8859_3, CS_ISO_8859_3}, - {C_ISO_8859_4, CS_ISO_8859_4}, - {C_ISO_8859_5, CS_ISO_8859_5}, - {C_ISO_8859_6, CS_ISO_8859_6}, - {C_ISO_8859_7, CS_ISO_8859_7}, - {C_ISO_8859_8, CS_ISO_8859_8}, - {C_ISO_8859_9, CS_ISO_8859_9}, - {C_ISO_8859_10, CS_ISO_8859_10}, - {C_ISO_8859_11, CS_ISO_8859_11}, - {C_ISO_8859_13, CS_ISO_8859_13}, - {C_ISO_8859_14, CS_ISO_8859_14}, - {C_ISO_8859_15, CS_ISO_8859_15}, - {C_BALTIC, CS_BALTIC}, - {C_CP1250, CS_CP1250}, - {C_CP1251, CS_CP1251}, - {C_CP1252, CS_CP1252}, - {C_CP1253, CS_CP1253}, - {C_CP1254, CS_CP1254}, - {C_CP1255, CS_CP1255}, - {C_CP1256, CS_CP1256}, - {C_CP1257, CS_CP1257}, - {C_CP1258, CS_CP1258}, - {C_WINDOWS_1250, CS_WINDOWS_1250}, - {C_WINDOWS_1251, CS_WINDOWS_1251}, - {C_WINDOWS_1252, CS_WINDOWS_1252}, - {C_WINDOWS_1253, CS_WINDOWS_1253}, - {C_WINDOWS_1254, CS_WINDOWS_1254}, - {C_WINDOWS_1255, CS_WINDOWS_1255}, - {C_WINDOWS_1256, CS_WINDOWS_1256}, - {C_WINDOWS_1257, CS_WINDOWS_1257}, - {C_WINDOWS_1258, CS_WINDOWS_1258}, - {C_KOI8_R, CS_KOI8_R}, - {C_KOI8_T, CS_KOI8_T}, - {C_KOI8_U, CS_KOI8_U}, - {C_ISO_2022_JP, CS_ISO_2022_JP}, - {C_ISO_2022_JP_2, CS_ISO_2022_JP_2}, - {C_ISO_2022_JP_3, CS_ISO_2022_JP_3}, - {C_EUC_JP, CS_EUC_JP}, - {C_EUC_JP, CS_EUCJP}, - {C_EUC_JP_MS, CS_EUC_JP_MS}, - {C_SHIFT_JIS, CS_SHIFT_JIS}, - {C_SHIFT_JIS, CS_SHIFT__JIS}, - {C_SHIFT_JIS, CS_SJIS}, - {C_ISO_2022_KR, CS_ISO_2022_KR}, - {C_EUC_KR, CS_EUC_KR}, - {C_ISO_2022_CN, CS_ISO_2022_CN}, - {C_EUC_CN, CS_EUC_CN}, - {C_GB2312, CS_GB2312}, - {C_GBK, CS_GBK}, - {C_EUC_TW, CS_EUC_TW}, - {C_BIG5, CS_BIG5}, - {C_BIG5_HKSCS, CS_BIG5_HKSCS}, - {C_TIS_620, CS_TIS_620}, - {C_WINDOWS_874, CS_WINDOWS_874}, - {C_GEORGIAN_PS, CS_GEORGIAN_PS}, - {C_TCVN5712_1, CS_TCVN5712_1}, -}; - -static const struct { - gchar *const locale; - CharSet charset; - CharSet out_charset; -} locale_table[] = { - {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP}, - {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP}, - {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP}, - {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP}, - {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP}, - {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP}, -#ifdef G_OS_WIN32 - {"ja_JP" , C_SHIFT_JIS , C_ISO_2022_JP}, -#else - {"ja_JP" , C_EUC_JP , C_ISO_2022_JP}, -#endif - {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR}, - {"ko_KR" , C_EUC_KR , C_EUC_KR}, - {"zh_CN.GB2312" , C_GB2312 , C_GB2312}, - {"zh_CN.GBK" , C_GBK , C_GBK}, - {"zh_CN" , C_GB2312 , C_GB2312}, - {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS}, - {"zh_TW.eucTW" , C_EUC_TW , C_BIG5}, - {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5}, - {"zh_TW.Big5" , C_BIG5 , C_BIG5}, - {"zh_TW" , C_BIG5 , C_BIG5}, - - {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R}, - {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R}, - {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R}, - {"ru_RU" , C_ISO_8859_5 , C_KOI8_R}, - {"tg_TJ" , C_KOI8_T , C_KOI8_T}, - {"ru_UA" , C_KOI8_U , C_KOI8_U}, - {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U}, - {"uk_UA" , C_KOI8_U , C_KOI8_U}, - - {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251}, - {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251}, - - {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255}, - - {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, - {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1}, - {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1}, - {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1}, - {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1}, - {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_US" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, - {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_US" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1}, - {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1}, - {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1}, - {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1}, - {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1}, - {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1}, - {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1}, - {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1}, - {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1}, - {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1}, - {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1}, - {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1}, - {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1}, - {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1}, - {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1}, - {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1}, - {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1}, - {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1}, - {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1}, - {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1}, - {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1}, - - {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2}, - {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2}, - {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2}, - {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2}, - {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2}, - {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2}, - {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2}, - {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2}, - - {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5}, - {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2}, - - {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3}, - - {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4}, - {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, - {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, - {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13}, - - {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5}, - - {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6}, - {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6}, - - {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7}, - {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8}, - {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8}, - {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9}, - - {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13}, - {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13}, - - {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14}, - - {"ar_IN" , C_UTF_8 , C_UTF_8}, - {"en_IN" , C_UTF_8 , C_UTF_8}, - {"se_NO" , C_UTF_8 , C_UTF_8}, - {"ta_IN" , C_UTF_8 , C_UTF_8}, - {"te_IN" , C_UTF_8 , C_UTF_8}, - {"ur_PK" , C_UTF_8 , C_UTF_8}, - - {"th_TH" , C_TIS_620 , C_TIS_620}, - /* {"th_TH" , C_WINDOWS_874}, */ - /* {"th_TH" , C_ISO_8859_11}, */ - - {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS}, - {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1}, - - {"C" , C_US_ASCII , C_US_ASCII}, - {"POSIX" , C_US_ASCII , C_US_ASCII}, - {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII}, -}; - -static GHashTable *conv_get_charset_to_str_table(void) -{ - static GHashTable *table; - gint i; - - if (table) - return table; - - table = g_hash_table_new(NULL, g_direct_equal); - - for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { - if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset)) - == NULL) { - g_hash_table_insert - (table, GUINT_TO_POINTER(charsets[i].charset), - charsets[i].name); - } - } - - return table; -} - -static GHashTable *conv_get_charset_from_str_table(void) -{ - static GHashTable *table; - gint i; - - if (table) - return table; - - table = g_hash_table_new(str_case_hash, str_case_equal); - - for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { - g_hash_table_insert(table, charsets[i].name, - GUINT_TO_POINTER(charsets[i].charset)); - } - - return table; -} - -const gchar *conv_get_charset_str(CharSet charset) -{ - GHashTable *table; - - table = conv_get_charset_to_str_table(); - return g_hash_table_lookup(table, GUINT_TO_POINTER(charset)); -} - -CharSet conv_get_charset_from_str(const gchar *charset) -{ - GHashTable *table; - - if (!charset) return C_AUTO; - - table = conv_get_charset_from_str_table(); - return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset)); -} - -CharSet conv_get_locale_charset(void) -{ - static CharSet cur_charset = -1; - const gchar *cur_locale; - const gchar *p; - gint i; - - if (cur_charset != -1) - return cur_charset; - - cur_locale = conv_get_current_locale(); - if (!cur_locale) { - cur_charset = C_US_ASCII; - return cur_charset; - } - - if (strcasestr(cur_locale, "UTF-8")) { - cur_charset = C_UTF_8; - return cur_charset; - } - - if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { - cur_charset = C_ISO_8859_15; - return cur_charset; - } - - for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { - const gchar *p; - - /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and - "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */ - if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, - strlen(locale_table[i].locale))) { - cur_charset = locale_table[i].charset; - return cur_charset; - } else if ((p = strchr(locale_table[i].locale, '_')) && - !strchr(p + 1, '.')) { - if (strlen(cur_locale) == 2 && - !g_ascii_strncasecmp(cur_locale, - locale_table[i].locale, 2)) { - cur_charset = locale_table[i].charset; - return cur_charset; - } - } - } - - cur_charset = C_AUTO; - return cur_charset; -} - -const gchar *conv_get_locale_charset_str(void) -{ - static const gchar *codeset = NULL; - - if (!codeset) - codeset = conv_get_charset_str(conv_get_locale_charset()); - - return codeset ? codeset : CS_INTERNAL; -} - -CharSet conv_get_internal_charset(void) -{ - return C_INTERNAL; -} - -const gchar *conv_get_internal_charset_str(void) -{ - return CS_INTERNAL; -} - -CharSet conv_get_outgoing_charset(void) -{ - static CharSet out_charset = -1; - const gchar *cur_locale; - const gchar *p; - gint i; - - if (out_charset != -1) - return out_charset; - - cur_locale = conv_get_current_locale(); - if (!cur_locale) { - out_charset = C_AUTO; - return out_charset; - } - - if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { - out_charset = C_ISO_8859_15; - return out_charset; - } - - for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { - const gchar *p; - - if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, - strlen(locale_table[i].locale))) { - out_charset = locale_table[i].out_charset; - break; - } else if ((p = strchr(locale_table[i].locale, '_')) && - !strchr(p + 1, '.')) { - if (strlen(cur_locale) == 2 && - !g_ascii_strncasecmp(cur_locale, - locale_table[i].locale, 2)) { - out_charset = locale_table[i].out_charset; - break; - } - } - } - - return out_charset; -} - -const gchar *conv_get_outgoing_charset_str(void) -{ - CharSet out_charset; - const gchar *str; - - out_charset = conv_get_outgoing_charset(); - str = conv_get_charset_str(out_charset); - - return str ? str : CS_UTF_8; -} - -gboolean conv_is_multibyte_encoding(CharSet encoding) -{ - switch (encoding) { - case C_EUC_JP: - case C_EUC_JP_MS: - case C_EUC_KR: - case C_EUC_TW: - case C_EUC_CN: - case C_ISO_2022_JP: - case C_ISO_2022_JP_2: - case C_ISO_2022_JP_3: - case C_ISO_2022_KR: - case C_ISO_2022_CN: - case C_SHIFT_JIS: - case C_GB2312: - case C_GBK: - case C_BIG5: - case C_UTF_8: - case C_UTF_7: - return TRUE; - default: - return FALSE; - } -} - -const gchar *conv_get_current_locale(void) -{ - static const gchar *cur_locale; - - if (!cur_locale) { -#ifdef G_OS_WIN32 - cur_locale = g_win32_getlocale(); -#else - cur_locale = g_getenv("LC_ALL"); - if (!cur_locale) cur_locale = g_getenv("LC_CTYPE"); - if (!cur_locale) cur_locale = g_getenv("LANG"); - if (!cur_locale) cur_locale = setlocale(LC_CTYPE, NULL); -#endif /* G_OS_WIN32 */ - - debug_print("current locale: %s\n", - cur_locale ? cur_locale : "(none)"); - } - - return cur_locale; -} - -gboolean conv_is_ja_locale(void) -{ - static gint is_ja_locale = -1; - const gchar *cur_locale; - - if (is_ja_locale != -1) - return is_ja_locale != 0; - - is_ja_locale = 0; - cur_locale = conv_get_current_locale(); - if (cur_locale) { - if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0) - is_ja_locale = 1; - } - - return is_ja_locale != 0; -} - -gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding) -{ - gchar *buf; - gchar *decoded_str; - - if (is_ascii_str(str)) - return unmime_header(str); - - if (default_encoding) { - buf = conv_codeset_strdup - (str, default_encoding, CS_INTERNAL); - if (buf) { - decoded_str = unmime_header(buf); - g_free(buf); - return decoded_str; - } - } - - if (conv_is_ja_locale()) - buf = conv_anytodisp(str, NULL); - else - buf = conv_localetodisp(str, NULL); - - decoded_str = unmime_header(buf); - g_free(buf); - - return decoded_str; -} - -#define MAX_LINELEN 76 -#define MAX_HARD_LINELEN 996 -#define MIMESEP_BEGIN "=?" -#define MIMESEP_END "?=" - -#define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0)) - -#define LBREAK_IF_REQUIRED(cond, is_plain_text) \ -{ \ - if (len - (destp - dest) < MAX_LINELEN + 2) { \ - *destp = '\0'; \ - return; \ - } \ - \ - if ((cond) && *srcp) { \ - if (destp > dest && left < MAX_LINELEN - 1) { \ - if (g_ascii_isspace(*(destp - 1))) \ - destp--; \ - else if (is_plain_text && \ - g_ascii_isspace(*srcp)) \ - srcp++; \ - if (*srcp) { \ - *destp++ = '\n'; \ - *destp++ = ' '; \ - left = MAX_LINELEN - 1; \ - } \ - } \ - } \ -} - -void conv_encode_header(gchar *dest, gint len, const gchar *src, - gint header_len, gboolean addr_field, - const gchar *out_encoding) -{ - const gchar *cur_encoding; - gint mimestr_len; - gchar *mimesep_enc; - gint left; - const gchar *srcp = src; - gchar *destp = dest; - gboolean use_base64; - - g_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE); - - if (MB_CUR_MAX > 1) { - use_base64 = TRUE; - mimesep_enc = "?B?"; - } else { - use_base64 = FALSE; - mimesep_enc = "?Q?"; - } - - cur_encoding = CS_INTERNAL; - if (!out_encoding) - out_encoding = conv_get_outgoing_charset_str(); - if (!strcmp(out_encoding, CS_US_ASCII)) - out_encoding = CS_ISO_8859_1; - - mimestr_len = strlen(MIMESEP_BEGIN) + strlen(out_encoding) + - strlen(mimesep_enc) + strlen(MIMESEP_END); - - left = MAX_LINELEN - header_len; - - while (*srcp) { - LBREAK_IF_REQUIRED(left <= 0, TRUE); - - while (g_ascii_isspace(*srcp)) { - *destp++ = *srcp++; - left--; - LBREAK_IF_REQUIRED(left <= 0, TRUE); - } - - /* output as it is if the next word is ASCII string */ - if (!is_next_nonascii(srcp)) { - gint word_len; - - word_len = get_next_word_len(srcp); - LBREAK_IF_REQUIRED(left < word_len, TRUE); - while (word_len > 0) { - LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE) - *destp++ = *srcp++; - left--; - word_len--; - } - - continue; - } - - /* don't include parentheses in encoded strings */ - if (addr_field && (*srcp == '(' || *srcp == ')')) { - LBREAK_IF_REQUIRED(left < 2, FALSE); - *destp++ = *srcp++; - left--; - } - - while (1) { - gint mb_len = 0; - gint cur_len = 0; - gchar *part_str; - gchar *out_str; - gchar *enc_str; - const gchar *p = srcp; - gint out_str_len; - gint out_enc_str_len; - gint mime_block_len; - gboolean cont = FALSE; - - while (*p != '\0') { - if (g_ascii_isspace(*p) && - !is_next_nonascii(p + 1)) - break; - /* don't include parentheses in encoded - strings */ - if (addr_field && (*p == '(' || *p == ')')) - break; - - mb_len = g_utf8_skip[*(guchar *)p]; - - Xstrndup_a(part_str, srcp, cur_len + mb_len, ); - out_str = conv_codeset_strdup - (part_str, cur_encoding, out_encoding); - if (!out_str) { - g_warning("conv_encode_header(): code conversion failed\n"); - conv_unreadable_8bit(part_str); - out_str = g_strdup(part_str); - } - out_str_len = strlen(out_str); - - if (use_base64) - out_enc_str_len = B64LEN(out_str_len); - else - out_enc_str_len = - qp_get_q_encoding_len - ((guchar *)out_str); - - g_free(out_str); - - if (mimestr_len + out_enc_str_len <= left) { - cur_len += mb_len; - p += mb_len; - } else if (cur_len == 0) { - LBREAK_IF_REQUIRED(1, FALSE); - continue; - } else { - cont = TRUE; - break; - } - } - - if (cur_len > 0) { - Xstrndup_a(part_str, srcp, cur_len, ); - out_str = conv_codeset_strdup - (part_str, cur_encoding, out_encoding); - if (!out_str) { - g_warning("conv_encode_header(): code conversion failed\n"); - conv_unreadable_8bit(part_str); - out_str = g_strdup(part_str); - } - out_str_len = strlen(out_str); - - if (use_base64) - out_enc_str_len = B64LEN(out_str_len); - else - out_enc_str_len = - qp_get_q_encoding_len - ((guchar *)out_str); - - Xalloca(enc_str, out_enc_str_len + 1, ); - if (use_base64) - base64_encode(enc_str, - (guchar *)out_str, - out_str_len); - else - qp_q_encode(enc_str, (guchar *)out_str); - - g_free(out_str); - - /* output MIME-encoded string block */ - mime_block_len = mimestr_len + strlen(enc_str); - g_snprintf(destp, mime_block_len + 1, - MIMESEP_BEGIN "%s%s%s" MIMESEP_END, - out_encoding, mimesep_enc, enc_str); - destp += mime_block_len; - srcp += cur_len; - - left -= mime_block_len; - } - - LBREAK_IF_REQUIRED(cont, FALSE); - - if (cur_len == 0) - break; - } - } - - *destp = '\0'; -} - -#undef LBREAK_IF_REQUIRED - -gint conv_copy_file(const gchar *src, const gchar *dest, const gchar *encoding) -{ - FILE *src_fp, *dest_fp; - gchar buf[BUFFSIZE]; - CodeConverter *conv; - gboolean err = FALSE; - - if ((src_fp = g_fopen(src, "rb")) == NULL) { - FILE_OP_ERROR(src, "fopen"); - return -1; - } - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - fclose(src_fp); - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning("can't change file mode\n"); - } - - conv = conv_code_converter_new(encoding, NULL); - - while (fgets(buf, sizeof(buf), src_fp) != NULL) { - gchar *outbuf; - - outbuf = conv_convert(conv, buf); - if (outbuf) { - fputs(outbuf, dest_fp); - g_free(outbuf); - } else - fputs(buf, dest_fp); - } - - conv_code_converter_destroy(conv); - - if (ferror(src_fp)) { - FILE_OP_ERROR(src, "fgets"); - err = TRUE; - } - fclose(src_fp); - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; - } - if (err) { - g_unlink(dest); - return -1; - } - - return 0; -} - -gint conv_copy_dir(const gchar *src, const gchar *dest, const gchar *encoding) -{ - GDir *dir; - const gchar *dir_name; - gchar *src_file; - gchar *dest_file; - - if ((dir = g_dir_open(src, 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", src); - return -1; - } - - if (make_dir_hier(dest) < 0) { - g_dir_close(dir); - return -1; - } - - while ((dir_name = g_dir_read_name(dir)) != NULL) { - src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); - dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, - NULL); - if (is_file_exist(src_file)) - conv_copy_file(src_file, dest_file, encoding); - g_free(dest_file); - g_free(src_file); - } - - g_dir_close(dir); - - return 0; -} - -gchar *conv_filename_from_utf8(const gchar *utf8_file) -{ - gchar *fs_file; - GError *error = NULL; - - fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error); - if (error) { - g_warning("failed to convert encoding of file name: %s\n", - error->message); - g_error_free(error); - } - if (!fs_file) - fs_file = g_strdup(utf8_file); - - return fs_file; -} - -gchar *conv_filename_to_utf8(const gchar *fs_file) -{ - gchar *utf8_file; - GError *error = NULL; - - utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error); - if (error) { - g_warning("failed to convert encoding of file name: %s\n", - error->message); - g_error_free(error); - } - if (!utf8_file) - utf8_file = g_strdup(fs_file); - - return utf8_file; -} diff --git a/src/codeconv.h b/src/codeconv.h deleted file mode 100644 index 833b1402..00000000 --- a/src/codeconv.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __CODECONV_H__ -#define __CODECONV_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include - -typedef struct _CodeConverter CodeConverter; - -typedef enum -{ - C_AUTO, - C_US_ASCII, - C_UTF_8, - C_UTF_7, - C_ISO_8859_1, - C_ISO_8859_2, - C_ISO_8859_3, - C_ISO_8859_4, - C_ISO_8859_5, - C_ISO_8859_6, - C_ISO_8859_7, - C_ISO_8859_8, - C_ISO_8859_9, - C_ISO_8859_10, - C_ISO_8859_11, - C_ISO_8859_13, - C_ISO_8859_14, - C_ISO_8859_15, - C_BALTIC, - C_CP1250, - C_CP1251, - C_CP1252, - C_CP1253, - C_CP1254, - C_CP1255, - C_CP1256, - C_CP1257, - C_CP1258, - C_WINDOWS_1250, - C_WINDOWS_1251, - C_WINDOWS_1252, - C_WINDOWS_1253, - C_WINDOWS_1254, - C_WINDOWS_1255, - C_WINDOWS_1256, - C_WINDOWS_1257, - C_WINDOWS_1258, - C_KOI8_R, - C_KOI8_T, - C_KOI8_U, - C_ISO_2022_JP, - C_ISO_2022_JP_2, - C_ISO_2022_JP_3, - C_EUC_JP, - C_EUC_JP_MS, - C_SHIFT_JIS, - C_ISO_2022_KR, - C_EUC_KR, - C_ISO_2022_CN, - C_EUC_CN, - C_GB2312, - C_GBK, - C_EUC_TW, - C_BIG5, - C_BIG5_HKSCS, - C_TIS_620, - C_WINDOWS_874, - C_GEORGIAN_PS, - C_TCVN5712_1 -} CharSet; - -typedef gchar *(*CodeConvFunc) (const gchar *inbuf, gint *error); - -struct _CodeConverter -{ - CodeConvFunc code_conv_func; - gchar *src_encoding; - gchar *dest_encoding; -}; - -#define CS_AUTO "AUTO" -#define CS_US_ASCII "US-ASCII" -#define CS_ANSI_X3_4_1968 "ANSI_X3.4-1968" -#define CS_UTF_8 "UTF-8" -#define CS_UTF_7 "UTF-7" -#define CS_ISO_8859_1 "ISO-8859-1" -#define CS_ISO_8859_2 "ISO-8859-2" -#define CS_ISO_8859_3 "ISO-8859-3" -#define CS_ISO_8859_4 "ISO-8859-4" -#define CS_ISO_8859_5 "ISO-8859-5" -#define CS_ISO_8859_6 "ISO-8859-6" -#define CS_ISO_8859_7 "ISO-8859-7" -#define CS_ISO_8859_8 "ISO-8859-8" -#define CS_ISO_8859_9 "ISO-8859-9" -#define CS_ISO_8859_10 "ISO-8859-10" -#define CS_ISO_8859_11 "ISO-8859-11" -#define CS_ISO_8859_13 "ISO-8859-13" -#define CS_ISO_8859_14 "ISO-8859-14" -#define CS_ISO_8859_15 "ISO-8859-15" -#define CS_BALTIC "BALTIC" -#define CS_CP1250 "CP1250" -#define CS_CP1251 "CP1251" -#define CS_CP1252 "CP1252" -#define CS_CP1253 "CP1253" -#define CS_CP1254 "CP1254" -#define CS_CP1255 "CP1255" -#define CS_CP1256 "CP1256" -#define CS_CP1257 "CP1257" -#define CS_CP1258 "CP1258" -#define CS_WINDOWS_1250 "Windows-1250" -#define CS_WINDOWS_1251 "Windows-1251" -#define CS_WINDOWS_1252 "Windows-1252" -#define CS_WINDOWS_1253 "Windows-1253" -#define CS_WINDOWS_1254 "Windows-1254" -#define CS_WINDOWS_1255 "Windows-1255" -#define CS_WINDOWS_1256 "Windows-1256" -#define CS_WINDOWS_1257 "Windows-1257" -#define CS_WINDOWS_1258 "Windows-1258" -#define CS_KOI8_R "KOI8-R" -#define CS_KOI8_T "KOI8-T" -#define CS_KOI8_U "KOI8-U" -#define CS_ISO_2022_JP "ISO-2022-JP" -#define CS_ISO_2022_JP_2 "ISO-2022-JP-2" -#define CS_ISO_2022_JP_3 "ISO-2022-JP-3" -#define CS_EUC_JP "EUC-JP" -#define CS_EUCJP "EUCJP" -#define CS_EUC_JP_MS "EUC-JP-MS" -#define CS_SHIFT_JIS "Shift_JIS" -#define CS_SHIFT__JIS "SHIFT-JIS" -#define CS_SJIS "SJIS" -#define CS_X_SJIS "X-SJIS" -#define CS_ISO_2022_KR "ISO-2022-KR" -#define CS_EUC_KR "EUC-KR" -#define CS_ISO_2022_CN "ISO-2022-CN" -#define CS_EUC_CN "EUC-CN" -#define CS_GB2312 "GB2312" -#define CS_GBK "GBK" -#define CS_X_GBK "X-GBK" -#define CS_EUC_TW "EUC-TW" -#define CS_BIG5 "Big5" -#define CS_BIG5_HKSCS "BIG5-HKSCS" -#define CS_TIS_620 "TIS-620" -#define CS_WINDOWS_874 "Windows-874" -#define CS_GEORGIAN_PS "GEORGIAN-PS" -#define CS_TCVN5712_1 "TCVN5712-1" - -#define C_INTERNAL C_UTF_8 -#define CS_INTERNAL CS_UTF_8 - -//void conv_mb_alnum(gchar *str); - -CharSet conv_guess_ja_encoding (const gchar *str); - -gchar *conv_utf8todisp (const gchar *inbuf, - gint *error); -gchar *conv_localetodisp (const gchar *inbuf, - gint *error); - -CodeConverter *conv_code_converter_new (const gchar *src_encoding, - const gchar *dest_encoding); -void conv_code_converter_destroy (CodeConverter *conv); -gchar *conv_convert (CodeConverter *conv, - const gchar *inbuf); - -#define conv_codeset_strdup(inbuf, src_code, dest_code) \ - (conv_codeset_strdup_full(inbuf, src_code, dest_code, NULL)) - -gchar *conv_codeset_strdup_full (const gchar *inbuf, - const gchar *src_encoding, - const gchar *dest_encoding, - gint *error); - -CodeConvFunc conv_get_code_conv_func (const gchar *src_encoding, - const gchar *dest_encoding); - -gchar *conv_iconv_strdup (const gchar *inbuf, - const gchar *src_encoding, - const gchar *dest_encoding, - gint *error); -gchar *conv_iconv_strdup_with_cd (const gchar *inbuf, - iconv_t cd, - gint *error); - -const gchar *conv_get_charset_str (CharSet charset); -CharSet conv_get_charset_from_str (const gchar *charset); -CharSet conv_get_locale_charset (void); -const gchar *conv_get_locale_charset_str (void); -CharSet conv_get_internal_charset (void); -const gchar *conv_get_internal_charset_str (void); -CharSet conv_get_outgoing_charset (void); -const gchar *conv_get_outgoing_charset_str (void); -gboolean conv_is_multibyte_encoding (CharSet encoding); - -const gchar *conv_get_current_locale (void); -gboolean conv_is_ja_locale (void); - -gchar *conv_unmime_header (const gchar *str, - const gchar *default_encoding); -void conv_encode_header (gchar *dest, - gint len, - const gchar *src, - gint header_len, - gboolean addr_field, - const gchar *out_encoding); - -gint conv_copy_file (const gchar *src, - const gchar *dest, - const gchar *src_encoding); -gint conv_copy_dir (const gchar *src, - const gchar *dest, - const gchar *src_encoding); - -gchar *conv_filename_from_utf8 (const gchar *utf8_file); -gchar *conv_filename_to_utf8 (const gchar *fs_file); - -#endif /* __CODECONV_H__ */ diff --git a/src/prefs.c b/src/prefs.c deleted file mode 100644 index 4579df4f..00000000 --- a/src/prefs.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "prefs.h" -#include "codeconv.h" -#include "utils.h" - -typedef enum -{ - DUMMY_PARAM -} DummyEnum; - -void prefs_read_config(PrefParam *param, const gchar *label, - const gchar *rcfile, const gchar *encoding) -{ - FILE *fp; - gchar buf[PREFSBUFSIZE]; - gchar *block_label; - - g_return_if_fail(param != NULL); - g_return_if_fail(label != NULL); - g_return_if_fail(rcfile != NULL); - - debug_print("Reading configuration...\n"); - - prefs_set_default(param); - - if ((fp = g_fopen(rcfile, "rb")) == NULL) { - if (ENOENT != errno) FILE_OP_ERROR(rcfile, "fopen"); - return; - } - - block_label = g_strdup_printf("[%s]", label); - - /* search aiming block */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - gint val; - - if (encoding) { - gchar *conv_str; - - conv_str = conv_codeset_strdup - (buf, encoding, CS_INTERNAL); - if (!conv_str) - conv_str = g_strdup(buf); - val = strncmp - (conv_str, block_label, strlen(block_label)); - g_free(conv_str); - } else - val = strncmp(buf, block_label, strlen(block_label)); - if (val == 0) { - debug_print("Found %s\n", block_label); - break; - } - } - g_free(block_label); - - while (fgets(buf, sizeof(buf), fp) != NULL) { - strretchomp(buf); - /* reached next block */ - if (buf[0] == '[') break; - - if (encoding) { - gchar *conv_str; - - conv_str = conv_codeset_strdup - (buf, encoding, CS_INTERNAL); - if (!conv_str) - conv_str = g_strdup(buf); - prefs_config_parse_one_line(param, conv_str); - g_free(conv_str); - } else - prefs_config_parse_one_line(param, buf); - } - - debug_print("Finished reading configuration.\n"); - fclose(fp); -} - -void prefs_config_parse_one_line(PrefParam *param, const gchar *buf) -{ - gint i; - gint name_len; - const gchar *value; - - for (i = 0; param[i].name != NULL; i++) { - name_len = strlen(param[i].name); - if (g_ascii_strncasecmp(buf, param[i].name, name_len)) - continue; - if (buf[name_len] != '=') - continue; - value = buf + name_len + 1; - /* debug_print("%s = %s\n", param[i].name, value); */ - - switch (param[i].type) { - case P_STRING: - g_free(*((gchar **)param[i].data)); - *((gchar **)param[i].data) = - *value ? g_strdup(value) : NULL; - break; - case P_INT: - *((gint *)param[i].data) = - (gint)atoi(value); - break; - case P_BOOL: - *((gboolean *)param[i].data) = - (*value == '0' || *value == '\0') - ? FALSE : TRUE; - break; - case P_ENUM: - *((DummyEnum *)param[i].data) = - (DummyEnum)atoi(value); - break; - case P_USHORT: - *((gushort *)param[i].data) = - (gushort)atoi(value); - break; - default: - break; - } - } -} - -#define TRY(func) \ -if (!(func)) \ -{ \ - g_warning(_("failed to write configuration to file\n")); \ - if (orig_fp) fclose(orig_fp); \ - prefs_file_close_revert(pfile); \ - g_free(rcpath); \ - g_free(block_label); \ - return; \ -} \ - -void prefs_write_config(PrefParam *param, const gchar *label, - const gchar *rcfile) -{ - FILE *orig_fp; - PrefFile *pfile; - gchar *rcpath; - gchar buf[PREFSBUFSIZE]; - gchar *block_label = NULL; - gboolean block_matched = FALSE; - - g_return_if_fail(param != NULL); - g_return_if_fail(label != NULL); - g_return_if_fail(rcfile != NULL); - - rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL); - if ((orig_fp = g_fopen(rcpath, "rb")) == NULL) { - if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); - } - - if ((pfile = prefs_file_open(rcpath)) == NULL) { - g_warning(_("failed to write configuration to file\n")); - if (orig_fp) fclose(orig_fp); - g_free(rcpath); - return; - } - - block_label = g_strdup_printf("[%s]", label); - - /* search aiming block */ - if (orig_fp) { - while (fgets(buf, sizeof(buf), orig_fp) != NULL) { - gint val; - - val = strncmp(buf, block_label, strlen(block_label)); - if (val == 0) { - debug_print(_("Found %s\n"), block_label); - block_matched = TRUE; - break; - } else - TRY(fputs(buf, pfile->fp) != EOF); - } - } - - TRY(fprintf(pfile->fp, "%s\n", block_label) > 0); - g_free(block_label); - block_label = NULL; - - /* write all param data to file */ - TRY(prefs_file_write_param(pfile, param) == 0); - - if (block_matched) { - while (fgets(buf, sizeof(buf), orig_fp) != NULL) { - /* next block */ - if (buf[0] == '[') { - TRY(fputc('\n', pfile->fp) != EOF && - fputs(buf, pfile->fp) != EOF); - break; - } - } - while (fgets(buf, sizeof(buf), orig_fp) != NULL) - TRY(fputs(buf, pfile->fp) != EOF); - } - - if (orig_fp) fclose(orig_fp); - if (prefs_file_close(pfile) < 0) - g_warning(_("failed to write configuration to file\n")); - g_free(rcpath); - - debug_print(_("Configuration is saved.\n")); -} - -gint prefs_file_write_param(PrefFile *pfile, PrefParam *param) -{ - gint i; - gchar buf[PREFSBUFSIZE]; - - for (i = 0; param[i].name != NULL; i++) { - switch (param[i].type) { - case P_STRING: - g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name, - *((gchar **)param[i].data) ? - *((gchar **)param[i].data) : ""); - break; - case P_INT: - g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, - *((gint *)param[i].data)); - break; - case P_BOOL: - g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, - *((gboolean *)param[i].data)); - break; - case P_ENUM: - g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, - *((DummyEnum *)param[i].data)); - break; - case P_USHORT: - g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, - *((gushort *)param[i].data)); - break; - default: - buf[0] = '\0'; - } - - if (buf[0] != '\0') { - if (fputs(buf, pfile->fp) == EOF) { - perror("fputs"); - return -1; - } - } - } - - return 0; -} - -PrefFile *prefs_file_open(const gchar *path) -{ - PrefFile *pfile; - gchar *tmppath; - FILE *fp; - - g_return_val_if_fail(path != NULL, NULL); - - tmppath = g_strconcat(path, ".tmp", NULL); - if ((fp = g_fopen(tmppath, "wb")) == NULL) { - FILE_OP_ERROR(tmppath, "fopen"); - g_free(tmppath); - return NULL; - } - - if (change_file_mode_rw(fp, tmppath) < 0) - FILE_OP_ERROR(tmppath, "chmod"); - - g_free(tmppath); - - pfile = g_new(PrefFile, 1); - pfile->fp = fp; - pfile->path = g_strdup(path); - - return pfile; -} - -gint prefs_file_close(PrefFile *pfile) -{ - FILE *fp; - gchar *path; - gchar *tmppath; - gchar *bakpath = NULL; - - g_return_val_if_fail(pfile != NULL, -1); - - fp = pfile->fp; - path = pfile->path; - g_free(pfile); - - tmppath = g_strconcat(path, ".tmp", NULL); - if (fclose(fp) == EOF) { - FILE_OP_ERROR(tmppath, "fclose"); - g_unlink(tmppath); - g_free(path); - g_free(tmppath); - return -1; - } - - if (is_file_exist(path)) { - bakpath = g_strconcat(path, ".bak", NULL); - if (rename_force(path, bakpath) < 0) { - FILE_OP_ERROR(path, "rename"); - g_unlink(tmppath); - g_free(path); - g_free(tmppath); - g_free(bakpath); - return -1; - } - } - - if (rename_force(tmppath, path) < 0) { - FILE_OP_ERROR(tmppath, "rename"); - g_unlink(tmppath); - g_free(path); - g_free(tmppath); - g_free(bakpath); - return -1; - } - - g_free(path); - g_free(tmppath); - g_free(bakpath); - return 0; -} - -gint prefs_file_close_revert(PrefFile *pfile) -{ - gchar *tmppath; - - g_return_val_if_fail(pfile != NULL, -1); - - tmppath = g_strconcat(pfile->path, ".tmp", NULL); - fclose(pfile->fp); - if (g_unlink(tmppath) < 0) - FILE_OP_ERROR(tmppath, "unlink"); - g_free(tmppath); - g_free(pfile->path); - g_free(pfile); - - return 0; -} - -void prefs_set_default(PrefParam *param) -{ - gint i; - - g_return_if_fail(param != NULL); - - for (i = 0; param[i].name != NULL; i++) { - if (!param[i].data) continue; - - switch (param[i].type) { - case P_STRING: - if (param[i].defval != NULL) { - if (!g_ascii_strncasecmp(param[i].defval, "ENV_", 4)) { - const gchar *envstr; - gchar *tmp = NULL; - - envstr = g_getenv(param[i].defval + 4); - if (envstr) { - tmp = conv_codeset_strdup - (envstr, - conv_get_locale_charset_str(), - CS_UTF_8); - if (!tmp) { - g_warning("failed to convert character set."); - tmp = g_strdup(envstr); - } - } - *((gchar **)param[i].data) = tmp; - } else if (param[i].defval[0] == '~') - *((gchar **)param[i].data) = - g_strconcat(get_home_dir(), - param[i].defval + 1, - NULL); - else if (param[i].defval[0] != '\0') - *((gchar **)param[i].data) = - g_strdup(param[i].defval); - else - *((gchar **)param[i].data) = NULL; - } else - *((gchar **)param[i].data) = NULL; - break; - case P_INT: - if (param[i].defval != NULL) - *((gint *)param[i].data) = - (gint)atoi(param[i].defval); - else - *((gint *)param[i].data) = 0; - break; - case P_BOOL: - if (param[i].defval != NULL) { - if (!g_ascii_strcasecmp(param[i].defval, "TRUE")) - *((gboolean *)param[i].data) = TRUE; - else - *((gboolean *)param[i].data) = - atoi(param[i].defval) ? TRUE : FALSE; - } else - *((gboolean *)param[i].data) = FALSE; - break; - case P_ENUM: - if (param[i].defval != NULL) - *((DummyEnum*)param[i].data) = - (DummyEnum)atoi(param[i].defval); - else - *((DummyEnum *)param[i].data) = 0; - break; - case P_USHORT: - if (param[i].defval != NULL) - *((gushort *)param[i].data) = - (gushort)atoi(param[i].defval); - else - *((gushort *)param[i].data) = 0; - break; - default: - break; - } - } -} - -void prefs_free(PrefParam *param) -{ - gint i; - - g_return_if_fail(param != NULL); - - for (i = 0; param[i].name != NULL; i++) { - if (!param[i].data) continue; - - switch (param[i].type) { - case P_STRING: - g_free(*((gchar **)param[i].data)); - break; - default: - break; - } - } -} diff --git a/src/prefs.h b/src/prefs.h deleted file mode 100644 index f062d1eb..00000000 --- a/src/prefs.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __PREFS_H__ -#define __PREFS_H__ - -#include -#include - -typedef struct _PrefParam PrefParam; -typedef struct _PrefFile PrefFile; - -#define PREFSBUFSIZE 1024 - -#define P_WID(wid) ((gpointer *)(wid)) - -typedef enum -{ - P_STRING, - P_INT, - P_BOOL, - P_ENUM, - P_USHORT, - P_OTHER -} PrefType; - -typedef void (*DataSetFunc) (PrefParam *pparam); -typedef void (*WidgetSetFunc) (PrefParam *pparam); - -struct _PrefParam { - gchar *name; - gchar *defval; - gpointer data; - PrefType type; - gpointer *widget; - DataSetFunc data_set_func; - WidgetSetFunc widget_set_func; -}; - -struct _PrefFile { - FILE *fp; - gchar *path; -}; - -void prefs_read_config (PrefParam *param, - const gchar *label, - const gchar *rcfile, - const gchar *encoding); -void prefs_config_parse_one_line(PrefParam *param, - const gchar *buf); -void prefs_write_config (PrefParam *param, - const gchar *label, - const gchar *rcfile); - -PrefFile *prefs_file_open (const gchar *path); -gint prefs_file_write_param (PrefFile *pfile, - PrefParam *param); -gint prefs_file_close (PrefFile *pfile); -gint prefs_file_close_revert (PrefFile *pfile); - -void prefs_set_default (PrefParam *param); -void prefs_free (PrefParam *param); - -#endif /* __PREFS_H__ */ diff --git a/src/quoted-printable.c b/src/quoted-printable.c deleted file mode 100644 index cea0b704..00000000 --- a/src/quoted-printable.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2003 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include - -static gboolean get_hex_value(guchar *out, gchar c1, gchar c2); -static void get_hex_str(gchar *out, guchar ch); - -#define MAX_LINELEN 76 - -#define IS_LBREAK(p) \ - (*(p) == '\0' || *(p) == '\n' || (*(p) == '\r' && *((p) + 1) == '\n')) - -#define SOFT_LBREAK_IF_REQUIRED(n) \ - if (len + (n) > MAX_LINELEN || \ - (len + (n) == MAX_LINELEN && (!IS_LBREAK(inp + 1)))) { \ - *outp++ = '='; \ - *outp++ = '\n'; \ - len = 0; \ - } - -void qp_encode_line(gchar *out, const guchar *in) -{ - const guchar *inp = in; - gchar *outp = out; - guchar ch; - gint len = 0; - - while (*inp != '\0') { - ch = *inp; - - if (IS_LBREAK(inp)) { - *outp++ = '\n'; - len = 0; - if (*inp == '\r') - inp++; - inp++; - } else if (ch == '\t' || ch == ' ') { - if (IS_LBREAK(inp + 1)) { - SOFT_LBREAK_IF_REQUIRED(3); - *outp++ = '='; - get_hex_str(outp, ch); - outp += 2; - len += 3; - inp++; - } else { - SOFT_LBREAK_IF_REQUIRED(1); - *outp++ = *inp++; - len++; - } - } else if ((ch >= 33 && ch <= 60) || (ch >= 62 && ch <= 126)) { - SOFT_LBREAK_IF_REQUIRED(1); - *outp++ = *inp++; - len++; - } else { - SOFT_LBREAK_IF_REQUIRED(3); - *outp++ = '='; - get_hex_str(outp, ch); - outp += 2; - len += 3; - inp++; - } - } - - if (len > 0) - *outp++ = '\n'; - - *outp = '\0'; -} - -gint qp_decode_line(gchar *str) -{ - gchar *inp = str, *outp = str; - - while (*inp != '\0') { - if (*inp == '=') { - if (inp[1] && inp[2] && - get_hex_value((guchar *)outp, inp[1], inp[2]) - == TRUE) { - inp += 3; - } else if (inp[1] == '\0' || g_ascii_isspace(inp[1])) { - /* soft line break */ - break; - } else { - /* broken QP string */ - *outp = *inp++; - } - } else { - *outp = *inp++; - } - outp++; - } - - *outp = '\0'; - - return outp - str; -} - -gint qp_decode_q_encoding(guchar *out, const gchar *in, gint inlen) -{ - const gchar *inp = in; - guchar *outp = out; - - if (inlen < 0) - inlen = G_MAXINT; - - while (inp - in < inlen && *inp != '\0') { - if (*inp == '=' && inp + 3 - in <= inlen) { - if (get_hex_value(outp, inp[1], inp[2]) == TRUE) { - inp += 3; - } else { - *outp = *inp++; - } - } else if (*inp == '_') { - *outp = ' '; - inp++; - } else { - *outp = *inp++; - } - outp++; - } - - *outp = '\0'; - - return outp - out; -} - -gint qp_get_q_encoding_len(const guchar *str) -{ - const guchar *inp = str; - gint len = 0; - - while (*inp != '\0') { - if (*inp == 0x20) - len++; - else if (*inp == '=' || *inp == '?' || *inp == '_' || - *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) - len += 3; - else - len++; - - inp++; - } - - return len; -} - -void qp_q_encode(gchar *out, const guchar *in) -{ - const guchar *inp = in; - gchar *outp = out; - - while (*inp != '\0') { - if (*inp == 0x20) - *outp++ = '_'; - else if (*inp == '=' || *inp == '?' || *inp == '_' || - *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) { - *outp++ = '='; - get_hex_str(outp, *inp); - outp += 2; - } else - *outp++ = *inp; - - inp++; - } - - *outp = '\0'; -} - -#define HEX_TO_INT(val, hex) \ -{ \ - gchar c = hex; \ - \ - if ('0' <= c && c <= '9') { \ - val = c - '0'; \ - } else if ('a' <= c && c <= 'f') { \ - val = c - 'a' + 10; \ - } else if ('A' <= c && c <= 'F') { \ - val = c - 'A' + 10; \ - } else { \ - val = -1; \ - } \ -} - -static gboolean get_hex_value(guchar *out, gchar c1, gchar c2) -{ - gint hi, lo; - - HEX_TO_INT(hi, c1); - HEX_TO_INT(lo, c2); - - if (hi == -1 || lo == -1) - return FALSE; - - *out = (hi << 4) + lo; - return TRUE; -} - -#define INT_TO_HEX(hex, val) \ -{ \ - if ((val) < 10) \ - hex = '0' + (val); \ - else \ - hex = 'A' + (val) - 10; \ -} - -static void get_hex_str(gchar *out, guchar ch) -{ - gchar hex; - - INT_TO_HEX(hex, ch >> 4); - *out++ = hex; - INT_TO_HEX(hex, ch & 0x0f); - *out++ = hex; -} diff --git a/src/quoted-printable.h b/src/quoted-printable.h deleted file mode 100644 index e5abf4f7..00000000 --- a/src/quoted-printable.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2003 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __QUOTED_PRINTABLE_H__ -#define __QUOTED_PRINTABLE_H__ - -#include - -void qp_encode_line (gchar *out, - const guchar *in); -gint qp_decode_line (gchar *str); - -gint qp_decode_q_encoding (guchar *out, - const gchar *in, - gint inlen); -gint qp_get_q_encoding_len (const guchar *str); -void qp_q_encode (gchar *out, - const guchar *in); - -#endif /* __QUOTED_PRINTABLE_H__ */ diff --git a/src/session.c b/src/session.c deleted file mode 100644 index 6e7fa4e9..00000000 --- a/src/session.c +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "defs.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "session.h" -#include "utils.h" - -static gint session_connect_cb (SockInfo *sock, - gpointer data); -static gint session_close (Session *session); - -static gboolean session_timeout_cb (gpointer data); - -static gboolean session_recv_msg_idle_cb (gpointer data); -static gboolean session_recv_data_idle_cb (gpointer data); - -static gboolean session_read_msg_cb (SockInfo *source, - GIOCondition condition, - gpointer data); -static gboolean session_read_data_cb (SockInfo *source, - GIOCondition condition, - gpointer data); -static gboolean session_write_msg_cb (SockInfo *source, - GIOCondition condition, - gpointer data); -static gboolean session_write_data_cb (SockInfo *source, - GIOCondition condition, - gpointer data); - - -void session_init(Session *session) -{ - session->type = SESSION_UNKNOWN; - session->sock = NULL; - session->server = NULL; - session->port = 0; -#if USE_SSL - session->ssl_type = SSL_NONE; -#endif - session->nonblocking = TRUE; - session->state = SESSION_READY; - session->last_access_time = time(NULL); - - g_get_current_time(&session->tv_prev); - - session->conn_id = 0; - - session->io_tag = 0; - - session->read_buf_p = session->read_buf; - session->read_buf_len = 0; - - session->read_msg_buf = g_string_sized_new(1024); - session->read_data_buf = g_byte_array_new(); - - session->write_buf = NULL; - session->write_buf_p = NULL; - session->write_buf_len = 0; - - session->write_data = NULL; - session->write_data_p = NULL; - session->write_data_len = 0; - - session->timeout_tag = 0; - session->timeout_interval = 0; - - session->data = NULL; -} - -gint session_connect(Session *session, const gchar *server, gushort port) -{ -#ifdef G_OS_UNIX - session->server = g_strdup(server); - session->port = port; - - session->conn_id = sock_connect_async(server, port, session_connect_cb, - session); - if (session->conn_id < 0) { - g_warning("can't connect to server."); - session_close(session); - return -1; - } - - return 0; -#else - SockInfo *sock; - - session->server = g_strdup(server); - session->port = port; - - sock = sock_connect(server, port); - if (sock == NULL) { - g_warning("can't connect to server."); - session_close(session); - return -1; - } - - return session_connect_cb(sock, session); -#endif -} - -static gint session_connect_cb(SockInfo *sock, gpointer data) -{ - Session *session = SESSION(data); - - session->conn_id = 0; - - if (!sock) { - g_warning("can't connect to server."); - session->state = SESSION_ERROR; - return -1; - } - - session->sock = sock; - -#if USE_SSL - if (session->ssl_type == SSL_TUNNEL) { - sock_set_nonblocking_mode(sock, FALSE); - if (!ssl_init_socket(sock)) { - g_warning("can't initialize SSL."); - session->state = SESSION_ERROR; - return -1; - } - } -#endif - - sock_set_nonblocking_mode(sock, session->nonblocking); - - debug_print("session (%p): connected\n", session); - - session->state = SESSION_RECV; - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_msg_cb, - session); - - return 0; -} - -gint session_disconnect(Session *session) -{ - session_close(session); - return 0; -} - -void session_destroy(Session *session) -{ - g_return_if_fail(session != NULL); - g_return_if_fail(session->destroy != NULL); - - session_close(session); - session->destroy(session); - g_free(session->server); - g_string_free(session->read_msg_buf, TRUE); - g_byte_array_free(session->read_data_buf, TRUE); - g_free(session->read_data_terminator); - g_free(session->write_buf); - - debug_print("session (%p): destroyed\n", session); - - g_free(session); -} - -gboolean session_is_connected(Session *session) -{ - return (session->state == SESSION_READY || - session->state == SESSION_SEND || - session->state == SESSION_RECV); -} - -void session_set_access_time(Session *session) -{ - session->last_access_time = time(NULL); -} - -void session_set_timeout(Session *session, guint interval) -{ - if (session->timeout_tag > 0) - g_source_remove(session->timeout_tag); - - session->timeout_interval = interval; - if (interval > 0) - session->timeout_tag = - g_timeout_add(interval, session_timeout_cb, session); - else - session->timeout_tag = 0; -} - -static gboolean session_timeout_cb(gpointer data) -{ - Session *session = SESSION(data); - - g_warning("session timeout.\n"); - - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - session->timeout_tag = 0; - session->state = SESSION_TIMEOUT; - - return FALSE; -} - -void session_set_recv_message_notify(Session *session, - RecvMsgNotify notify_func, gpointer data) -{ - session->recv_msg_notify = notify_func; - session->recv_msg_notify_data = data; -} - -void session_set_recv_data_progressive_notify - (Session *session, - RecvDataProgressiveNotify notify_func, - gpointer data) -{ - session->recv_data_progressive_notify = notify_func, - session->recv_data_progressive_notify_data = data; -} - -void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func, - gpointer data) -{ - session->recv_data_notify = notify_func; - session->recv_data_notify_data = data; -} - -void session_set_send_data_progressive_notify - (Session *session, - SendDataProgressiveNotify notify_func, - gpointer data) -{ - session->send_data_progressive_notify = notify_func; - session->send_data_progressive_notify_data = data; -} - -void session_set_send_data_notify(Session *session, SendDataNotify notify_func, - gpointer data) -{ - session->send_data_notify = notify_func; - session->send_data_notify_data = data; -} - -static gint session_close(Session *session) -{ - g_return_val_if_fail(session != NULL, -1); - -#ifdef G_OS_UNIX - if (session->conn_id > 0) { - sock_connect_async_cancel(session->conn_id); - session->conn_id = 0; - debug_print("session (%p): connection cancelled\n", session); - } -#endif - - session_set_timeout(session, 0); - - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - if (session->sock) { - sock_close(session->sock); - session->sock = NULL; - session->state = SESSION_DISCONNECTED; - debug_print("session (%p): closed\n", session); - } - - return 0; -} - -#if USE_SSL -gint session_start_tls(Session *session) -{ - gboolean nb_mode; - - nb_mode = sock_is_nonblocking_mode(session->sock); - - if (nb_mode) - sock_set_nonblocking_mode(session->sock, FALSE); - - if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) { - g_warning("can't start TLS session.\n"); - if (nb_mode) - sock_set_nonblocking_mode(session->sock, TRUE); - return -1; - } - - if (nb_mode) - sock_set_nonblocking_mode(session->sock, session->nonblocking); - - return 0; -} -#endif - -gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg) -{ - gboolean ret; - - g_return_val_if_fail(session->write_buf == NULL, -1); - g_return_val_if_fail(msg != NULL, -1); - g_return_val_if_fail(msg[0] != '\0', -1); - - session->state = SESSION_SEND; - session->write_buf = g_strconcat(msg, "\r\n", NULL); - session->write_buf_p = session->write_buf; - session->write_buf_len = strlen(msg) + 2; - - ret = session_write_msg_cb(session->sock, G_IO_OUT, session); - - if (ret == TRUE) - session->io_tag = sock_add_watch(session->sock, G_IO_OUT, - session_write_msg_cb, session); - else if (session->state == SESSION_ERROR) - return -1; - - return 0; -} - -gint session_recv_msg(Session *session) -{ - g_return_val_if_fail(session->read_msg_buf->len == 0, -1); - - session->state = SESSION_RECV; - - if (session->read_buf_len > 0) - g_idle_add(session_recv_msg_idle_cb, session); - else - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_msg_cb, session); - - return 0; -} - -static gboolean session_recv_msg_idle_cb(gpointer data) -{ - Session *session = SESSION(data); - gboolean ret; - - ret = session_read_msg_cb(session->sock, G_IO_IN, session); - - if (ret == TRUE) - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_msg_cb, session); - - return FALSE; -} - -gint session_send_data(Session *session, const guchar *data, guint size) -{ - gboolean ret; - - g_return_val_if_fail(session->write_data == NULL, -1); - g_return_val_if_fail(data != NULL, -1); - g_return_val_if_fail(size != 0, -1); - - session->state = SESSION_SEND; - - session->write_data = data; - session->write_data_p = session->write_data; - session->write_data_len = size; - g_get_current_time(&session->tv_prev); - - ret = session_write_data_cb(session->sock, G_IO_OUT, session); - - if (ret == TRUE) - session->io_tag = sock_add_watch(session->sock, G_IO_OUT, - session_write_data_cb, - session); - else if (session->state == SESSION_ERROR) - return -1; - - return 0; -} - -gint session_recv_data(Session *session, guint size, const gchar *terminator) -{ - g_return_val_if_fail(session->read_data_buf->len == 0, -1); - - session->state = SESSION_RECV; - - g_free(session->read_data_terminator); - session->read_data_terminator = g_strdup(terminator); - g_get_current_time(&session->tv_prev); - - if (session->read_buf_len > 0) - g_idle_add(session_recv_data_idle_cb, session); - else - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_data_cb, session); - - return 0; -} - -static gboolean session_recv_data_idle_cb(gpointer data) -{ - Session *session = SESSION(data); - gboolean ret; - - ret = session_read_data_cb(session->sock, G_IO_IN, session); - - if (ret == TRUE) - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_data_cb, session); - - return FALSE; -} - -static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, - gpointer data) -{ - Session *session = SESSION(data); - gchar buf[SESSION_BUFFSIZE]; - gint line_len; - gchar *newline; - gchar *msg; - gint ret; - - g_return_val_if_fail(condition == G_IO_IN, FALSE); - - session_set_timeout(session, session->timeout_interval); - - if (session->read_buf_len == 0) { - gint read_len; - - read_len = sock_read(session->sock, session->read_buf, - SESSION_BUFFSIZE - 1); - - if (read_len == 0) { - g_warning("sock_read: received EOF\n"); - session->state = SESSION_EOF; - return FALSE; - } - - if (read_len < 0) { - switch (errno) { - case EAGAIN: - return TRUE; - default: - g_warning("sock_read: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; - return FALSE; - } - } - - session->read_buf_len = read_len; - } - - if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len)) - != NULL) - line_len = newline - session->read_buf_p + 1; - else - line_len = session->read_buf_len; - - if (line_len == 0) - return TRUE; - - memcpy(buf, session->read_buf_p, line_len); - buf[line_len] = '\0'; - - g_string_append(session->read_msg_buf, buf); - - session->read_buf_len -= line_len; - if (session->read_buf_len == 0) - session->read_buf_p = session->read_buf; - else - session->read_buf_p += line_len; - - /* incomplete read */ - if (buf[line_len - 1] != '\n') - return TRUE; - - /* complete */ - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - /* callback */ - msg = g_strdup(session->read_msg_buf->str); - strretchomp(msg); - g_string_truncate(session->read_msg_buf, 0); - - ret = session->recv_msg(session, msg); - session->recv_msg_notify(session, msg, session->recv_msg_notify_data); - - g_free(msg); - - if (ret < 0) - session->state = SESSION_ERROR; - - return FALSE; -} - -static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, - gpointer data) -{ - Session *session = SESSION(data); - GByteArray *data_buf; - gint terminator_len; - gboolean complete = FALSE; - guint data_len; - gint ret; - - g_return_val_if_fail(condition == G_IO_IN, FALSE); - - session_set_timeout(session, session->timeout_interval); - - if (session->read_buf_len == 0) { - gint read_len; - - read_len = sock_read(session->sock, session->read_buf, - SESSION_BUFFSIZE); - - if (read_len == 0) { - g_warning("sock_read: received EOF\n"); - session->state = SESSION_EOF; - return FALSE; - } - - if (read_len < 0) { - switch (errno) { - case EAGAIN: - return TRUE; - default: - g_warning("sock_read: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; - return FALSE; - } - } - - session->read_buf_len = read_len; - } - - data_buf = session->read_data_buf; - terminator_len = strlen(session->read_data_terminator); - - if (session->read_buf_len == 0) - return TRUE; - - g_byte_array_append(data_buf, session->read_buf_p, - session->read_buf_len); - - session->read_buf_len = 0; - session->read_buf_p = session->read_buf; - - /* check if data is terminated */ - if (data_buf->len >= terminator_len) { - if (memcmp(data_buf->data, session->read_data_terminator, - terminator_len) == 0) - complete = TRUE; - else if (data_buf->len >= terminator_len + 2 && - memcmp(data_buf->data + data_buf->len - - (terminator_len + 2), "\r\n", 2) == 0 && - memcmp(data_buf->data + data_buf->len - - terminator_len, session->read_data_terminator, - terminator_len) == 0) - complete = TRUE; - } - - /* incomplete read */ - if (!complete) { - GTimeVal tv_cur; - - g_get_current_time(&tv_cur); - if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || - tv_cur.tv_usec - session->tv_prev.tv_usec > - UI_REFRESH_INTERVAL) { - session->recv_data_progressive_notify - (session, data_buf->len, 0, - session->recv_data_progressive_notify_data); - g_get_current_time(&session->tv_prev); - } - return TRUE; - } - - /* complete */ - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - data_len = data_buf->len - terminator_len; - - /* callback */ - ret = session->recv_data_finished(session, (gchar *)data_buf->data, - data_len); - - g_byte_array_set_size(data_buf, 0); - - session->recv_data_notify(session, data_len, - session->recv_data_notify_data); - - if (ret < 0) - session->state = SESSION_ERROR; - - return FALSE; -} - -static gint session_write_buf(Session *session) -{ - gint write_len; - gint to_write_len; - - g_return_val_if_fail(session->write_buf != NULL, -1); - g_return_val_if_fail(session->write_buf_p != NULL, -1); - g_return_val_if_fail(session->write_buf_len > 0, -1); - - to_write_len = session->write_buf_len - - (session->write_buf_p - session->write_buf); - to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); - - write_len = sock_write(session->sock, session->write_buf_p, - to_write_len); - - if (write_len < 0) { - switch (errno) { - case EAGAIN: - write_len = 0; - break; - default: - g_warning("sock_write: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; - return -1; - } - } - - /* incomplete write */ - if (session->write_buf_p - session->write_buf + write_len < - session->write_buf_len) { - session->write_buf_p += write_len; - return 1; - } - - g_free(session->write_buf); - session->write_buf = NULL; - session->write_buf_p = NULL; - session->write_buf_len = 0; - - return 0; -} - -static gint session_write_data(Session *session) -{ - gint write_len; - gint to_write_len; - - g_return_val_if_fail(session->write_data != NULL, -1); - g_return_val_if_fail(session->write_data_p != NULL, -1); - g_return_val_if_fail(session->write_data_len > 0, -1); - - to_write_len = session->write_data_len - - (session->write_data_p - session->write_data); - to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); - - write_len = sock_write(session->sock, session->write_data_p, - to_write_len); - - if (write_len < 0) { - switch (errno) { - case EAGAIN: - write_len = 0; - break; - default: - g_warning("sock_write: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; - return -1; - } - } - - /* incomplete write */ - if (session->write_data_p - session->write_data + write_len < - session->write_data_len) { - session->write_data_p += write_len; - return 1; - } - - session->write_data = NULL; - session->write_data_p = NULL; - session->write_data_len = 0; - - return 0; -} - -static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, - gpointer data) -{ - Session *session = SESSION(data); - gint ret; - - g_return_val_if_fail(condition == G_IO_OUT, FALSE); - g_return_val_if_fail(session->write_buf != NULL, FALSE); - g_return_val_if_fail(session->write_buf_p != NULL, FALSE); - g_return_val_if_fail(session->write_buf_len > 0, FALSE); - - ret = session_write_buf(session); - - if (ret < 0) { - session->state = SESSION_ERROR; - return FALSE; - } else if (ret > 0) - return TRUE; - - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - session_recv_msg(session); - - return FALSE; -} - -static gboolean session_write_data_cb(SockInfo *source, - GIOCondition condition, gpointer data) -{ - Session *session = SESSION(data); - guint write_data_len; - gint ret; - - g_return_val_if_fail(condition == G_IO_OUT, FALSE); - g_return_val_if_fail(session->write_data != NULL, FALSE); - g_return_val_if_fail(session->write_data_p != NULL, FALSE); - g_return_val_if_fail(session->write_data_len > 0, FALSE); - - write_data_len = session->write_data_len; - - ret = session_write_data(session); - - if (ret < 0) { - session->state = SESSION_ERROR; - return FALSE; - } else if (ret > 0) { - GTimeVal tv_cur; - - g_get_current_time(&tv_cur); - if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || - tv_cur.tv_usec - session->tv_prev.tv_usec > - UI_REFRESH_INTERVAL) { - session_set_timeout(session, session->timeout_interval); - session->send_data_progressive_notify - (session, - session->write_data_p - session->write_data, - write_data_len, - session->send_data_progressive_notify_data); - g_get_current_time(&session->tv_prev); - } - return TRUE; - } - - if (session->io_tag > 0) { - g_source_remove(session->io_tag); - session->io_tag = 0; - } - - /* callback */ - ret = session->send_data_finished(session, write_data_len); - session->send_data_notify(session, write_data_len, - session->send_data_notify_data); - - return FALSE; -} diff --git a/src/session.h b/src/session.h deleted file mode 100644 index 9ed0eaf4..00000000 --- a/src/session.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __SESSION_H__ -#define __SESSION_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include -#include - -#include "socket.h" - -#define SESSION_BUFFSIZE 4096 - -typedef struct _Session Session; - -#define SESSION(obj) ((Session *)obj) - -typedef enum { - SESSION_UNKNOWN, - SESSION_IMAP, - SESSION_NEWS, - SESSION_SMTP, - SESSION_POP3 -} SessionType; - -typedef enum { - SESSION_READY, - SESSION_SEND, - SESSION_RECV, - SESSION_EOF, - SESSION_TIMEOUT, - SESSION_ERROR, - SESSION_DISCONNECTED -} SessionState; - -typedef enum -{ - SESSION_MSG_NORMAL, - SESSION_MSG_SEND_DATA, - SESSION_MSG_RECV_DATA, - SESSION_MSG_CONTROL, - SESSION_MSG_ERROR, - SESSION_MSG_UNKNOWN -} SessionMsgType; - -typedef gint (*RecvMsgNotify) (Session *session, - const gchar *msg, - gpointer user_data); -typedef gint (*RecvDataProgressiveNotify) (Session *session, - guint cur_len, - guint total_len, - gpointer user_data); -typedef gint (*RecvDataNotify) (Session *session, - guint len, - gpointer user_data); -typedef gint (*SendDataProgressiveNotify) (Session *session, - guint cur_len, - guint total_len, - gpointer user_data); -typedef gint (*SendDataNotify) (Session *session, - guint len, - gpointer user_data); - -struct _Session -{ - SessionType type; - - SockInfo *sock; - - gchar *server; - gushort port; - -#if USE_SSL - SSLType ssl_type; -#endif - - gboolean nonblocking; - - SessionState state; - - time_t last_access_time; - GTimeVal tv_prev; - - gint conn_id; - - gint io_tag; - - gchar read_buf[SESSION_BUFFSIZE]; - gchar *read_buf_p; - gint read_buf_len; - - GString *read_msg_buf; - GByteArray *read_data_buf; - gchar *read_data_terminator; - - /* buffer for short messages */ - gchar *write_buf; - gchar *write_buf_p; - gint write_buf_len; - - /* buffer for large data */ - const guchar *write_data; - const guchar *write_data_p; - gint write_data_len; - - guint timeout_tag; - guint timeout_interval; - - gpointer data; - - /* virtual methods to parse server responses */ - gint (*recv_msg) (Session *session, - const gchar *msg); - - gint (*send_data_finished) (Session *session, - guint len); - gint (*recv_data_finished) (Session *session, - guchar *data, - guint len); - - void (*destroy) (Session *session); - - /* notification functions */ - RecvMsgNotify recv_msg_notify; - RecvDataProgressiveNotify recv_data_progressive_notify; - RecvDataNotify recv_data_notify; - SendDataProgressiveNotify send_data_progressive_notify; - SendDataNotify send_data_notify; - - gpointer recv_msg_notify_data; - gpointer recv_data_progressive_notify_data; - gpointer recv_data_notify_data; - gpointer send_data_progressive_notify_data; - gpointer send_data_notify_data; -}; - -void session_init (Session *session); -gint session_connect (Session *session, - const gchar *server, - gushort port); -gint session_disconnect (Session *session); -void session_destroy (Session *session); -gboolean session_is_connected (Session *session); - -void session_set_access_time (Session *session); - -void session_set_timeout (Session *session, - guint interval); - -void session_set_recv_message_notify (Session *session, - RecvMsgNotify notify_func, - gpointer data); -void session_set_recv_data_progressive_notify - (Session *session, - RecvDataProgressiveNotify notify_func, - gpointer data); -void session_set_recv_data_notify (Session *session, - RecvDataNotify notify_func, - gpointer data); -void session_set_send_data_progressive_notify - (Session *session, - SendDataProgressiveNotify notify_func, - gpointer data); -void session_set_send_data_notify (Session *session, - SendDataNotify notify_func, - gpointer data); - -#if USE_SSL -gint session_start_tls (Session *session); -#endif - -gint session_send_msg (Session *session, - SessionMsgType type, - const gchar *msg); -gint session_recv_msg (Session *session); -gint session_send_data (Session *session, - const guchar *data, - guint size); -gint session_recv_data (Session *session, - guint size, - const gchar *terminator); - -#endif /* __SESSION_H__ */ diff --git a/src/socket.c b/src/socket.c deleted file mode 100644 index 17063ab1..00000000 --- a/src/socket.c +++ /dev/null @@ -1,1397 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#ifdef G_OS_WIN32 -# include -#else -# if HAVE_SYS_WAIT_H -# include -# endif -# include -# include -# include -# include -# include -#endif /* G_OS_WIN32 */ -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SYS_SELECT_H -# include -#endif - -#include "socket.h" -#if USE_SSL -# include "ssl.h" -#endif - -#define BUFFSIZE 8192 - -typedef gint (*SockAddrFunc) (GList *addr_list, - gpointer data); - -typedef struct _SockConnectData SockConnectData; -typedef struct _SockLookupData SockLookupData; -typedef struct _SockAddrData SockAddrData; -typedef struct _SockSource SockSource; - -struct _SockConnectData { - gint id; - gchar *hostname; - gushort port; - GList *addr_list; - GList *cur_addr; - SockLookupData *lookup_data; - GIOChannel *channel; - guint io_tag; - SockConnectFunc func; - gpointer data; -}; - -struct _SockLookupData { - gchar *hostname; - pid_t child_pid; - GIOChannel *channel; - guint io_tag; - SockAddrFunc func; - gpointer data; -}; - -struct _SockAddrData { - gint family; - gint socktype; - gint protocol; - gint addr_len; - struct sockaddr *addr; -}; - -struct _SockSource { - GSource parent; - SockInfo *sock; -}; - -static guint io_timeout = 60; - -static GList *sock_connect_data_list = NULL; - -static gboolean sock_prepare (GSource *source, - gint *timeout); -static gboolean sock_check (GSource *source); -static gboolean sock_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data); - -GSourceFuncs sock_watch_funcs = { - sock_prepare, - sock_check, - sock_dispatch, - NULL -}; - -static gint sock_connect_with_timeout (gint sock, - const struct sockaddr *serv_addr, - gint addrlen, - guint timeout_secs); - -#ifndef INET6 -static gint sock_connect_by_hostname (gint sock, - const gchar *hostname, - gushort port); -#else -static gint sock_connect_by_getaddrinfo (const gchar *hostname, - gushort port); -#endif - -#ifdef G_OS_UNIX -static void sock_address_list_free (GList *addr_list); - -static gboolean sock_connect_async_cb (GIOChannel *source, - GIOCondition condition, - gpointer data); -static gint sock_connect_async_get_address_info_cb - (GList *addr_list, - gpointer data); - -static gint sock_connect_address_list_async (SockConnectData *conn_data); - -static gboolean sock_get_address_info_async_cb (GIOChannel *source, - GIOCondition condition, - gpointer data); -static SockLookupData *sock_get_address_info_async - (const gchar *hostname, - gushort port, - SockAddrFunc func, - gpointer data); -static gint sock_get_address_info_async_cancel (SockLookupData *lookup_data); -#endif /* G_OS_UNIX */ - - -gint sock_init(void) -{ -#ifdef G_OS_WIN32 - WSADATA wsadata; - gint result; - - result = WSAStartup(MAKEWORD(2, 2), &wsadata); - if (result != NO_ERROR) { - g_warning("WSAStartup() failed\n"); - return -1; - } -#endif - return 0; -} - -gint sock_cleanup(void) -{ -#ifdef G_OS_WIN32 - WSACleanup(); -#endif - return 0; -} - -gint sock_set_io_timeout(guint sec) -{ - io_timeout = sec; - return 0; -} - -gint fd_connect_unix(const gchar *path) -{ -#ifdef G_OS_UNIX - gint sock; - struct sockaddr_un addr; - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - perror("sock_connect_unix(): socket"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fd_close(sock); - return -1; - } - - return sock; -#else - return -1; -#endif -} - -gint fd_open_unix(const gchar *path) -{ -#ifdef G_OS_UNIX - gint sock; - struct sockaddr_un addr; - - sock = socket(PF_UNIX, SOCK_STREAM, 0); - - if (sock < 0) { - perror("sock_open_unix(): socket"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); - - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("bind"); - fd_close(sock); - return -1; - } - - if (listen(sock, 1) < 0) { - perror("listen"); - fd_close(sock); - return -1; - } - - return sock; -#else - return -1; -#endif -} - -gint fd_accept(gint sock) -{ -#ifdef G_OS_UNIX - struct sockaddr_in caddr; - guint caddr_len; - - caddr_len = sizeof(caddr); - return accept(sock, (struct sockaddr *)&caddr, &caddr_len); -#else - return -1; -#endif -} - - -static gint set_nonblocking_mode(gint fd, gboolean nonblock) -{ -#ifdef G_OS_UNIX - gint flags; - - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) { - perror("fcntl"); - return -1; - } - - if (nonblock) - flags |= O_NONBLOCK; - else - flags &= ~O_NONBLOCK; - - return fcntl(fd, F_SETFL, flags); -#else - return -1; -#endif -} - -gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock) -{ - g_return_val_if_fail(sock != NULL, -1); - - return set_nonblocking_mode(sock->sock, nonblock); -} - -static gboolean is_nonblocking_mode(gint fd) -{ -#ifdef G_OS_UNIX - gint flags; - - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) { - perror("fcntl"); - return FALSE; - } - - return ((flags & O_NONBLOCK) != 0); -#else - return FALSE; -#endif -} - -gboolean sock_is_nonblocking_mode(SockInfo *sock) -{ - g_return_val_if_fail(sock != NULL, FALSE); - - return is_nonblocking_mode(sock->sock); -} - - -static gboolean sock_prepare(GSource *source, gint *timeout) -{ - *timeout = 1; - return FALSE; -} - -static gboolean sock_check(GSource *source) -{ - SockInfo *sock = ((SockSource *)source)->sock; - struct timeval timeout = {0, 0}; - fd_set fds; - GIOCondition condition = sock->condition; - -#if USE_SSL - if (sock->ssl) { - if (condition & G_IO_IN) { - if (SSL_pending(sock->ssl) > 0) - return TRUE; - if (SSL_want_write(sock->ssl)) - condition |= G_IO_OUT; - } - - if (condition & G_IO_OUT) { - if (SSL_want_read(sock->ssl)) - condition |= G_IO_IN; - } - } -#endif - - FD_ZERO(&fds); - FD_SET(sock->sock, &fds); - - select(sock->sock + 1, - (condition & G_IO_IN) ? &fds : NULL, - (condition & G_IO_OUT) ? &fds : NULL, - NULL, &timeout); - - return FD_ISSET(sock->sock, &fds) != 0; -} - -static gboolean sock_dispatch(GSource *source, GSourceFunc callback, - gpointer user_data) -{ - SockInfo *sock = ((SockSource *)source)->sock; - - return sock->callback(sock, sock->condition, sock->data); -} - -static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition, - gpointer data) -{ - SockInfo *sock = (SockInfo *)data; - - if ((condition & sock->condition) == 0) - return TRUE; - - return sock->callback(sock, sock->condition, sock->data); -} - -guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func, - gpointer data) -{ - sock->callback = func; - sock->condition = condition; - sock->data = data; - -#if USE_SSL - if (sock->ssl) { - GSource *source; - - source = g_source_new(&sock_watch_funcs, sizeof(SockSource)); - ((SockSource *)source)->sock = sock; - g_source_set_priority(source, G_PRIORITY_DEFAULT); - g_source_set_can_recurse(source, FALSE); - return g_source_attach(source, NULL); - } -#endif - - return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock); -} - -static gint fd_check_io(gint fd, GIOCondition cond) -{ - struct timeval timeout; - fd_set fds; - - if (is_nonblocking_mode(fd)) - return 0; - - timeout.tv_sec = io_timeout; - timeout.tv_usec = 0; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - if (cond == G_IO_IN) { - select(fd + 1, &fds, NULL, NULL, - io_timeout > 0 ? &timeout : NULL); - } else { - select(fd + 1, NULL, &fds, NULL, - io_timeout > 0 ? &timeout : NULL); - } - - if (FD_ISSET(fd, &fds)) { - return 0; - } else { - g_warning("Socket IO timeout\n"); - return -1; - } -} - -#ifdef G_OS_UNIX -static sigjmp_buf jmpenv; - -static void timeout_handler(gint sig) -{ - siglongjmp(jmpenv, 1); -} -#endif - -static gint sock_connect_with_timeout(gint sock, - const struct sockaddr *serv_addr, - gint addrlen, - guint timeout_secs) -{ - gint ret; -#ifdef G_OS_UNIX - void (*prev_handler)(gint); - - alarm(0); - prev_handler = signal(SIGALRM, timeout_handler); - if (sigsetjmp(jmpenv, 1)) { - alarm(0); - signal(SIGALRM, prev_handler); - errno = ETIMEDOUT; - return -1; - } - alarm(timeout_secs); -#endif - - ret = connect(sock, serv_addr, addrlen); - -#ifdef G_OS_UNIX - alarm(0); - signal(SIGALRM, prev_handler); -#endif - - return ret; -} - -struct hostent *my_gethostbyname(const gchar *hostname) -{ - struct hostent *hp; -#ifdef G_OS_UNIX - void (*prev_handler)(gint); - - alarm(0); - prev_handler = signal(SIGALRM, timeout_handler); - if (sigsetjmp(jmpenv, 1)) { - alarm(0); - signal(SIGALRM, prev_handler); - fprintf(stderr, "%s: host lookup timed out.\n", hostname); - errno = 0; - return NULL; - } - alarm(io_timeout); -#endif - - if ((hp = gethostbyname(hostname)) == NULL) { -#ifdef G_OS_UNIX - alarm(0); - signal(SIGALRM, prev_handler); -#endif - fprintf(stderr, "%s: unknown host.\n", hostname); - errno = 0; - return NULL; - } - -#ifdef G_OS_UNIX - alarm(0); - signal(SIGALRM, prev_handler); -#endif - - return hp; -} - -#ifndef INET6 -static gint my_inet_aton(const gchar *hostname, struct in_addr *inp) -{ -#if HAVE_INET_ATON - return inet_aton(hostname, inp); -#else -#if HAVE_INET_ADDR - guint32 inaddr; - - inaddr = inet_addr(hostname); - if (inaddr != -1) { - memcpy(inp, &inaddr, sizeof(inaddr)); - return 1; - } else - return 0; -#else - return 0; -#endif -#endif /* HAVE_INET_ATON */ -} - -static gint sock_connect_by_hostname(gint sock, const gchar *hostname, - gushort port) -{ - struct hostent *hp; - struct sockaddr_in ad; - - memset(&ad, 0, sizeof(ad)); - ad.sin_family = AF_INET; - ad.sin_port = htons(port); - - if (!my_inet_aton(hostname, &ad.sin_addr)) { - if ((hp = my_gethostbyname(hostname)) == NULL) { - fprintf(stderr, "%s: unknown host.\n", hostname); - errno = 0; - return -1; - } - - if (hp->h_length != 4 && hp->h_length != 8) { - fprintf(stderr, "illegal address length received for host %s\n", hostname); - errno = 0; - return -1; - } - - memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); - } - - return sock_connect_with_timeout(sock, (struct sockaddr *)&ad, - sizeof(ad), io_timeout); -} - -#else /* INET6 */ -static gint sock_connect_by_getaddrinfo(const gchar *hostname, gushort port) -{ - gint sock = -1, gai_error; - struct addrinfo hints, *res, *ai; - gchar port_str[6]; - - memset(&hints, 0, sizeof(hints)); - /* hints.ai_flags = AI_CANONNAME; */ - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - /* convert port from integer to string. */ - g_snprintf(port_str, sizeof(port_str), "%d", port); - - if ((gai_error = getaddrinfo(hostname, port_str, &hints, &res)) != 0) { - fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n", - hostname, port_str, gai_strerror(gai_error)); - return -1; - } - - for (ai = res; ai != NULL; ai = ai->ai_next) { - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) - continue; - - if (sock_connect_with_timeout - (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0) - break; - - fd_close(sock); - } - - if (res != NULL) - freeaddrinfo(res); - - if (ai == NULL) - return -1; - - return sock; -} -#endif /* !INET6 */ - -SockInfo *sock_connect(const gchar *hostname, gushort port) -{ -#ifdef G_OS_WIN32 - SOCKET sock; -#else - gint sock; -#endif - SockInfo *sockinfo; - -#ifdef INET6 - if ((sock = sock_connect_by_getaddrinfo(hostname, port)) < 0) - return NULL; -#else -#ifdef G_OS_WIN32 - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { - g_warning("socket() failed: %ld\n", WSAGetLastError()); -#else - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket"); -#endif /* G_OS_WIN32 */ - return NULL; - } - - if (sock_connect_by_hostname(sock, hostname, port) < 0) { - if (errno != 0) perror("connect"); - fd_close(sock); - return NULL; - } -#endif /* INET6 */ - - sockinfo = g_new0(SockInfo, 1); - sockinfo->sock = sock; - sockinfo->sock_ch = g_io_channel_unix_new(sock); - sockinfo->hostname = g_strdup(hostname); - sockinfo->port = port; - sockinfo->state = CONN_ESTABLISHED; - - g_usleep(100000); - - return sockinfo; -} - -#ifdef G_OS_UNIX -static void sock_address_list_free(GList *addr_list) -{ - GList *cur; - - for (cur = addr_list; cur != NULL; cur = cur->next) { - SockAddrData *addr_data = (SockAddrData *)cur->data; - g_free(addr_data->addr); - g_free(addr_data); - } - - g_list_free(addr_list); -} - -/* asynchronous TCP connection */ - -static gboolean sock_connect_async_cb(GIOChannel *source, - GIOCondition condition, gpointer data) -{ - SockConnectData *conn_data = (SockConnectData *)data; - gint fd; - gint val; - guint len; - SockInfo *sockinfo; - - fd = g_io_channel_unix_get_fd(source); - - conn_data->io_tag = 0; - conn_data->channel = NULL; - g_io_channel_unref(source); - - len = sizeof(val); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) { - perror("getsockopt"); - fd_close(fd); - sock_connect_address_list_async(conn_data); - return FALSE; - } - - if (val != 0) { - fd_close(fd); - sock_connect_address_list_async(conn_data); - return FALSE; - } - - sockinfo = g_new0(SockInfo, 1); - sockinfo->sock = fd; - sockinfo->sock_ch = g_io_channel_unix_new(fd); - sockinfo->hostname = g_strdup(conn_data->hostname); - sockinfo->port = conn_data->port; - sockinfo->state = CONN_ESTABLISHED; - - conn_data->func(sockinfo, conn_data->data); - - sock_connect_async_cancel(conn_data->id); - - return FALSE; -} - -static gint sock_connect_async_get_address_info_cb(GList *addr_list, - gpointer data) -{ - SockConnectData *conn_data = (SockConnectData *)data; - - conn_data->addr_list = addr_list; - conn_data->cur_addr = addr_list; - conn_data->lookup_data = NULL; - - return sock_connect_address_list_async(conn_data); -} - -gint sock_connect_async(const gchar *hostname, gushort port, - SockConnectFunc func, gpointer data) -{ - static gint id = 1; - SockConnectData *conn_data; - - conn_data = g_new0(SockConnectData, 1); - conn_data->id = id++; - conn_data->hostname = g_strdup(hostname); - conn_data->port = port; - conn_data->addr_list = NULL; - conn_data->cur_addr = NULL; - conn_data->io_tag = 0; - conn_data->func = func; - conn_data->data = data; - - conn_data->lookup_data = sock_get_address_info_async - (hostname, port, sock_connect_async_get_address_info_cb, - conn_data); - - if (conn_data->lookup_data == NULL) { - g_free(conn_data->hostname); - g_free(conn_data); - return -1; - } - - sock_connect_data_list = g_list_append(sock_connect_data_list, - conn_data); - - return conn_data->id; -} - -gint sock_connect_async_cancel(gint id) -{ - SockConnectData *conn_data = NULL; - GList *cur; - - for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) { - if (((SockConnectData *)cur->data)->id == id) { - conn_data = (SockConnectData *)cur->data; - break; - } - } - - if (conn_data) { - sock_connect_data_list = g_list_remove(sock_connect_data_list, - conn_data); - - if (conn_data->lookup_data) - sock_get_address_info_async_cancel - (conn_data->lookup_data); - - if (conn_data->io_tag > 0) - g_source_remove(conn_data->io_tag); - if (conn_data->channel) { - g_io_channel_shutdown(conn_data->channel, FALSE, NULL); - g_io_channel_unref(conn_data->channel); - } - - sock_address_list_free(conn_data->addr_list); - g_free(conn_data->hostname); - g_free(conn_data); - } else { - g_warning("sock_connect_async_cancel: id %d not found.\n", id); - return -1; - } - - return 0; -} - -static gint sock_connect_address_list_async(SockConnectData *conn_data) -{ - SockAddrData *addr_data; - gint sock = -1; - - for (; conn_data->cur_addr != NULL; - conn_data->cur_addr = conn_data->cur_addr->next) { - addr_data = (SockAddrData *)conn_data->cur_addr->data; - - if ((sock = socket(addr_data->family, addr_data->socktype, - addr_data->protocol)) < 0) { - perror("socket"); - continue; - } - - set_nonblocking_mode(sock, TRUE); - - if (connect(sock, addr_data->addr, addr_data->addr_len) < 0) { - if (EINPROGRESS == errno) { - break; - } else { - perror("connect"); - fd_close(sock); - } - } else - break; - } - - if (conn_data->cur_addr == NULL) { - g_warning("sock_connect_address_list_async: " - "connection to %s:%d failed\n", - conn_data->hostname, conn_data->port); - conn_data->func(NULL, conn_data->data); - sock_connect_async_cancel(conn_data->id); - return -1; - } - - conn_data->cur_addr = conn_data->cur_addr->next; - - conn_data->channel = g_io_channel_unix_new(sock); - conn_data->io_tag = g_io_add_watch(conn_data->channel, G_IO_OUT, - sock_connect_async_cb, conn_data); - - return 0; -} - -/* asynchronous DNS lookup */ - -static gboolean sock_get_address_info_async_cb(GIOChannel *source, - GIOCondition condition, - gpointer data) -{ - SockLookupData *lookup_data = (SockLookupData *)data; - GList *addr_list = NULL; - SockAddrData *addr_data; - gsize bytes_read; - gint ai_member[4]; - struct sockaddr *addr; - - for (;;) { - if (g_io_channel_read(source, (gchar *)ai_member, - sizeof(ai_member), &bytes_read) - != G_IO_ERROR_NONE) { - g_warning("sock_get_address_info_async_cb: " - "address length read error\n"); - break; - } - - if (bytes_read == 0 || bytes_read != sizeof(ai_member)) - break; - - if (ai_member[0] == AF_UNSPEC) { - g_warning("DNS lookup failed\n"); - break; - } - - addr = g_malloc(ai_member[3]); - if (g_io_channel_read(source, (gchar *)addr, ai_member[3], - &bytes_read) - != G_IO_ERROR_NONE) { - g_warning("sock_get_address_info_async_cb: " - "address data read error\n"); - g_free(addr); - break; - } - - if (bytes_read != ai_member[3]) { - g_warning("sock_get_address_info_async_cb: " - "incomplete address data\n"); - g_free(addr); - break; - } - - addr_data = g_new0(SockAddrData, 1); - addr_data->family = ai_member[0]; - addr_data->socktype = ai_member[1]; - addr_data->protocol = ai_member[2]; - addr_data->addr_len = ai_member[3]; - addr_data->addr = addr; - - addr_list = g_list_append(addr_list, addr_data); - } - - g_io_channel_shutdown(source, FALSE, NULL); - g_io_channel_unref(source); - - kill(lookup_data->child_pid, SIGKILL); - waitpid(lookup_data->child_pid, NULL, 0); - - lookup_data->func(addr_list, lookup_data->data); - - g_free(lookup_data->hostname); - g_free(lookup_data); - - return FALSE; -} - -static SockLookupData *sock_get_address_info_async(const gchar *hostname, - gushort port, - SockAddrFunc func, - gpointer data) -{ - SockLookupData *lookup_data = NULL; - gint pipe_fds[2]; - pid_t pid; - - if (pipe(pipe_fds) < 0) { - perror("pipe"); - func(NULL, data); - return NULL; - } - - if ((pid = fork()) < 0) { - perror("fork"); - func(NULL, data); - return NULL; - } - - /* child process */ - if (pid == 0) { -#ifdef INET6 - gint gai_err; - struct addrinfo hints, *res, *ai; - gchar port_str[6]; -#else /* !INET6 */ - struct hostent *hp; - gchar **addr_list_p; - struct sockaddr_in ad; -#endif /* INET6 */ - gint ai_member[4] = {AF_UNSPEC, 0, 0, 0}; - - close(pipe_fds[0]); - -#ifdef INET6 - memset(&hints, 0, sizeof(hints)); - /* hints.ai_flags = AI_CANONNAME; */ - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - g_snprintf(port_str, sizeof(port_str), "%d", port); - - gai_err = getaddrinfo(hostname, port_str, &hints, &res); - if (gai_err != 0) { - g_warning("getaddrinfo for %s:%s failed: %s\n", - hostname, port_str, gai_strerror(gai_err)); - fd_write_all(pipe_fds[1], (gchar *)ai_member, - sizeof(ai_member)); - close(pipe_fds[1]); - _exit(1); - } - - for (ai = res; ai != NULL; ai = ai->ai_next) { - ai_member[0] = ai->ai_family; - ai_member[1] = ai->ai_socktype; - ai_member[2] = ai->ai_protocol; - ai_member[3] = ai->ai_addrlen; - - fd_write_all(pipe_fds[1], (gchar *)ai_member, - sizeof(ai_member)); - fd_write_all(pipe_fds[1], (gchar *)ai->ai_addr, - ai->ai_addrlen); - } - - if (res != NULL) - freeaddrinfo(res); -#else /* !INET6 */ - hp = my_gethostbyname(hostname); - if (hp == NULL || hp->h_addrtype != AF_INET) { - fd_write_all(pipe_fds[1], (gchar *)ai_member, - sizeof(ai_member)); - close(pipe_fds[1]); - _exit(1); - } - - ai_member[0] = AF_INET; - ai_member[1] = SOCK_STREAM; - ai_member[2] = IPPROTO_TCP; - ai_member[3] = sizeof(ad); - - memset(&ad, 0, sizeof(ad)); - ad.sin_family = AF_INET; - ad.sin_port = htons(port); - - for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL; - addr_list_p++) { - memcpy(&ad.sin_addr, *addr_list_p, hp->h_length); - fd_write_all(pipe_fds[1], (gchar *)ai_member, - sizeof(ai_member)); - fd_write_all(pipe_fds[1], (gchar *)&ad, sizeof(ad)); - } -#endif /* INET6 */ - - close(pipe_fds[1]); - - _exit(0); - } else { - close(pipe_fds[1]); - - lookup_data = g_new0(SockLookupData, 1); - lookup_data->hostname = g_strdup(hostname); - lookup_data->child_pid = pid; - lookup_data->func = func; - lookup_data->data = data; - - lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]); - lookup_data->io_tag = g_io_add_watch - (lookup_data->channel, G_IO_IN, - sock_get_address_info_async_cb, lookup_data); - } - - return lookup_data; -} - -static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data) -{ - if (lookup_data->io_tag > 0) - g_source_remove(lookup_data->io_tag); - if (lookup_data->channel) { - g_io_channel_shutdown(lookup_data->channel, FALSE, NULL); - g_io_channel_unref(lookup_data->channel); - } - - if (lookup_data->child_pid > 0) { - kill(lookup_data->child_pid, SIGKILL); - waitpid(lookup_data->child_pid, NULL, 0); - } - - g_free(lookup_data->hostname); - g_free(lookup_data); - - return 0; -} -#endif /* G_OS_UNIX */ - - -gint sock_printf(SockInfo *sock, const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE]; - - va_start(args, format); - g_vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - - return sock_write_all(sock, buf, strlen(buf)); -} - -gint sock_read(SockInfo *sock, gchar *buf, gint len) -{ - g_return_val_if_fail(sock != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_read(sock->ssl, buf, len); -#endif - return fd_read(sock->sock, buf, len); -} - -gint fd_read(gint fd, gchar *buf, gint len) -{ - if (fd_check_io(fd, G_IO_IN) < 0) - return -1; - -#ifdef G_OS_WIN32 - return recv(fd, buf, len, 0); -#else - return read(fd, buf, len); -#endif -} - -#if USE_SSL -gint ssl_read(SSL *ssl, gchar *buf, gint len) -{ - gint err, ret; - - if (SSL_pending(ssl) == 0) { - if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) - return -1; - } - - ret = SSL_read(ssl, buf, len); - - switch ((err = SSL_get_error(ssl, ret))) { - case SSL_ERROR_NONE: - return ret; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - errno = EAGAIN; - return -1; - case SSL_ERROR_ZERO_RETURN: - return 0; - default: - g_warning("SSL_read() returned error %d, ret = %d\n", err, ret); - if (ret == 0) - return 0; - return -1; - } -} -#endif - -gint sock_write(SockInfo *sock, const gchar *buf, gint len) -{ - g_return_val_if_fail(sock != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_write(sock->ssl, buf, len); -#endif - return fd_write(sock->sock, buf, len); -} - -gint fd_write(gint fd, const gchar *buf, gint len) -{ - if (fd_check_io(fd, G_IO_OUT) < 0) - return -1; - -#ifdef G_OS_WIN32 - return send(fd, buf, len, 0); -#else - return write(fd, buf, len); -#endif -} - -#if USE_SSL -gint ssl_write(SSL *ssl, const gchar *buf, gint len) -{ - gint ret; - - ret = SSL_write(ssl, buf, len); - - switch (SSL_get_error(ssl, ret)) { - case SSL_ERROR_NONE: - return ret; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - errno = EAGAIN; - return -1; - default: - return -1; - } -} -#endif - -gint sock_write_all(SockInfo *sock, const gchar *buf, gint len) -{ - g_return_val_if_fail(sock != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_write_all(sock->ssl, buf, len); -#endif - return fd_write_all(sock->sock, buf, len); -} - -gint fd_write_all(gint fd, const gchar *buf, gint len) -{ - gint n, wrlen = 0; - - while (len) { - n = fd_write(fd, buf, len); - if (n <= 0) - return -1; - len -= n; - wrlen += n; - buf += n; - } - - return wrlen; -} - -#if USE_SSL -gint ssl_write_all(SSL *ssl, const gchar *buf, gint len) -{ - gint n, wrlen = 0; - - while (len) { - n = ssl_write(ssl, buf, len); - if (n <= 0) - return -1; - len -= n; - wrlen += n; - buf += n; - } - - return wrlen; -} -#endif - -gint fd_recv(gint fd, gchar *buf, gint len, gint flags) -{ - if (fd_check_io(fd, G_IO_IN) < 0) - return -1; - - return recv(fd, buf, len, flags); -} - -gint fd_gets(gint fd, gchar *buf, gint len) -{ - gchar *newline, *bp = buf; - gint n; - - if (--len < 1) - return -1; - do { - if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0) - return -1; - if ((newline = memchr(bp, '\n', n)) != NULL) - n = newline - bp + 1; - if ((n = fd_read(fd, bp, n)) < 0) - return -1; - bp += n; - len -= n; - } while (!newline && len); - - *bp = '\0'; - return bp - buf; -} - -#if USE_SSL -gint ssl_gets(SSL *ssl, gchar *buf, gint len) -{ - gchar *newline, *bp = buf; - gint n; - - if (--len < 1) - return -1; - do { - if ((n = ssl_peek(ssl, bp, len)) <= 0) - return -1; - if ((newline = memchr(bp, '\n', n)) != NULL) - n = newline - bp + 1; - if ((n = ssl_read(ssl, bp, n)) < 0) - return -1; - bp += n; - len -= n; - } while (!newline && len); - - *bp = '\0'; - return bp - buf; -} -#endif - -gint sock_gets(SockInfo *sock, gchar *buf, gint len) -{ - g_return_val_if_fail(sock != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_gets(sock->ssl, buf, len); -#endif - return fd_gets(sock->sock, buf, len); -} - -gint fd_getline(gint fd, gchar **line) -{ - gchar buf[BUFFSIZE]; - gchar *str = NULL; - gint len; - gulong size = 0; - gulong cur_offset = 0; - - while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) { - size += len; - str = g_realloc(str, size + 1); - memcpy(str + cur_offset, buf, len + 1); - cur_offset += len; - if (buf[len - 1] == '\n') - break; - } - - *line = str; - - if (!str) - return -1; - else - return (gint)size; -} - -#if USE_SSL -gint ssl_getline(SSL *ssl, gchar **line) -{ - gchar buf[BUFFSIZE]; - gchar *str = NULL; - gint len; - gulong size = 0; - gulong cur_offset = 0; - - while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) { - size += len; - str = g_realloc(str, size + 1); - memcpy(str + cur_offset, buf, len + 1); - cur_offset += len; - if (buf[len - 1] == '\n') - break; - } - - *line = str; - - if (!str) - return -1; - else - return (gint)size; -} -#endif - -gint sock_getline(SockInfo *sock, gchar **line) -{ - g_return_val_if_fail(sock != NULL, -1); - g_return_val_if_fail(line != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_getline(sock->ssl, line); -#endif - return fd_getline(sock->sock, line); -} - -gint sock_puts(SockInfo *sock, const gchar *buf) -{ - gint ret; - - if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0) - return ret; - return sock_write_all(sock, "\r\n", 2); -} - -/* peek at the socket data without actually reading it */ -#if USE_SSL -gint ssl_peek(SSL *ssl, gchar *buf, gint len) -{ - gint err, ret; - - if (SSL_pending(ssl) == 0) { - if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) - return -1; - } - - ret = SSL_peek(ssl, buf, len); - - switch ((err = SSL_get_error(ssl, ret))) { - case SSL_ERROR_NONE: - return ret; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - errno = EAGAIN; - return -1; - case SSL_ERROR_ZERO_RETURN: - return 0; - default: - g_warning("SSL_peek() returned error %d, ret = %d\n", err, ret); - if (ret == 0) - return 0; - return -1; - } -} -#endif - -gint sock_peek(SockInfo *sock, gchar *buf, gint len) -{ - g_return_val_if_fail(sock != NULL, -1); - -#if USE_SSL - if (sock->ssl) - return ssl_peek(sock->ssl, buf, len); -#endif - return fd_recv(sock->sock, buf, len, MSG_PEEK); -} - -gint sock_close(SockInfo *sock) -{ - if (!sock) - return 0; - -#if USE_SSL - if (sock->ssl) - ssl_done_socket(sock); -#endif - - if (sock->sock_ch) { - g_io_channel_shutdown(sock->sock_ch, FALSE, NULL); - g_io_channel_unref(sock->sock_ch); - } - - g_free(sock->hostname); - g_free(sock); - - return 0; -} - -gint fd_close(gint fd) -{ -#ifdef G_OS_WIN32 - return closesocket(fd); -#else - return close(fd); -#endif -} diff --git a/src/socket.h b/src/socket.h deleted file mode 100644 index 5721a304..00000000 --- a/src/socket.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __SOCKET_H__ -#define __SOCKET_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#if HAVE_NETDB_H -# include -#endif - -typedef struct _SockInfo SockInfo; - -#if USE_SSL -# include "ssl.h" -#endif - -typedef enum -{ - CONN_READY, - CONN_LOOKUPSUCCESS, - CONN_ESTABLISHED, - CONN_LOOKUPFAILED, - CONN_FAILED -} ConnectionState; - -typedef gint (*SockConnectFunc) (SockInfo *sock, - gpointer data); -typedef gboolean (*SockFunc) (SockInfo *sock, - GIOCondition condition, - gpointer data); - -struct _SockInfo -{ - gint sock; -#if USE_SSL - SSL *ssl; -#endif - GIOChannel *sock_ch; - - gchar *hostname; - gushort port; - ConnectionState state; - gpointer data; - - SockFunc callback; - GIOCondition condition; -}; - -gint sock_init (void); -gint sock_cleanup (void); - -gint sock_set_io_timeout (guint sec); - -gint sock_set_nonblocking_mode (SockInfo *sock, gboolean nonblock); -gboolean sock_is_nonblocking_mode (SockInfo *sock); - -guint sock_add_watch (SockInfo *sock, GIOCondition condition, - SockFunc func, gpointer data); - -struct hostent *my_gethostbyname (const gchar *hostname); - -SockInfo *sock_connect (const gchar *hostname, gushort port); -#ifdef G_OS_UNIX -gint sock_connect_async (const gchar *hostname, gushort port, - SockConnectFunc func, gpointer data); -gint sock_connect_async_cancel (gint id); -#endif - -/* Basic I/O functions */ -gint sock_printf (SockInfo *sock, const gchar *format, ...) - G_GNUC_PRINTF(2, 3); -gint sock_read (SockInfo *sock, gchar *buf, gint len); -gint sock_write (SockInfo *sock, const gchar *buf, gint len); -gint sock_write_all (SockInfo *sock, const gchar *buf, gint len); -gint sock_gets (SockInfo *sock, gchar *buf, gint len); -gint sock_getline (SockInfo *sock, gchar **line); -gint sock_puts (SockInfo *sock, const gchar *buf); -gint sock_peek (SockInfo *sock, gchar *buf, gint len); -gint sock_close (SockInfo *sock); - -/* Functions to directly work on FD. They are needed for pipes */ -gint fd_connect_unix (const gchar *path); -gint fd_open_unix (const gchar *path); -gint fd_accept (gint sock); - -gint fd_read (gint sock, gchar *buf, gint len); -gint fd_write (gint sock, const gchar *buf, gint len); -gint fd_write_all (gint sock, const gchar *buf, gint len); -gint fd_gets (gint sock, gchar *buf, gint len); -gint fd_getline (gint sock, gchar **line); -gint fd_close (gint sock); - -/* Functions for SSL */ -#if USE_SSL -gint ssl_read (SSL *ssl, gchar *buf, gint len); -gint ssl_write (SSL *ssl, const gchar *buf, gint len); -gint ssl_write_all (SSL *ssl, const gchar *buf, gint len); -gint ssl_gets (SSL *ssl, gchar *buf, gint len); -gint ssl_getline (SSL *ssl, gchar **line); -gint ssl_peek (SSL *ssl, gchar *buf, gint len); -#endif - -#endif /* __SOCKET_H__ */ diff --git a/src/ssl.c b/src/ssl.c deleted file mode 100644 index b57c60a9..00000000 --- a/src/ssl.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if USE_SSL - -#include "defs.h" - -#include -#include - -#include "utils.h" -#include "ssl.h" - -static SSL_CTX *ssl_ctx_SSLv23; -static SSL_CTX *ssl_ctx_TLSv1; - -void ssl_init(void) -{ - gchar *certs_dir; - - SSL_library_init(); - SSL_load_error_strings(); - - certs_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "certs", NULL); - if (!is_dir_exist(certs_dir)) { - debug_print("%s doesn't exist, or not a directory.\n", - certs_dir); - g_free(certs_dir); - certs_dir = NULL; - } - - ssl_ctx_SSLv23 = SSL_CTX_new(SSLv23_client_method()); - if (ssl_ctx_SSLv23 == NULL) { - debug_print(_("SSLv23 not available\n")); - } else { - debug_print(_("SSLv23 available\n")); - if (certs_dir && - !SSL_CTX_load_verify_locations(ssl_ctx_SSLv23, NULL, - certs_dir)) - g_warning("SSLv23 SSL_CTX_load_verify_locations failed.\n"); - } - - ssl_ctx_TLSv1 = SSL_CTX_new(TLSv1_client_method()); - if (ssl_ctx_TLSv1 == NULL) { - debug_print(_("TLSv1 not available\n")); - } else { - debug_print(_("TLSv1 available\n")); - if (certs_dir && - !SSL_CTX_load_verify_locations(ssl_ctx_TLSv1, NULL, - certs_dir)) - g_warning("TLSv1 SSL_CTX_load_verify_locations failed.\n"); - } - - g_free(certs_dir); -} - -void ssl_done(void) -{ - if (ssl_ctx_SSLv23) { - SSL_CTX_free(ssl_ctx_SSLv23); - } - - if (ssl_ctx_TLSv1) { - SSL_CTX_free(ssl_ctx_TLSv1); - } -} - -gboolean ssl_init_socket(SockInfo *sockinfo) -{ - return ssl_init_socket_with_method(sockinfo, SSL_METHOD_SSLv23); -} - -gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method) -{ - X509 *server_cert; - gint ret; - - switch (method) { - case SSL_METHOD_SSLv23: - if (!ssl_ctx_SSLv23) { - g_warning(_("SSL method not available\n")); - return FALSE; - } - sockinfo->ssl = SSL_new(ssl_ctx_SSLv23); - break; - case SSL_METHOD_TLSv1: - if (!ssl_ctx_TLSv1) { - g_warning(_("SSL method not available\n")); - return FALSE; - } - sockinfo->ssl = SSL_new(ssl_ctx_TLSv1); - break; - default: - g_warning(_("Unknown SSL method *PROGRAM BUG*\n")); - return FALSE; - break; - } - - if (sockinfo->ssl == NULL) { - g_warning(_("Error creating ssl context\n")); - return FALSE; - } - - SSL_set_fd(sockinfo->ssl, sockinfo->sock); - if ((ret = SSL_connect(sockinfo->ssl)) == -1) { - g_warning(_("SSL connect failed (%s)\n"), - ERR_error_string(ERR_get_error(), NULL)); - return FALSE; - } - - /* Get the cipher */ - - debug_print(_("SSL connection using %s\n"), - SSL_get_cipher(sockinfo->ssl)); - - /* Get server's certificate (note: beware of dynamic allocation) */ - - if ((server_cert = SSL_get_peer_certificate(sockinfo->ssl)) != NULL) { - gchar *str; - glong verify_result; - - debug_print(_("Server certificate:\n")); - - if ((str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0)) != NULL) { - debug_print(_(" Subject: %s\n"), str); - g_free(str); - } - - if ((str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0)) != NULL) { - debug_print(_(" Issuer: %s\n"), str); - g_free(str); - } - - verify_result = SSL_get_verify_result(sockinfo->ssl); - if (verify_result == X509_V_OK) - debug_print("SSL verify OK\n"); - else - g_warning("%s: SSL certificate verify failed (%ld: %s)\n", - sockinfo->hostname, verify_result, - X509_verify_cert_error_string(verify_result)); - - X509_free(server_cert); - } - - return TRUE; -} - -void ssl_done_socket(SockInfo *sockinfo) -{ - if (sockinfo->ssl) { - SSL_free(sockinfo->ssl); - } -} - -#endif /* USE_SSL */ diff --git a/src/ssl.h b/src/ssl.h deleted file mode 100644 index 6c13ddac..00000000 --- a/src/ssl.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2002 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __SSL_H__ -#define __SSL_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if USE_SSL - -#include -#include -#include -#include -#include -#include - -#include "socket.h" - -typedef enum { - SSL_METHOD_SSLv23, - SSL_METHOD_TLSv1 -} SSLMethod; - -typedef enum { - SSL_NONE, - SSL_TUNNEL, - SSL_STARTTLS -} SSLType; - -void ssl_init (void); -void ssl_done (void); -gboolean ssl_init_socket (SockInfo *sockinfo); -gboolean ssl_init_socket_with_method (SockInfo *sockinfo, - SSLMethod method); -void ssl_done_socket (SockInfo *sockinfo); - -#endif /* USE_SSL */ - -#endif /* __SSL_H__ */ diff --git a/src/stringtable.c b/src/stringtable.c deleted file mode 100644 index da5e0ea4..00000000 --- a/src/stringtable.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2001 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include - -#include "stringtable.h" -#include "utils.h" - -/* alfons - hashed string table (I wasn't content with GStringChunk; - * can't recall why :-) */ - -#if 0 -#define XXX_DEBUG \ - debug_print -#else -#define XXX_DEBUG \ - if (0) debug_print -#endif - -typedef struct StringEntry_ { - gint ref_count; - gchar *string; -} StringEntry; - -static StringEntry *string_entry_new(const gchar *str) -{ - StringEntry *entry; - - entry = g_new0(StringEntry, 1); - entry->ref_count = 1; - entry->string = g_strdup(str); - return entry; -} - -static void string_entry_free(StringEntry *entry) -{ - g_return_if_fail(entry != NULL); - - g_free(entry->string); - g_free(entry); -} - -StringTable *string_table_new(void) -{ - StringTable *strtable; - - strtable = g_new0(StringTable, 1); - g_return_val_if_fail(strtable != NULL, NULL); - strtable->hash_table = g_hash_table_new(g_str_hash, g_str_equal); - g_return_val_if_fail(strtable->hash_table, NULL); - return strtable; -} - -gchar *string_table_lookup_string(StringTable *table, const gchar *str) -{ - StringEntry *entry; - - entry = g_hash_table_lookup(table->hash_table, str); - - if (entry) { - return entry->string; - } else { - return NULL; - } -} - -gchar *string_table_insert_string(StringTable *table, const gchar *str) -{ - StringEntry *entry; - - entry = g_hash_table_lookup(table->hash_table, str); - - if (entry) { - entry->ref_count++; - XXX_DEBUG ("ref++ for %s (%d)\n", entry->string, - entry->ref_count); - } else { - entry = string_entry_new(str); - XXX_DEBUG ("inserting %s\n", str); - /* insert entry->string instead of str, since it can be - * invalid pointer after this. */ - g_hash_table_insert(table->hash_table, entry->string, entry); - } - - return entry->string; -} - -void string_table_free_string(StringTable *table, const gchar *str) -{ - StringEntry *entry; - - entry = g_hash_table_lookup(table->hash_table, str); - - if (entry) { - entry->ref_count--; - if (entry->ref_count <= 0) { - XXX_DEBUG ("refcount of string %s dropped to zero\n", - entry->string); - g_hash_table_remove(table->hash_table, str); - string_entry_free(entry); - } else { - XXX_DEBUG ("ref-- for %s (%d)\n", entry->string, - entry->ref_count); - } - } -} - -static gboolean string_table_remove_for_each_fn(gchar *key, StringEntry *entry, - gpointer user_data) -{ - g_return_val_if_fail(key != NULL, TRUE); - g_return_val_if_fail(entry != NULL, TRUE); - - string_entry_free(entry); - - return TRUE; -} - -void string_table_free(StringTable *table) -{ - g_return_if_fail(table != NULL); - g_return_if_fail(table->hash_table != NULL); - - g_hash_table_foreach_remove(table->hash_table, - (GHRFunc)string_table_remove_for_each_fn, - NULL); - g_hash_table_destroy(table->hash_table); - g_free(table); -} - -static void string_table_stats_for_each_fn(gchar *key, StringEntry *entry, - guint *totals) -{ - if (entry->ref_count > 1) { - *totals += strlen(key) * (entry->ref_count - 1); - } -} - -void string_table_get_stats(StringTable *table) -{ - guint totals = 0; - - g_hash_table_foreach(table->hash_table, - (GHFunc)string_table_stats_for_each_fn, &totals); - XXX_DEBUG ("TOTAL UNSPILLED %d (%dK)\n", totals, totals / 1024); -} diff --git a/src/stringtable.h b/src/stringtable.h deleted file mode 100644 index 337ef2c7..00000000 --- a/src/stringtable.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * sylpheed -- a gtk+ based, lightweight, and fast e-mail client - * copyright (c) 1999-2001 hiroyuki yamamoto - * - * 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; either version 2 of the license, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef STRINGTABLE_H__ -#define STRINGTABLE_H__ - -#include - -typedef struct { - GHashTable *hash_table; -} StringTable; - -StringTable *string_table_new (void); -void string_table_free (StringTable *table); - -gchar *string_table_lookup_string (StringTable *table, const gchar *str); -gchar *string_table_insert_string (StringTable *table, const gchar *str); -void string_table_free_string (StringTable *table, const gchar *str); - -void string_table_get_stats (StringTable *table); - -#endif /* STRINGTABLE_H__ */ diff --git a/src/unmime.c b/src/unmime.c deleted file mode 100644 index 23d787a3..00000000 --- a/src/unmime.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include - -#include "codeconv.h" -#include "base64.h" -#include "quoted-printable.h" - -#define ENCODED_WORD_BEGIN "=?" -#define ENCODED_WORD_END "?=" - -/* Decodes headers based on RFC2045 and RFC2047. */ - -gchar *unmime_header(const gchar *encoded_str) -{ - const gchar *p = encoded_str; - const gchar *eword_begin_p, *encoding_begin_p, *text_begin_p, - *eword_end_p; - gchar charset[32]; - gchar encoding; - gchar *conv_str; - GString *outbuf; - gchar *out_str; - gsize out_len; - - outbuf = g_string_sized_new(strlen(encoded_str) * 2); - - while (*p != '\0') { - gchar *decoded_text = NULL; - gint len; - - eword_begin_p = strstr(p, ENCODED_WORD_BEGIN); - if (!eword_begin_p) { - g_string_append(outbuf, p); - break; - } - encoding_begin_p = strchr(eword_begin_p + 2, '?'); - if (!encoding_begin_p) { - g_string_append(outbuf, p); - break; - } - text_begin_p = strchr(encoding_begin_p + 1, '?'); - if (!text_begin_p) { - g_string_append(outbuf, p); - break; - } - eword_end_p = strstr(text_begin_p + 1, ENCODED_WORD_END); - if (!eword_end_p) { - g_string_append(outbuf, p); - break; - } - - if (p == encoded_str) { - g_string_append_len(outbuf, p, eword_begin_p - p); - p = eword_begin_p; - } else { - /* ignore spaces between encoded words */ - const gchar *sp; - - for (sp = p; sp < eword_begin_p; sp++) { - if (!g_ascii_isspace(*sp)) { - g_string_append_len - (outbuf, p, eword_begin_p - p); - p = eword_begin_p; - break; - } - } - } - - len = MIN(sizeof(charset) - 1, - encoding_begin_p - (eword_begin_p + 2)); - memcpy(charset, eword_begin_p + 2, len); - charset[len] = '\0'; - encoding = g_ascii_toupper(*(encoding_begin_p + 1)); - - if (encoding == 'B') { - decoded_text = g_malloc - (eword_end_p - (text_begin_p + 1) + 1); - len = base64_decode(decoded_text, text_begin_p + 1, - eword_end_p - (text_begin_p + 1)); - decoded_text[len] = '\0'; - } else if (encoding == 'Q') { - decoded_text = g_malloc - (eword_end_p - (text_begin_p + 1) + 1); - len = qp_decode_q_encoding - (decoded_text, text_begin_p + 1, - eword_end_p - (text_begin_p + 1)); - } else { - g_string_append_len(outbuf, p, eword_end_p + 2 - p); - p = eword_end_p + 2; - continue; - } - - /* convert to UTF-8 */ - conv_str = conv_codeset_strdup(decoded_text, charset, NULL); - if (!conv_str) - conv_str = conv_utf8todisp(decoded_text, NULL); - g_string_append(outbuf, conv_str); - g_free(conv_str); - - g_free(decoded_text); - - p = eword_end_p + 2; - } - - out_str = outbuf->str; - out_len = outbuf->len; - g_string_free(outbuf, FALSE); - - return g_realloc(out_str, out_len + 1); -} diff --git a/src/unmime.h b/src/unmime.h deleted file mode 100644 index be9f390e..00000000 --- a/src/unmime.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __UNMIME_H__ -#define __UNMIME_H__ - -#include - -gchar *unmime_header (const gchar *encoded_str); - -#endif /* __UNMIME_H__ */ diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index dc72b814..00000000 --- a/src/utils.c +++ /dev/null @@ -1,3436 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "defs.h" - -#include -#include -#include -#include -#include -#include - -#if (HAVE_WCTYPE_H && HAVE_WCHAR_H) -# include -# include -#endif -#include -#include -#include -#include -#include -#if HAVE_SYS_WAIT_H -# include -#endif -#include -#include - -#ifdef G_OS_WIN32 -# include -# include -#endif - -#include "utils.h" -#include "socket.h" - -#define BUFFSIZE 8192 - -static gboolean debug_mode = FALSE; - - -#if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX) -gint g_chdir(const gchar *path) -{ -#ifdef G_OS_WIN32 - if (G_WIN32_HAVE_WIDECHAR_API()) { - wchar_t *wpath; - gint retval; - gint save_errno; - - wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); - if (wpath == NULL) { - errno = EINVAL; - return -1; - } - - retval = _wchdir(wpath); - save_errno = errno; - - g_free(wpath); - - errno = save_errno; - return retval; - } else { - gchar *cp_path; - gint retval; - gint save_errno; - - cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); - if (cp_path == NULL) { - errno = EINVAL; - return -1; - } - - retval = chdir(cp_path); - save_errno = errno; - - g_free(cp_path); - - errno = save_errno; - return retval; - } -#else - return chdir(path); -#endif -} - -gint g_chmod(const gchar *path, gint mode) -{ -#ifdef G_OS_WIN32 - if (G_WIN32_HAVE_WIDECHAR_API()) { - wchar_t *wpath; - gint retval; - gint save_errno; - - wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); - if (wpath == NULL) { - errno = EINVAL; - return -1; - } - - retval = _wchmod(wpath, mode); - save_errno = errno; - - g_free(wpath); - - errno = save_errno; - return retval; - } else { - gchar *cp_path; - gint retval; - gint save_errno; - - cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); - if (cp_path == NULL) { - errno = EINVAL; - return -1; - } - - retval = chmod(cp_path, mode); - save_errno = errno; - - g_free(cp_path); - - errno = save_errno; - return retval; - } -#else - return chmod(path, mode); -#endif -} -#endif /* GLIB_CHECK_VERSION && G_OS_UNIX */ - -void list_free_strings(GList *list) -{ - list = g_list_first(list); - - while (list != NULL) { - g_free(list->data); - list = list->next; - } -} - -void slist_free_strings(GSList *list) -{ - while (list != NULL) { - g_free(list->data); - list = list->next; - } -} - -static void hash_free_strings_func(gpointer key, gpointer value, gpointer data) -{ - g_free(key); -} - -void hash_free_strings(GHashTable *table) -{ - g_hash_table_foreach(table, hash_free_strings_func, NULL); -} - -static void hash_free_value_mem_func(gpointer key, gpointer value, - gpointer data) -{ - g_free(value); -} - -void hash_free_value_mem(GHashTable *table) -{ - g_hash_table_foreach(table, hash_free_value_mem_func, NULL); -} - -gint str_case_equal(gconstpointer v, gconstpointer v2) -{ - return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0; -} - -guint str_case_hash(gconstpointer key) -{ - const gchar *p = key; - guint h = *p; - - if (h) { - h = g_ascii_tolower(h); - for (p += 1; *p != '\0'; p++) - h = (h << 5) - h + g_ascii_tolower(*p); - } - - return h; -} - -void ptr_array_free_strings(GPtrArray *array) -{ - gint i; - gchar *str; - - g_return_if_fail(array != NULL); - - for (i = 0; i < array->len; i++) { - str = g_ptr_array_index(array, i); - g_free(str); - } -} - -gboolean str_find(const gchar *haystack, const gchar *needle) -{ - return strstr(haystack, needle) != NULL ? TRUE : FALSE; -} - -gboolean str_case_find(const gchar *haystack, const gchar *needle) -{ - return strcasestr(haystack, needle) != NULL ? TRUE : FALSE; -} - -gboolean str_find_equal(const gchar *haystack, const gchar *needle) -{ - return strcmp(haystack, needle) == 0; -} - -gboolean str_case_find_equal(const gchar *haystack, const gchar *needle) -{ - return g_ascii_strcasecmp(haystack, needle) == 0; -} - -gint to_number(const gchar *nstr) -{ - register const gchar *p; - - if (*nstr == '\0') return -1; - - for (p = nstr; *p != '\0'; p++) - if (!g_ascii_isdigit(*p)) return -1; - - return atoi(nstr); -} - -/* convert integer into string, - nstr must be not lower than 11 characters length */ -gchar *itos_buf(gchar *nstr, gint n) -{ - g_snprintf(nstr, 11, "%d", n); - return nstr; -} - -/* convert integer into string */ -gchar *itos(gint n) -{ - static gchar nstr[11]; - - return itos_buf(nstr, n); -} - -gchar *to_human_readable(off_t size) -{ - static gchar str[10]; - - if (size < 1024) - g_snprintf(str, sizeof(str), _("%dB"), (gint)size); - else if (size >> 10 < 1024) - g_snprintf(str, sizeof(str), _("%.1fKB"), (gfloat)size / (1 << 10)); - else if (size >> 20 < 1024) - g_snprintf(str, sizeof(str), _("%.2fMB"), (gfloat)size / (1 << 20)); - else - g_snprintf(str, sizeof(str), _("%.2fGB"), (gfloat)size / (1 << 30)); - - return str; -} - -/* strcmp with NULL-checking */ -gint strcmp2(const gchar *s1, const gchar *s2) -{ - if (s1 == NULL || s2 == NULL) - return -1; - else - return strcmp(s1, s2); -} - -/* compare paths */ -gint path_cmp(const gchar *s1, const gchar *s2) -{ - gint len1, len2; -#ifdef G_OS_WIN32 - gchar *s1_, *s2_; -#endif - - if (s1 == NULL || s2 == NULL) return -1; - if (*s1 == '\0' || *s2 == '\0') return -1; - - len1 = strlen(s1); - len2 = strlen(s2); - -#ifdef G_OS_WIN32 - Xstrdup_a(s1_, s1, return -1); - Xstrdup_a(s2_, s2, return -1); - subst_char(s1_, '/', G_DIR_SEPARATOR); - subst_char(s2_, '/', G_DIR_SEPARATOR); - if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--; - if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--; - - return strncmp(s1_, s2_, MAX(len1, len2)); -#else - if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--; - if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--; - - return strncmp(s1, s2, MAX(len1, len2)); -#endif -} - -/* remove trailing return code */ -gchar *strretchomp(gchar *str) -{ - register gchar *s; - - if (!*str) return str; - - for (s = str + strlen(str) - 1; - s >= str && (*s == '\n' || *s == '\r'); - s--) - *s = '\0'; - - return str; -} - -/* remove trailing character */ -gchar *strtailchomp(gchar *str, gchar tail_char) -{ - register gchar *s; - - if (!*str) return str; - if (tail_char == '\0') return str; - - for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--) - *s = '\0'; - - return str; -} - -/* remove CR (carriage return) */ -gchar *strcrchomp(gchar *str) -{ - register gchar *s; - - if (!*str) return str; - - s = str + strlen(str) - 1; - if (*s == '\n' && s > str && *(s - 1) == '\r') { - *(s - 1) = '\n'; - *s = '\0'; - } - - return str; -} - -/* Similar to `strstr' but this function ignores the case of both strings. */ -gchar *strcasestr(const gchar *haystack, const gchar *needle) -{ - register size_t haystack_len, needle_len; - - haystack_len = strlen(haystack); - needle_len = strlen(needle); - - if (haystack_len < needle_len || needle_len == 0) - return NULL; - - while (haystack_len >= needle_len) { - if (!g_ascii_strncasecmp(haystack, needle, needle_len)) - return (gchar *)haystack; - else { - haystack++; - haystack_len--; - } - } - - return NULL; -} - -gpointer my_memmem(gconstpointer haystack, size_t haystacklen, - gconstpointer needle, size_t needlelen) -{ - const gchar *haystack_ = (const gchar *)haystack; - const gchar *needle_ = (const gchar *)needle; - const gchar *haystack_cur = (const gchar *)haystack; - - if (needlelen == 1) - return memchr(haystack_, *needle_, haystacklen); - - while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen)) - != NULL) { - if (haystacklen - (haystack_cur - haystack_) < needlelen) - break; - if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0) - return (gpointer)haystack_cur; - else - haystack_cur++; - } - - return NULL; -} - -/* Copy no more than N characters of SRC to DEST, with NULL terminating. */ -gchar *strncpy2(gchar *dest, const gchar *src, size_t n) -{ - register const gchar *s = src; - register gchar *d = dest; - - while (--n && *s) - *d++ = *s++; - *d = '\0'; - - return dest; -} - -#if !HAVE_ISWALNUM -int iswalnum(wint_t wc) -{ - return g_ascii_isalnum((int)wc); -} -#endif - -#if !HAVE_ISWSPACE -int iswspace(wint_t wc) -{ - return g_ascii_isspace((int)wc); -} -#endif - -#if !HAVE_TOWLOWER -wint_t towlower(wint_t wc) -{ - if (wc >= L'A' && wc <= L'Z') - return wc + L'a' - L'A'; - - return wc; -} -#endif - -#if !HAVE_WCSLEN -size_t wcslen(const wchar_t *s) -{ - size_t len = 0; - - while (*s != L'\0') - ++len, ++s; - - return len; -} -#endif - -#if !HAVE_WCSCPY -/* Copy SRC to DEST. */ -wchar_t *wcscpy(wchar_t *dest, const wchar_t *src) -{ - wint_t c; - wchar_t *s = dest; - - do { - c = *src++; - *dest++ = c; - } while (c != L'\0'); - - return s; -} -#endif - -#if !HAVE_WCSNCPY -/* Copy no more than N wide-characters of SRC to DEST. */ -wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n) -{ - wint_t c; - wchar_t *s = dest; - - do { - c = *src++; - *dest++ = c; - if (--n == 0) - return s; - } while (c != L'\0'); - - /* zero fill */ - do - *dest++ = L'\0'; - while (--n > 0); - - return s; -} -#endif - -/* Duplicate S, returning an identical malloc'd string. */ -wchar_t *wcsdup(const wchar_t *s) -{ - wchar_t *new_str; - - if (s) { - new_str = g_new(wchar_t, wcslen(s) + 1); - wcscpy(new_str, s); - } else - new_str = NULL; - - return new_str; -} - -/* Duplicate no more than N wide-characters of S, - returning an identical malloc'd string. */ -wchar_t *wcsndup(const wchar_t *s, size_t n) -{ - wchar_t *new_str; - - if (s) { - new_str = g_new(wchar_t, n + 1); - wcsncpy(new_str, s, n); - new_str[n] = (wchar_t)0; - } else - new_str = NULL; - - return new_str; -} - -wchar_t *strdup_mbstowcs(const gchar *s) -{ - wchar_t *new_str; - - if (s) { - new_str = g_new(wchar_t, strlen(s) + 1); - if (mbstowcs(new_str, s, strlen(s) + 1) < 0) { - g_free(new_str); - new_str = NULL; - } else - new_str = g_realloc(new_str, - sizeof(wchar_t) * (wcslen(new_str) + 1)); - } else - new_str = NULL; - - return new_str; -} - -gchar *strdup_wcstombs(const wchar_t *s) -{ - gchar *new_str; - size_t len; - - if (s) { - len = wcslen(s) * MB_CUR_MAX + 1; - new_str = g_new(gchar, len); - if (wcstombs(new_str, s, len) < 0) { - g_free(new_str); - new_str = NULL; - } else - new_str = g_realloc(new_str, strlen(new_str) + 1); - } else - new_str = NULL; - - return new_str; -} - -/* Compare S1 and S2, ignoring case. */ -gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n) -{ - wint_t c1; - wint_t c2; - - while (n--) { - c1 = towlower(*s1++); - c2 = towlower(*s2++); - if (c1 != c2) - return c1 - c2; - else if (c1 == 0 && c2 == 0) - break; - } - - return 0; -} - -/* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case. */ -wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle) -{ - register size_t haystack_len, needle_len; - - haystack_len = wcslen(haystack); - needle_len = wcslen(needle); - - if (haystack_len < needle_len || needle_len == 0) - return NULL; - - while (haystack_len >= needle_len) { - if (!wcsncasecmp(haystack, needle, needle_len)) - return (wchar_t *)haystack; - else { - haystack++; - haystack_len--; - } - } - - return NULL; -} - -gint get_mbs_len(const gchar *s) -{ - const gchar *p = s; - gint mb_len; - gint len = 0; - - if (!p) - return -1; - - while (*p != '\0') { - mb_len = g_utf8_skip[*(guchar *)p]; - if (mb_len == 0) - break; - else - len++; - - p += mb_len; - } - - return len; -} - -/* Examine if next block is non-ASCII string */ -gboolean is_next_nonascii(const gchar *s) -{ - const gchar *p; - - /* skip head space */ - for (p = s; *p != '\0' && g_ascii_isspace(*p); p++) - ; - for (; *p != '\0' && !g_ascii_isspace(*p); p++) { - if (*(guchar *)p > 127 || *(guchar *)p < 32) - return TRUE; - } - - return FALSE; -} - -gint get_next_word_len(const gchar *s) -{ - gint len = 0; - - for (; *s != '\0' && !g_ascii_isspace(*s); s++, len++) - ; - - return len; -} - -/* compare subjects */ -gint subject_compare(const gchar *s1, const gchar *s2) -{ - gchar *str1, *str2; - - if (!s1 || !s2) return -1; - if (!*s1 || !*s2) return -1; - - Xstrdup_a(str1, s1, return -1); - Xstrdup_a(str2, s2, return -1); - - trim_subject_for_compare(str1); - trim_subject_for_compare(str2); - - if (!*str1 || !*str2) return -1; - - return strcmp(str1, str2); -} - -gint subject_compare_for_sort(const gchar *s1, const gchar *s2) -{ - gchar *str1, *str2; - - if (!s1 || !s2) return -1; - - Xstrdup_a(str1, s1, return -1); - Xstrdup_a(str2, s2, return -1); - - trim_subject_for_sort(str1); - trim_subject_for_sort(str2); - - return g_ascii_strcasecmp(str1, str2); -} - -void trim_subject_for_compare(gchar *str) -{ - gchar *srcp; - - eliminate_parenthesis(str, '[', ']'); - eliminate_parenthesis(str, '(', ')'); - g_strstrip(str); - - while (!g_ascii_strncasecmp(str, "Re:", 3)) { - srcp = str + 3; - while (g_ascii_isspace(*srcp)) srcp++; - memmove(str, srcp, strlen(srcp) + 1); - } -} - -void trim_subject_for_sort(gchar *str) -{ - gchar *srcp; - - g_strstrip(str); - - while (!g_ascii_strncasecmp(str, "Re:", 3)) { - srcp = str + 3; - while (g_ascii_isspace(*srcp)) srcp++; - memmove(str, srcp, strlen(srcp) + 1); - } -} - -void trim_subject(gchar *str) -{ - register gchar *srcp, *destp; - gchar op, cl; - gint in_brace; - - destp = str; - while (!g_ascii_strncasecmp(destp, "Re:", 3)) { - destp += 3; - while (g_ascii_isspace(*destp)) destp++; - } - - if (*destp == '[') { - op = '['; - cl = ']'; - } else if (*destp == '(') { - op = '('; - cl = ')'; - } else - return; - - srcp = destp + 1; - in_brace = 1; - while (*srcp) { - if (*srcp == op) - in_brace++; - else if (*srcp == cl) - in_brace--; - srcp++; - if (in_brace == 0) - break; - } - while (g_ascii_isspace(*srcp)) srcp++; - memmove(destp, srcp, strlen(srcp) + 1); -} - -void eliminate_parenthesis(gchar *str, gchar op, gchar cl) -{ - register gchar *srcp, *destp; - gint in_brace; - - srcp = destp = str; - - while ((destp = strchr(destp, op))) { - in_brace = 1; - srcp = destp + 1; - while (*srcp) { - if (*srcp == op) - in_brace++; - else if (*srcp == cl) - in_brace--; - srcp++; - if (in_brace == 0) - break; - } - while (g_ascii_isspace(*srcp)) srcp++; - memmove(destp, srcp, strlen(srcp) + 1); - } -} - -void extract_parenthesis(gchar *str, gchar op, gchar cl) -{ - register gchar *srcp, *destp; - gint in_brace; - - srcp = destp = str; - - while ((srcp = strchr(destp, op))) { - if (destp > str) - *destp++ = ' '; - memmove(destp, srcp + 1, strlen(srcp)); - in_brace = 1; - while(*destp) { - if (*destp == op) - in_brace++; - else if (*destp == cl) - in_brace--; - - if (in_brace == 0) - break; - - destp++; - } - } - *destp = '\0'; -} - -void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr, - gchar op, gchar cl) -{ - register gchar *srcp, *destp; - gint in_brace; - gboolean in_quote = FALSE; - - srcp = destp = str; - - while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) { - if (destp > str) - *destp++ = ' '; - memmove(destp, srcp + 1, strlen(srcp)); - in_brace = 1; - while(*destp) { - if (*destp == op && !in_quote) - in_brace++; - else if (*destp == cl && !in_quote) - in_brace--; - else if (*destp == quote_chr) - in_quote ^= TRUE; - - if (in_brace == 0) - break; - - destp++; - } - } - *destp = '\0'; -} - -void eliminate_quote(gchar *str, gchar quote_chr) -{ - register gchar *srcp, *destp; - - srcp = destp = str; - - while ((destp = strchr(destp, quote_chr))) { - if ((srcp = strchr(destp + 1, quote_chr))) { - srcp++; - while (g_ascii_isspace(*srcp)) srcp++; - memmove(destp, srcp, strlen(srcp) + 1); - } else { - *destp = '\0'; - break; - } - } -} - -void extract_quote(gchar *str, gchar quote_chr) -{ - register gchar *p; - - if ((str = strchr(str, quote_chr))) { - if ((p = strchr(str + 1, quote_chr))) { - *p = '\0'; - memmove(str, str + 1, p - str); - } - } -} - -void eliminate_address_comment(gchar *str) -{ - register gchar *srcp, *destp; - gint in_brace; - - srcp = destp = str; - - while ((destp = strchr(destp, '"'))) { - if ((srcp = strchr(destp + 1, '"'))) { - srcp++; - if (*srcp == '@') { - destp = srcp + 1; - } else { - while (g_ascii_isspace(*srcp)) srcp++; - memmove(destp, srcp, strlen(srcp) + 1); - } - } else { - *destp = '\0'; - break; - } - } - - srcp = destp = str; - - while ((destp = strchr_with_skip_quote(destp, '"', '('))) { - in_brace = 1; - srcp = destp + 1; - while (*srcp) { - if (*srcp == '(') - in_brace++; - else if (*srcp == ')') - in_brace--; - srcp++; - if (in_brace == 0) - break; - } - while (g_ascii_isspace(*srcp)) srcp++; - memmove(destp, srcp, strlen(srcp) + 1); - } -} - -gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) -{ - gboolean in_quote = FALSE; - - while (*str) { - if (*str == c && !in_quote) - return (gchar *)str; - if (*str == quote_chr) - in_quote ^= TRUE; - str++; - } - - return NULL; -} - -gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) -{ - gboolean in_quote = FALSE; - const gchar *p; - - p = str + strlen(str) - 1; - while (p >= str) { - if (*p == c && !in_quote) - return (gchar *)p; - if (*p == quote_chr) - in_quote ^= TRUE; - p--; - } - - return NULL; -} - -void extract_address(gchar *str) -{ - eliminate_address_comment(str); - if (strchr_with_skip_quote(str, '"', '<')) - extract_parenthesis_with_skip_quote(str, '"', '<', '>'); - g_strstrip(str); -} - -void extract_list_id_str(gchar *str) -{ - if (strchr_with_skip_quote(str, '"', '<')) - extract_parenthesis_with_skip_quote(str, '"', '<', '>'); - g_strstrip(str); -} - -GSList *address_list_append(GSList *addr_list, const gchar *str) -{ - gchar *work; - gchar *workp; - - if (!str) return addr_list; - - Xstrdup_a(work, str, return addr_list); - - eliminate_address_comment(work); - workp = work; - - while (workp && *workp) { - gchar *p, *next; - - if ((p = strchr_with_skip_quote(workp, '"', ','))) { - *p = '\0'; - next = p + 1; - } else - next = NULL; - - if (strchr_with_skip_quote(workp, '"', '<')) - extract_parenthesis_with_skip_quote - (workp, '"', '<', '>'); - - g_strstrip(workp); - if (*workp) - addr_list = g_slist_append(addr_list, g_strdup(workp)); - - workp = next; - } - - return addr_list; -} - -GSList *references_list_prepend(GSList *msgid_list, const gchar *str) -{ - const gchar *strp; - - if (!str) return msgid_list; - strp = str; - - while (strp && *strp) { - const gchar *start, *end; - gchar *msgid; - - if ((start = strchr(strp, '<')) != NULL) { - end = strchr(start + 1, '>'); - if (!end) break; - } else - break; - - msgid = g_strndup(start + 1, end - start - 1); - g_strstrip(msgid); - if (*msgid) - msgid_list = g_slist_prepend(msgid_list, msgid); - else - g_free(msgid); - - strp = end + 1; - } - - return msgid_list; -} - -GSList *references_list_append(GSList *msgid_list, const gchar *str) -{ - GSList *list; - - list = references_list_prepend(NULL, str); - list = g_slist_reverse(list); - msgid_list = g_slist_concat(msgid_list, list); - - return msgid_list; -} - -GSList *newsgroup_list_append(GSList *group_list, const gchar *str) -{ - gchar *work; - gchar *workp; - - if (!str) return group_list; - - Xstrdup_a(work, str, return group_list); - - workp = work; - - while (workp && *workp) { - gchar *p, *next; - - if ((p = strchr_with_skip_quote(workp, '"', ','))) { - *p = '\0'; - next = p + 1; - } else - next = NULL; - - g_strstrip(workp); - if (*workp) - group_list = g_slist_append(group_list, - g_strdup(workp)); - - workp = next; - } - - return group_list; -} - -GList *add_history(GList *list, const gchar *str) -{ - GList *old; - - g_return_val_if_fail(str != NULL, list); - - old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2); - if (old) { - g_free(old->data); - list = g_list_remove(list, old->data); - } else if (g_list_length(list) >= MAX_HISTORY_SIZE) { - GList *last; - - last = g_list_last(list); - if (last) { - g_free(last->data); - g_list_remove(list, last->data); - } - } - - list = g_list_prepend(list, g_strdup(str)); - - return list; -} - -void remove_return(gchar *str) -{ - register gchar *p = str; - - while (*p) { - if (*p == '\n' || *p == '\r') - memmove(p, p + 1, strlen(p)); - else - p++; - } -} - -void remove_space(gchar *str) -{ - register gchar *p = str; - register gint spc; - - while (*p) { - spc = 0; - while (g_ascii_isspace(*(p + spc))) - spc++; - if (spc) - memmove(p, p + spc, strlen(p + spc) + 1); - else - p++; - } -} - -void unfold_line(gchar *str) -{ - register gchar *p = str; - register gint spc; - - while (*p) { - if (*p == '\n' || *p == '\r') { - *p++ = ' '; - spc = 0; - while (g_ascii_isspace(*(p + spc))) - spc++; - if (spc) - memmove(p, p + spc, strlen(p + spc) + 1); - } else - p++; - } -} - -void subst_char(gchar *str, gchar orig, gchar subst) -{ - register gchar *p = str; - - while (*p) { - if (*p == orig) - *p = subst; - p++; - } -} - -void subst_chars(gchar *str, gchar *orig, gchar subst) -{ - register gchar *p = str; - - while (*p) { - if (strchr(orig, *p) != NULL) - *p = subst; - p++; - } -} - -void subst_null(gchar *str, gint len, gchar subst) -{ - register gchar *p = str; - - while (len--) { - if (*p == '\0') - *p = subst; - p++; - } -} - -void subst_for_filename(gchar *str) -{ - subst_chars(str, " \t\r\n\"'/\\", '_'); -} - -gboolean is_header_line(const gchar *str) -{ - if (str[0] == ':') return FALSE; - - while (*str != '\0' && *str != ' ') { - if (*str == ':') - return TRUE; - str++; - } - - return FALSE; -} - -gboolean is_ascii_str(const gchar *str) -{ - const guchar *p = (const guchar *)str; - - while (*p != '\0') { - if (*p != '\t' && *p != ' ' && - *p != '\r' && *p != '\n' && - (*p < 32 || *p >= 127)) - return FALSE; - p++; - } - - return TRUE; -} - -gint get_quote_level(const gchar *str) -{ - const gchar *first_pos; - const gchar *last_pos; - const gchar *p = str; - gint quote_level = -1; - - /* speed up line processing by only searching to the last '>' */ - if ((first_pos = strchr(str, '>')) != NULL) { - /* skip a line if it contains a '<' before the initial '>' */ - if (memchr(str, '<', first_pos - str) != NULL) - return -1; - last_pos = strrchr(first_pos, '>'); - } else - return -1; - - while (p <= last_pos) { - while (p < last_pos) { - if (g_ascii_isspace(*p)) - p++; - else - break; - } - - if (*p == '>') - quote_level++; - else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) { - /* any characters are allowed except '-' and space */ - while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) && - p < last_pos) - p++; - if (*p == '>') - quote_level++; - else - break; - } - - p++; - } - - return quote_level; -} - -gint check_line_length(const gchar *str, gint max_chars, gint *line) -{ - const gchar *p = str, *q; - gint cur_line = 0, len; - - while ((q = strchr(p, '\n')) != NULL) { - len = q - p + 1; - if (len > max_chars) { - if (line) - *line = cur_line; - return -1; - } - p = q + 1; - ++cur_line; - } - - len = strlen(p); - if (len > max_chars) { - if (line) - *line = cur_line; - return -1; - } - - return 0; -} - -gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle) -{ - register guint haystack_len, needle_len; - gboolean in_squote = FALSE, in_dquote = FALSE; - - haystack_len = strlen(haystack); - needle_len = strlen(needle); - - if (haystack_len < needle_len || needle_len == 0) - return NULL; - - while (haystack_len >= needle_len) { - if (!in_squote && !in_dquote && - !strncmp(haystack, needle, needle_len)) - return (gchar *)haystack; - - /* 'foo"bar"' -> foo"bar" - "foo'bar'" -> foo'bar' */ - if (*haystack == '\'') { - if (in_squote) - in_squote = FALSE; - else if (!in_dquote) - in_squote = TRUE; - } else if (*haystack == '\"') { - if (in_dquote) - in_dquote = FALSE; - else if (!in_squote) - in_dquote = TRUE; - } - - haystack++; - haystack_len--; - } - - return NULL; -} - -gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl) -{ - const gchar *p; - gchar quote_chr = '"'; - gint in_brace; - gboolean in_quote = FALSE; - - p = str; - - if ((p = strchr_with_skip_quote(p, quote_chr, op))) { - p++; - in_brace = 1; - while (*p) { - if (*p == op && !in_quote) - in_brace++; - else if (*p == cl && !in_quote) - in_brace--; - else if (*p == quote_chr) - in_quote ^= TRUE; - - if (in_brace == 0) - return (gchar *)p; - - p++; - } - } - - return NULL; -} - -gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl, - gint max_tokens) -{ - GSList *string_list = NULL, *slist; - gchar **str_array; - const gchar *s_op, *s_cl; - guint i, n = 1; - - g_return_val_if_fail(str != NULL, NULL); - - if (max_tokens < 1) - max_tokens = G_MAXINT; - - s_op = strchr_with_skip_quote(str, '"', op); - if (!s_op) return NULL; - str = s_op; - s_cl = strchr_parenthesis_close(str, op, cl); - if (s_cl) { - do { - guint len; - gchar *new_string; - - str++; - len = s_cl - str; - new_string = g_new(gchar, len + 1); - strncpy(new_string, str, len); - new_string[len] = 0; - string_list = g_slist_prepend(string_list, new_string); - n++; - str = s_cl + 1; - - while (*str && g_ascii_isspace(*str)) str++; - if (*str != op) { - string_list = g_slist_prepend(string_list, - g_strdup("")); - n++; - s_op = strchr_with_skip_quote(str, '"', op); - if (!--max_tokens || !s_op) break; - str = s_op; - } else - s_op = str; - s_cl = strchr_parenthesis_close(str, op, cl); - } while (--max_tokens && s_cl); - } - - str_array = g_new(gchar*, n); - - i = n - 1; - - str_array[i--] = NULL; - for (slist = string_list; slist; slist = slist->next) - str_array[i--] = slist->data; - - g_slist_free(string_list); - - return str_array; -} - -gchar **strsplit_with_quote(const gchar *str, const gchar *delim, - gint max_tokens) -{ - GSList *string_list = NULL, *slist; - gchar **str_array, *s, *new_str; - guint i, n = 1, len; - - g_return_val_if_fail(str != NULL, NULL); - g_return_val_if_fail(delim != NULL, NULL); - - if (max_tokens < 1) - max_tokens = G_MAXINT; - - s = strstr_with_skip_quote(str, delim); - if (s) { - guint delimiter_len = strlen(delim); - - do { - len = s - str; - new_str = g_strndup(str, len); - - if (new_str[0] == '\'' || new_str[0] == '\"') { - if (new_str[len - 1] == new_str[0]) { - new_str[len - 1] = '\0'; - memmove(new_str, new_str + 1, len - 1); - } - } - string_list = g_slist_prepend(string_list, new_str); - n++; - str = s + delimiter_len; - s = strstr_with_skip_quote(str, delim); - } while (--max_tokens && s); - } - - if (*str) { - new_str = g_strdup(str); - if (new_str[0] == '\'' || new_str[0] == '\"') { - len = strlen(str); - if (new_str[len - 1] == new_str[0]) { - new_str[len - 1] = '\0'; - memmove(new_str, new_str + 1, len - 1); - } - } - string_list = g_slist_prepend(string_list, new_str); - n++; - } - - str_array = g_new(gchar*, n); - - i = n - 1; - - str_array[i--] = NULL; - for (slist = string_list; slist; slist = slist->next) - str_array[i--] = slist->data; - - g_slist_free(string_list); - - return str_array; -} - -gchar *get_abbrev_newsgroup_name(const gchar *group, gint len) -{ - gchar *abbrev_group; - gchar *ap; - const gchar *p = group; - const gchar *last; - - last = group + strlen(group); - abbrev_group = ap = g_malloc(strlen(group) + 1); - - while (*p) { - while (*p == '.') - *ap++ = *p++; - if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) { - *ap++ = *p++; - while (*p != '.') p++; - } else { - strcpy(ap, p); - return abbrev_group; - } - } - - *ap = '\0'; - return abbrev_group; -} - -gchar *trim_string(const gchar *str, gint len) -{ - const gchar *p = str; - gint mb_len; - gchar *new_str; - gint new_len = 0; - - if (!str) return NULL; - if (strlen(str) <= len) - return g_strdup(str); - if (g_utf8_validate(str, -1, NULL) == FALSE) - return g_strdup(str); - - while (*p != '\0') { - mb_len = g_utf8_skip[*(guchar *)p]; - if (mb_len == 0) - break; - else if (new_len + mb_len > len) - break; - - new_len += mb_len; - p += mb_len; - } - - Xstrndup_a(new_str, str, new_len, return g_strdup(str)); - return g_strconcat(new_str, "...", NULL); -} - -gchar *trim_string_before(const gchar *str, gint len) -{ - const gchar *p = str; - gint mb_len; - gint new_len; - - if (!str) return NULL; - if ((new_len = strlen(str)) <= len) - return g_strdup(str); - if (g_utf8_validate(str, -1, NULL) == FALSE) - return g_strdup(str); - - while (*p != '\0') { - mb_len = g_utf8_skip[*(guchar *)p]; - if (mb_len == 0) - break; - - new_len -= mb_len; - p += mb_len; - - if (new_len <= len) - break; - } - - return g_strconcat("...", p, NULL); -} - -GList *uri_list_extract_filenames(const gchar *uri_list) -{ - GList *result = NULL; - const gchar *p, *q; - gchar *file; - - p = uri_list; - - while (p) { - if (*p != '#') { - while (g_ascii_isspace(*p)) p++; - if (!strncmp(p, "file:", 5)) { - p += 5; - while (*p == '/' && *(p + 1) == '/') p++; - q = p; - while (*q && *q != '\n' && *q != '\r') q++; - - if (q > p) { - q--; - while (q > p && g_ascii_isspace(*q)) - q--; - file = g_malloc(q - p + 2); - strncpy(file, p, q - p + 1); - file[q - p + 1] = '\0'; - decode_uri(file, file); - result = g_list_append(result,file); - } - } - } - p = strchr(p, '\n'); - if (p) p++; - } - - return result; -} - -#define HEX_TO_INT(val, hex) \ -{ \ - gchar c = hex; \ - \ - if ('0' <= c && c <= '9') { \ - val = c - '0'; \ - } else if ('a' <= c && c <= 'f') { \ - val = c - 'a' + 10; \ - } else if ('A' <= c && c <= 'F') { \ - val = c - 'A' + 10; \ - } else { \ - val = 0; \ - } \ -} - -/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped - * characters. - */ -static gint axtoi(const gchar *hex_str) -{ - gint hi, lo; - - HEX_TO_INT(hi, hex_str[0]); - HEX_TO_INT(lo, hex_str[1]); - - return (hi << 4) + lo; -} - -gboolean is_uri_string(const gchar *str) -{ - return (g_ascii_strncasecmp(str, "http://", 7) == 0 || - g_ascii_strncasecmp(str, "https://", 8) == 0 || - g_ascii_strncasecmp(str, "ftp://", 6) == 0 || - g_ascii_strncasecmp(str, "www.", 4) == 0); -} - -gchar *get_uri_path(const gchar *uri) -{ - if (g_ascii_strncasecmp(uri, "http://", 7) == 0) - return (gchar *)(uri + 7); - else if (g_ascii_strncasecmp(uri, "https://", 8) == 0) - return (gchar *)(uri + 8); - else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0) - return (gchar *)(uri + 6); - else - return (gchar *)uri; -} - -gint get_uri_len(const gchar *str) -{ - const gchar *p; - - if (is_uri_string(str)) { - for (p = str; *p != '\0'; p++) { - if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p)) - break; - } - return p - str; - } - - return 0; -} - -/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by - * plusses, and escape characters are used) - * Note: decoded_uri and encoded_uri can point the same location - */ -void decode_uri(gchar *decoded_uri, const gchar *encoded_uri) -{ - gchar *dec = decoded_uri; - const gchar *enc = encoded_uri; - - while (*enc) { - if (*enc == '%') { - enc++; - if (isxdigit((guchar)enc[0]) && - isxdigit((guchar)enc[1])) { - *dec = axtoi(enc); - dec++; - enc += 2; - } - } else { - if (*enc == '+') - *dec = ' '; - else - *dec = *enc; - dec++; - enc++; - } - } - - *dec = '\0'; -} - -gchar *encode_uri(const gchar *filename) -{ - gchar *uri; - - uri = g_filename_to_uri(filename, NULL, NULL); - if (!uri) - uri = g_strconcat("file://", filename, NULL); - - return uri; -} - -gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc, - gchar **subject, gchar **body) -{ - gchar *tmp_mailto; - gchar *p; - - Xstrdup_a(tmp_mailto, mailto, return -1); - - if (!strncmp(tmp_mailto, "mailto:", 7)) - tmp_mailto += 7; - - p = strchr(tmp_mailto, '?'); - if (p) { - *p = '\0'; - p++; - } - - if (to && !*to) - *to = g_strdup(tmp_mailto); - - while (p) { - gchar *field, *value; - - field = p; - - p = strchr(p, '='); - if (!p) break; - *p = '\0'; - p++; - - value = p; - - p = strchr(p, '&'); - if (p) { - *p = '\0'; - p++; - } - - if (*value == '\0') continue; - - if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) { - *cc = g_strdup(value); - } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) { - *bcc = g_strdup(value); - } else if (subject && !*subject && - !g_ascii_strcasecmp(field, "subject")) { - *subject = g_malloc(strlen(value) + 1); - decode_uri(*subject, value); - } else if (body && !*body && - !g_ascii_strcasecmp(field, "body")) { - *body = g_malloc(strlen(value) + 1); - decode_uri(*body, value); - } - } - - return 0; -} - -const gchar *get_home_dir(void) -{ -#ifdef G_OS_WIN32 - static const gchar *home_dir = NULL; - - if (!home_dir) { - home_dir = g_get_home_dir(); - if (!home_dir) - home_dir = "C:\\Sylpheed"; - } - - return home_dir; -#else - return g_get_home_dir(); -#endif -} - -const gchar *get_rc_dir(void) -{ - static gchar *rc_dir = NULL; - - if (!rc_dir) -#ifdef G_OS_WIN32 - rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, - "Application Data", G_DIR_SEPARATOR_S, - RC_DIR, NULL); -#else - rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, - RC_DIR, NULL); -#endif - - return rc_dir; -} - -const gchar *get_old_rc_dir(void) -{ - static gchar *old_rc_dir = NULL; - - if (!old_rc_dir) - old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, - OLD_RC_DIR, NULL); - - return old_rc_dir; -} - -const gchar *get_mail_base_dir(void) -{ -#ifdef G_OS_WIN32 - static gchar *mail_base_dir = NULL; - - if (!mail_base_dir) - mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - "Mailboxes", NULL); - - return mail_base_dir; -#else - return get_home_dir(); -#endif -} - -const gchar *get_news_cache_dir(void) -{ - static gchar *news_cache_dir = NULL; - - if (!news_cache_dir) - news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - NEWS_CACHE_DIR, NULL); - - return news_cache_dir; -} - -const gchar *get_imap_cache_dir(void) -{ - static gchar *imap_cache_dir = NULL; - - if (!imap_cache_dir) - imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - IMAP_CACHE_DIR, NULL); - - return imap_cache_dir; -} - -const gchar *get_mime_tmp_dir(void) -{ - static gchar *mime_tmp_dir = NULL; - - if (!mime_tmp_dir) - mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - MIME_TMP_DIR, NULL); - - return mime_tmp_dir; -} - -const gchar *get_template_dir(void) -{ - static gchar *template_dir = NULL; - - if (!template_dir) - template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - TEMPLATE_DIR, NULL); - - return template_dir; -} - -const gchar *get_tmp_dir(void) -{ - static gchar *tmp_dir = NULL; - - if (!tmp_dir) - tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, - TMP_DIR, NULL); - - return tmp_dir; -} - -gchar *get_tmp_file(void) -{ - gchar *tmp_file; - static guint32 id = 0; - - tmp_file = g_strdup_printf("%s%ctmpfile.%08x", - get_tmp_dir(), G_DIR_SEPARATOR, id++); - - return tmp_file; -} - -const gchar *get_domain_name(void) -{ -#ifdef G_OS_UNIX - static gchar *domain_name = NULL; - - if (!domain_name) { - gchar buf[128] = ""; - struct hostent *hp; - - if (gethostname(buf, sizeof(buf)) < 0) { - perror("gethostname"); - domain_name = "unknown"; - } else { - buf[sizeof(buf) - 1] = '\0'; - if ((hp = my_gethostbyname(buf)) == NULL) { - perror("gethostbyname"); - domain_name = g_strdup(buf); - } else { - domain_name = g_strdup(hp->h_name); - } - } - - debug_print("domain name = %s\n", domain_name); - } - - return domain_name; -#else - return "unknown"; -#endif -} - -off_t get_file_size(const gchar *file) -{ - struct stat s; - - if (g_stat(file, &s) < 0) { - FILE_OP_ERROR(file, "stat"); - return -1; - } - - return s.st_size; -} - -off_t get_file_size_as_crlf(const gchar *file) -{ - FILE *fp; - off_t size = 0; - gchar buf[BUFFSIZE]; - - if ((fp = g_fopen(file, "rb")) == NULL) { - FILE_OP_ERROR(file, "fopen"); - return -1; - } - - while (fgets(buf, sizeof(buf), fp) != NULL) { - strretchomp(buf); - size += strlen(buf) + 2; - } - - if (ferror(fp)) { - FILE_OP_ERROR(file, "fgets"); - size = -1; - } - - fclose(fp); - - return size; -} - -off_t get_left_file_size(FILE *fp) -{ - glong pos; - glong end; - off_t size; - - if ((pos = ftell(fp)) < 0) { - perror("ftell"); - return -1; - } - if (fseek(fp, 0L, SEEK_END) < 0) { - perror("fseek"); - return -1; - } - if ((end = ftell(fp)) < 0) { - perror("fseek"); - return -1; - } - size = end - pos; - if (fseek(fp, pos, SEEK_SET) < 0) { - perror("fseek"); - return -1; - } - - return size; -} - -gboolean file_exist(const gchar *file, gboolean allow_fifo) -{ - struct stat s; - - if (file == NULL) - return FALSE; - - if (g_stat(file, &s) < 0) { - if (ENOENT != errno) FILE_OP_ERROR(file, "stat"); - return FALSE; - } - - if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode))) - return TRUE; - - return FALSE; -} - -gboolean is_dir_exist(const gchar *dir) -{ - if (dir == NULL) - return FALSE; - - return g_file_test(dir, G_FILE_TEST_IS_DIR); -} - -gboolean is_file_entry_exist(const gchar *file) -{ - if (file == NULL) - return FALSE; - - return g_file_test(file, G_FILE_TEST_EXISTS); -} - -gboolean dirent_is_regular_file(struct dirent *d) -{ -#ifdef HAVE_DIRENT_D_TYPE - if (d->d_type == DT_REG) - return TRUE; - else if (d->d_type != DT_UNKNOWN) - return FALSE; -#endif - - return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR); -} - -gboolean dirent_is_directory(struct dirent *d) -{ -#ifdef HAVE_DIRENT_D_TYPE - if (d->d_type == DT_DIR) - return TRUE; - else if (d->d_type != DT_UNKNOWN) - return FALSE; -#endif - - return g_file_test(d->d_name, G_FILE_TEST_IS_DIR); -} - -gint change_dir(const gchar *dir) -{ - gchar *prevdir = NULL; - - if (debug_mode) - prevdir = g_get_current_dir(); - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - if (debug_mode) g_free(prevdir); - return -1; - } else if (debug_mode) { - gchar *cwd; - - cwd = g_get_current_dir(); - if (strcmp(prevdir, cwd) != 0) - g_print("current dir: %s\n", cwd); - g_free(cwd); - g_free(prevdir); - } - - return 0; -} - -gint make_dir(const gchar *dir) -{ - if (g_mkdir(dir, S_IRWXU) < 0) { - FILE_OP_ERROR(dir, "mkdir"); - return -1; - } - if (g_chmod(dir, S_IRWXU) < 0) - FILE_OP_ERROR(dir, "chmod"); - - return 0; -} - -gint make_dir_hier(const gchar *dir) -{ - gchar *parent_dir; - const gchar *p; - - for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) { - parent_dir = g_strndup(dir, p - dir); - if (*parent_dir != '\0') { - if (!is_dir_exist(parent_dir)) { - if (make_dir(parent_dir) < 0) { - g_free(parent_dir); - return -1; - } - } - } - g_free(parent_dir); - } - - if (!is_dir_exist(dir)) { - if (make_dir(dir) < 0) - return -1; - } - - return 0; -} - -gint remove_all_files(const gchar *dir) -{ - GDir *dp; - const gchar *dir_name; - gchar *prev_dir; - - prev_dir = g_get_current_dir(); - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - g_free(prev_dir); - return -1; - } - - if ((dp = g_dir_open(".", 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", dir); - g_free(prev_dir); - return -1; - } - - while ((dir_name = g_dir_read_name(dp)) != NULL) { - if (g_unlink(dir_name) < 0) - FILE_OP_ERROR(dir_name, "unlink"); - } - - g_dir_close(dp); - - if (g_chdir(prev_dir) < 0) { - FILE_OP_ERROR(prev_dir, "chdir"); - g_free(prev_dir); - return -1; - } - - g_free(prev_dir); - - return 0; -} - -gint remove_numbered_files(const gchar *dir, guint first, guint last) -{ - GDir *dp; - const gchar *dir_name; - gchar *prev_dir; - gint file_no; - - prev_dir = g_get_current_dir(); - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - g_free(prev_dir); - return -1; - } - - if ((dp = g_dir_open(".", 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", dir); - g_free(prev_dir); - return -1; - } - - while ((dir_name = g_dir_read_name(dp)) != NULL) { - file_no = to_number(dir_name); - if (file_no > 0 && first <= file_no && file_no <= last) { - if (is_dir_exist(dir_name)) - continue; - if (g_unlink(dir_name) < 0) - FILE_OP_ERROR(dir_name, "unlink"); - } - } - - g_dir_close(dp); - - if (g_chdir(prev_dir) < 0) { - FILE_OP_ERROR(prev_dir, "chdir"); - g_free(prev_dir); - return -1; - } - - g_free(prev_dir); - - return 0; -} - -gint remove_all_numbered_files(const gchar *dir) -{ - return remove_numbered_files(dir, 0, UINT_MAX); -} - -gint remove_expired_files(const gchar *dir, guint hours) -{ - GDir *dp; - const gchar *dir_name; - struct stat s; - gchar *prev_dir; - gint file_no; - time_t mtime, now, expire_time; - - prev_dir = g_get_current_dir(); - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - g_free(prev_dir); - return -1; - } - - if ((dp = g_dir_open(".", 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", dir); - g_free(prev_dir); - return -1; - } - - now = time(NULL); - expire_time = hours * 60 * 60; - - while ((dir_name = g_dir_read_name(dp)) != NULL) { - file_no = to_number(dir_name); - if (file_no > 0) { - if (g_stat(dir_name, &s) < 0) { - FILE_OP_ERROR(dir_name, "stat"); - continue; - } - if (S_ISDIR(s.st_mode)) - continue; - mtime = MAX(s.st_mtime, s.st_atime); - if (now - mtime > expire_time) { - if (g_unlink(dir_name) < 0) - FILE_OP_ERROR(dir_name, "unlink"); - } - } - } - - g_dir_close(dp); - - if (g_chdir(prev_dir) < 0) { - FILE_OP_ERROR(prev_dir, "chdir"); - g_free(prev_dir); - return -1; - } - - g_free(prev_dir); - - return 0; -} - -static gint remove_dir_recursive_real(const gchar *dir) -{ - struct stat s; - GDir *dp; - const gchar *dir_name; - gchar *prev_dir; - - if (g_stat(dir, &s) < 0) { - FILE_OP_ERROR(dir, "stat"); - if (ENOENT == errno) return 0; - return -1; - } - - if (!S_ISDIR(s.st_mode)) { - if (g_unlink(dir) < 0) { - FILE_OP_ERROR(dir, "unlink"); - return -1; - } - - return 0; - } - - prev_dir = g_get_current_dir(); - /* g_print("prev_dir = %s\n", prev_dir); */ - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - g_free(prev_dir); - return -1; - } - - if ((dp = g_dir_open(".", 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", dir); - g_chdir(prev_dir); - g_free(prev_dir); - return -1; - } - - /* remove all files in the directory */ - while ((dir_name = g_dir_read_name(dp)) != NULL) { - /* g_print("removing %s\n", dir_name); */ - - if (is_dir_exist(dir_name)) { - if (remove_dir_recursive_real(dir_name) < 0) { - g_warning("can't remove directory\n"); - return -1; - } - } else { - if (g_unlink(dir_name) < 0) - FILE_OP_ERROR(dir_name, "unlink"); - } - } - - g_dir_close(dp); - - if (g_chdir(prev_dir) < 0) { - FILE_OP_ERROR(prev_dir, "chdir"); - g_free(prev_dir); - return -1; - } - - g_free(prev_dir); - - if (g_rmdir(dir) < 0) { - FILE_OP_ERROR(dir, "rmdir"); - return -1; - } - - return 0; -} - -gint remove_dir_recursive(const gchar *dir) -{ - gchar *cur_dir; - gint ret; - - cur_dir = g_get_current_dir(); - - if (g_chdir(dir) < 0) { - FILE_OP_ERROR(dir, "chdir"); - ret = -1; - goto leave; - } - if (g_chdir("..") < 0) { - FILE_OP_ERROR(dir, "chdir"); - ret = -1; - goto leave; - } - - ret = remove_dir_recursive_real(dir); - -leave: - if (is_dir_exist(cur_dir)) { - if (g_chdir(cur_dir) < 0) { - FILE_OP_ERROR(cur_dir, "chdir"); - } - } - - g_free(cur_dir); - - return ret; -} - -gint rename_force(const gchar *oldpath, const gchar *newpath) -{ -#ifndef G_OS_UNIX - if (!is_file_entry_exist(oldpath)) { - errno = ENOENT; - return -1; - } - if (is_file_exist(newpath)) { - if (g_unlink(newpath) < 0) - FILE_OP_ERROR(newpath, "unlink"); - } -#endif - return g_rename(oldpath, newpath); -} - -gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup) -{ - FILE *src_fp, *dest_fp; - gint n_read; - gchar buf[BUFSIZ]; - gchar *dest_bak = NULL; - gboolean err = FALSE; - - if ((src_fp = g_fopen(src, "rb")) == NULL) { - FILE_OP_ERROR(src, "fopen"); - return -1; - } - if (is_file_exist(dest)) { - dest_bak = g_strconcat(dest, ".bak", NULL); - if (rename_force(dest, dest_bak) < 0) { - FILE_OP_ERROR(dest, "rename"); - fclose(src_fp); - g_free(dest_bak); - return -1; - } - } - - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - fclose(src_fp); - if (dest_bak) { - if (rename_force(dest_bak, dest) < 0) - FILE_OP_ERROR(dest_bak, "rename"); - g_free(dest_bak); - } - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning(_("can't change file mode\n")); - } - - while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) { - if (n_read < sizeof(buf) && ferror(src_fp)) - break; - if (fwrite(buf, n_read, 1, dest_fp) < 1) { - g_warning(_("writing to %s failed.\n"), dest); - fclose(dest_fp); - fclose(src_fp); - g_unlink(dest); - if (dest_bak) { - if (rename_force(dest_bak, dest) < 0) - FILE_OP_ERROR(dest_bak, "rename"); - g_free(dest_bak); - } - return -1; - } - } - - if (ferror(src_fp)) { - FILE_OP_ERROR(src, "fread"); - err = TRUE; - } - fclose(src_fp); - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; - } - - if (err) { - g_unlink(dest); - if (dest_bak) { - if (rename_force(dest_bak, dest) < 0) - FILE_OP_ERROR(dest_bak, "rename"); - g_free(dest_bak); - } - return -1; - } - - if (keep_backup == FALSE && dest_bak) - g_unlink(dest_bak); - - g_free(dest_bak); - - return 0; -} - -gint copy_dir(const gchar *src, const gchar *dest) -{ - GDir *dir; - const gchar *dir_name; - gchar *src_file; - gchar *dest_file; - - if ((dir = g_dir_open(src, 0, NULL)) == NULL) { - g_warning("failed to open directory: %s\n", src); - return -1; - } - - if (make_dir_hier(dest) < 0) { - g_dir_close(dir); - return -1; - } - - while ((dir_name = g_dir_read_name(dir)) != NULL) { - src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); - dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, - NULL); - if (is_file_exist(src_file)) - copy_file(src_file, dest_file, FALSE); - g_free(dest_file); - g_free(src_file); - } - - g_dir_close(dir); - - return 0; -} - -gint move_file(const gchar *src, const gchar *dest, gboolean overwrite) -{ - if (overwrite == FALSE && is_file_exist(dest)) { - g_warning("move_file(): file %s already exists.", dest); - return -1; - } - - if (rename_force(src, dest) == 0) return 0; - - if (EXDEV != errno) { - FILE_OP_ERROR(src, "rename"); - return -1; - } - - if (copy_file(src, dest, FALSE) < 0) return -1; - - g_unlink(src); - - return 0; -} - -gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) -{ - FILE *dest_fp; - gint n_read; - gint bytes_left, to_read; - gchar buf[BUFSIZ]; - gboolean err = FALSE; - - if (fseek(fp, offset, SEEK_SET) < 0) { - perror("fseek"); - return -1; - } - - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning("can't change file mode\n"); - } - - bytes_left = length; - to_read = MIN(bytes_left, sizeof(buf)); - - while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) { - if (n_read < to_read && ferror(fp)) - break; - if (fwrite(buf, n_read, 1, dest_fp) < 1) { - g_warning(_("writing to %s failed.\n"), dest); - fclose(dest_fp); - g_unlink(dest); - return -1; - } - bytes_left -= n_read; - if (bytes_left == 0) - break; - to_read = MIN(bytes_left, sizeof(buf)); - } - - if (ferror(fp)) { - perror("fread"); - err = TRUE; - } - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; - } - - if (err) { - g_unlink(dest); - return -1; - } - - return 0; -} - -/* convert line endings into CRLF. If the last line doesn't end with - * linebreak, add it. - */ -gchar *canonicalize_str(const gchar *str) -{ - const gchar *p; - guint new_len = 0; - gchar *out, *outp; - - for (p = str; *p != '\0'; ++p) { - if (*p != '\r') { - ++new_len; - if (*p == '\n') - ++new_len; - } - } - if (p == str || *(p - 1) != '\n') - new_len += 2; - - out = outp = g_malloc(new_len + 1); - for (p = str; *p != '\0'; ++p) { - if (*p != '\r') { - if (*p == '\n') - *outp++ = '\r'; - *outp++ = *p; - } - } - if (p == str || *(p - 1) != '\n') { - *outp++ = '\r'; - *outp++ = '\n'; - } - *outp = '\0'; - - return out; -} - -gint canonicalize_file(const gchar *src, const gchar *dest) -{ - FILE *src_fp, *dest_fp; - gchar buf[BUFFSIZE]; - gint len; - gboolean err = FALSE; - gboolean last_linebreak = FALSE; - - if ((src_fp = g_fopen(src, "rb")) == NULL) { - FILE_OP_ERROR(src, "fopen"); - return -1; - } - - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - fclose(src_fp); - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning("can't change file mode\n"); - } - - while (fgets(buf, sizeof(buf), src_fp) != NULL) { - gint r = 0; - - len = strlen(buf); - if (len == 0) break; - last_linebreak = FALSE; - - if (buf[len - 1] != '\n') { - last_linebreak = TRUE; - r = fputs(buf, dest_fp); - } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') { - r = fputs(buf, dest_fp); - } else { - if (len > 1) { - r = fwrite(buf, len - 1, 1, dest_fp); - if (r != 1) - r = EOF; - } - if (r != EOF) - r = fputs("\r\n", dest_fp); - } - - if (r == EOF) { - g_warning("writing to %s failed.\n", dest); - fclose(dest_fp); - fclose(src_fp); - g_unlink(dest); - return -1; - } - } - - if (last_linebreak == TRUE) { - if (fputs("\r\n", dest_fp) == EOF) - err = TRUE; - } - - if (ferror(src_fp)) { - FILE_OP_ERROR(src, "fgets"); - err = TRUE; - } - fclose(src_fp); - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; - } - - if (err) { - g_unlink(dest); - return -1; - } - - return 0; -} - -gint canonicalize_file_replace(const gchar *file) -{ - gchar *tmp_file; - - tmp_file = get_tmp_file(); - - if (canonicalize_file(file, tmp_file) < 0) { - g_free(tmp_file); - return -1; - } - - if (move_file(tmp_file, file, TRUE) < 0) { - g_warning("can't replace %s .\n", file); - g_unlink(tmp_file); - g_free(tmp_file); - return -1; - } - - g_free(tmp_file); - return 0; -} - -gint uncanonicalize_file(const gchar *src, const gchar *dest) -{ - FILE *src_fp, *dest_fp; - gchar buf[BUFFSIZE]; - gboolean err = FALSE; - - if ((src_fp = g_fopen(src, "rb")) == NULL) { - FILE_OP_ERROR(src, "fopen"); - return -1; - } - - if ((dest_fp = g_fopen(dest, "wb")) == NULL) { - FILE_OP_ERROR(dest, "fopen"); - fclose(src_fp); - return -1; - } - - if (change_file_mode_rw(dest_fp, dest) < 0) { - FILE_OP_ERROR(dest, "chmod"); - g_warning("can't change file mode\n"); - } - - while (fgets(buf, sizeof(buf), src_fp) != NULL) { - strcrchomp(buf); - if (fputs(buf, dest_fp) == EOF) { - g_warning("writing to %s failed.\n", dest); - fclose(dest_fp); - fclose(src_fp); - g_unlink(dest); - return -1; - } - } - - if (ferror(src_fp)) { - FILE_OP_ERROR(src, "fgets"); - err = TRUE; - } - fclose(src_fp); - if (fclose(dest_fp) == EOF) { - FILE_OP_ERROR(dest, "fclose"); - err = TRUE; - } - - if (err) { - g_unlink(dest); - return -1; - } - - return 0; -} - -gint uncanonicalize_file_replace(const gchar *file) -{ - gchar *tmp_file; - - tmp_file = get_tmp_file(); - - if (uncanonicalize_file(file, tmp_file) < 0) { - g_free(tmp_file); - return -1; - } - - if (move_file(tmp_file, file, TRUE) < 0) { - g_warning("can't replace %s .\n", file); - g_unlink(tmp_file); - g_free(tmp_file); - return -1; - } - - g_free(tmp_file); - return 0; -} - -gchar *normalize_newlines(const gchar *str) -{ - const gchar *p = str; - gchar *out, *outp; - - out = outp = g_malloc(strlen(str) + 1); - for (p = str; *p != '\0'; ++p) { - if (*p == '\r') { - if (*(p + 1) != '\n') - *outp++ = '\n'; - } else - *outp++ = *p; - } - - *outp = '\0'; - - return out; -} - -gchar *get_outgoing_rfc2822_str(FILE *fp) -{ - gchar buf[BUFFSIZE]; - GString *str; - gchar *ret; - - str = g_string_new(NULL); - - /* output header part */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - strretchomp(buf); - if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) { - gint next; - - for (;;) { - next = fgetc(fp); - if (next == EOF) - break; - else if (next != ' ' && next != '\t') { - ungetc(next, fp); - break; - } - if (fgets(buf, sizeof(buf), fp) == NULL) - break; - } -#if 0 - } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) { - get_rfc822_date(buf, sizeof(buf)); - g_string_append_printf(str, "Date: %s\r\n", buf); -#endif - } else { - g_string_append(str, buf); - g_string_append(str, "\r\n"); - if (buf[0] == '\0') - break; - } - } - - /* output body part */ - while (fgets(buf, sizeof(buf), fp) != NULL) { - strretchomp(buf); - if (buf[0] == '.') - g_string_append_c(str, '.'); - g_string_append(str, buf); - g_string_append(str, "\r\n"); - } - - ret = str->str; - g_string_free(str, FALSE); - - return ret; -} - -/* - * Create a new boundary in a way that it is very unlikely that this - * will occur in the following text. It would be easy to ensure - * uniqueness if everything is either quoted-printable or base64 - * encoded (note that conversion is allowed), but because MIME bodies - * may be nested, it may happen that the same boundary has already - * been used. We avoid scanning the message for conflicts and hope the - * best. - * - * boundary := 0*69 bcharsnospace - * bchars := bcharsnospace / " " - * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / - * "+" / "_" / "," / "-" / "." / - * "/" / ":" / "=" / "?" - * - * some special characters removed because of buggy MTAs - */ - -gchar *generate_mime_boundary(const gchar *prefix) -{ - static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "1234567890+_./="; - gchar buf_uniq[17]; - gchar buf_date[64]; - gint i; - - for (i = 0; i < sizeof(buf_uniq) - 1; i++) - buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)]; - buf_uniq[i] = '\0'; - - get_rfc822_date(buf_date, sizeof(buf_date)); - subst_char(buf_date, ' ', '_'); - subst_char(buf_date, ',', '_'); - subst_char(buf_date, ':', '_'); - - return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart", - buf_date, buf_uniq); -} - -gint change_file_mode_rw(FILE *fp, const gchar *file) -{ -#if HAVE_FCHMOD - return fchmod(fileno(fp), S_IRUSR|S_IWUSR); -#else - return g_chmod(file, S_IRUSR|S_IWUSR); -#endif -} - -FILE *my_tmpfile(void) -{ -#if HAVE_MKSTEMP - const gchar suffix[] = ".XXXXXX"; - const gchar *tmpdir; - guint tmplen; - const gchar *progname; - guint proglen; - gchar *fname; - gint fd; - FILE *fp; - - tmpdir = get_tmp_dir(); - tmplen = strlen(tmpdir); - progname = g_get_prgname(); - proglen = strlen(progname); - Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix), - return tmpfile()); - - memcpy(fname, tmpdir, tmplen); - fname[tmplen] = G_DIR_SEPARATOR; - memcpy(fname + tmplen + 1, progname, proglen); - memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix)); - - fd = mkstemp(fname); - if (fd < 0) - return tmpfile(); - - g_unlink(fname); - - fp = fdopen(fd, "w+b"); - if (!fp) - close(fd); - else - return fp; -#endif /* HAVE_MKSTEMP */ - - return tmpfile(); -} - -FILE *str_open_as_stream(const gchar *str) -{ - FILE *fp; - size_t len; - - g_return_val_if_fail(str != NULL, NULL); - - fp = my_tmpfile(); - if (!fp) { - FILE_OP_ERROR("str_open_as_stream", "my_tmpfile"); - return NULL; - } - - len = strlen(str); - if (len == 0) return fp; - - if (fwrite(str, len, 1, fp) != 1) { - FILE_OP_ERROR("str_open_as_stream", "fwrite"); - fclose(fp); - return NULL; - } - - rewind(fp); - return fp; -} - -gint str_write_to_file(const gchar *str, const gchar *file) -{ - FILE *fp; - size_t len; - - g_return_val_if_fail(str != NULL, -1); - g_return_val_if_fail(file != NULL, -1); - - if ((fp = g_fopen(file, "wb")) == NULL) { - FILE_OP_ERROR(file, "fopen"); - return -1; - } - - len = strlen(str); - if (len == 0) { - fclose(fp); - return 0; - } - - if (fwrite(str, len, 1, fp) != 1) { - FILE_OP_ERROR(file, "fwrite"); - fclose(fp); - g_unlink(file); - return -1; - } - - if (fclose(fp) == EOF) { - FILE_OP_ERROR(file, "fclose"); - g_unlink(file); - return -1; - } - - return 0; -} - -gchar *file_read_to_str(const gchar *file) -{ - FILE *fp; - gchar *str; - - g_return_val_if_fail(file != NULL, NULL); - - if ((fp = g_fopen(file, "rb")) == NULL) { - FILE_OP_ERROR(file, "fopen"); - return NULL; - } - - str = file_read_stream_to_str(fp); - - fclose(fp); - - return str; -} - -gchar *file_read_stream_to_str(FILE *fp) -{ - GByteArray *array; - guchar buf[BUFSIZ]; - gint n_read; - gchar *str; - - g_return_val_if_fail(fp != NULL, NULL); - - array = g_byte_array_new(); - - while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { - if (n_read < sizeof(buf) && ferror(fp)) - break; - g_byte_array_append(array, buf, n_read); - } - - if (ferror(fp)) { - FILE_OP_ERROR("file stream", "fread"); - g_byte_array_free(array, TRUE); - return NULL; - } - - buf[0] = '\0'; - g_byte_array_append(array, buf, 1); - str = (gchar *)array->data; - g_byte_array_free(array, FALSE); - - return str; -} - -gint execute_async(gchar *const argv[]) -{ - g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); - - if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, FALSE) == FALSE) { - g_warning("Can't execute command: %s\n", argv[0]); - return -1; - } - - return 0; -} - -gint execute_sync(gchar *const argv[]) -{ - gint status; - - g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); - - if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, NULL, &status, NULL) == FALSE) { - g_warning("Can't execute command: %s\n", argv[0]); - return -1; - } - -#ifdef G_OS_UNIX - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return -1; -#else - return status; -#endif -} - -gint execute_command_line(const gchar *cmdline, gboolean async) -{ - gchar **argv; - gint ret; - - debug_print("execute_command_line(): executing: %s\n", cmdline); - - argv = strsplit_with_quote(cmdline, " ", 0); - - if (async) - ret = execute_async(argv); - else - ret = execute_sync(argv); - - g_strfreev(argv); - - return ret; -} - -gchar *get_command_output(const gchar *cmdline) -{ - gchar *child_stdout; - gint status; - - g_return_val_if_fail(cmdline != NULL, NULL); - - debug_print("get_command_output(): executing: %s\n", cmdline); - - if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status, - NULL) == FALSE) { - g_warning("Can't execute command: %s\n", cmdline); - return NULL; - } - - return child_stdout; -} - -gint open_uri(const gchar *uri, const gchar *cmdline) -{ - gchar buf[BUFFSIZE]; - gchar *p; - - g_return_val_if_fail(uri != NULL, -1); - - if (cmdline && - (p = strchr(cmdline, '%')) && *(p + 1) == 's' && - !strchr(p + 2, '%')) - g_snprintf(buf, sizeof(buf), cmdline, uri); - else { - if (cmdline) - g_warning("Open URI command line is invalid " - "(there must be only one '%%s'): %s", - cmdline); - g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri); - } - - execute_command_line(buf, TRUE); - - return 0; -} - -time_t remote_tzoffset_sec(const gchar *zone) -{ - static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT"; - gchar zone3[4]; - gchar *p; - gchar c; - gint iustz; - gint offset; - time_t remoteoffset; - - strncpy(zone3, zone, 3); - zone3[3] = '\0'; - remoteoffset = 0; - - if (sscanf(zone, "%c%d", &c, &offset) == 2 && - (c == '+' || c == '-')) { - remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60; - if (c == '-') - remoteoffset = -remoteoffset; - } else if (!strncmp(zone, "UT" , 2) || - !strncmp(zone, "GMT", 2)) { - remoteoffset = 0; - } else if (strlen(zone3) == 3) { - for (p = ustzstr; *p != '\0'; p += 3) { - if (!g_ascii_strncasecmp(p, zone3, 3)) { - iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8; - remoteoffset = iustz * 3600; - break; - } - } - if (*p == '\0') - return -1; - } else if (strlen(zone3) == 1) { - switch (zone[0]) { - case 'Z': remoteoffset = 0; break; - case 'A': remoteoffset = -1; break; - case 'B': remoteoffset = -2; break; - case 'C': remoteoffset = -3; break; - case 'D': remoteoffset = -4; break; - case 'E': remoteoffset = -5; break; - case 'F': remoteoffset = -6; break; - case 'G': remoteoffset = -7; break; - case 'H': remoteoffset = -8; break; - case 'I': remoteoffset = -9; break; - case 'K': remoteoffset = -10; break; /* J is not used */ - case 'L': remoteoffset = -11; break; - case 'M': remoteoffset = -12; break; - case 'N': remoteoffset = 1; break; - case 'O': remoteoffset = 2; break; - case 'P': remoteoffset = 3; break; - case 'Q': remoteoffset = 4; break; - case 'R': remoteoffset = 5; break; - case 'S': remoteoffset = 6; break; - case 'T': remoteoffset = 7; break; - case 'U': remoteoffset = 8; break; - case 'V': remoteoffset = 9; break; - case 'W': remoteoffset = 10; break; - case 'X': remoteoffset = 11; break; - case 'Y': remoteoffset = 12; break; - default: remoteoffset = 0; break; - } - remoteoffset = remoteoffset * 3600; - } else - return -1; - - return remoteoffset; -} - -time_t tzoffset_sec(time_t *now) -{ - struct tm gmt, *lt; - gint off; - - gmt = *gmtime(now); - lt = localtime(now); - - off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; - - if (lt->tm_year < gmt.tm_year) - off -= 24 * 60; - else if (lt->tm_year > gmt.tm_year) - off += 24 * 60; - else if (lt->tm_yday < gmt.tm_yday) - off -= 24 * 60; - else if (lt->tm_yday > gmt.tm_yday) - off += 24 * 60; - - if (off >= 24 * 60) /* should be impossible */ - off = 23 * 60 + 59; /* if not, insert silly value */ - if (off <= -24 * 60) - off = -(23 * 60 + 59); - - return off * 60; -} - -/* calculate timezone offset */ -gchar *tzoffset(time_t *now) -{ - static gchar offset_string[6]; - struct tm gmt, *lt; - gint off; - gchar sign = '+'; - - gmt = *gmtime(now); - lt = localtime(now); - - off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; - - if (lt->tm_year < gmt.tm_year) - off -= 24 * 60; - else if (lt->tm_year > gmt.tm_year) - off += 24 * 60; - else if (lt->tm_yday < gmt.tm_yday) - off -= 24 * 60; - else if (lt->tm_yday > gmt.tm_yday) - off += 24 * 60; - - if (off < 0) { - sign = '-'; - off = -off; - } - - if (off >= 24 * 60) /* should be impossible */ - off = 23 * 60 + 59; /* if not, insert silly value */ - - sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60); - - return offset_string; -} - -void get_rfc822_date(gchar *buf, gint len) -{ - struct tm *lt; - time_t t; - gchar day[4], mon[4]; - gint dd, hh, mm, ss, yyyy; - - t = time(NULL); - lt = localtime(&t); - - sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n", - day, mon, &dd, &hh, &mm, &ss, &yyyy); - g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s", - day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t)); -} - -/* just a wrapper to suppress the warning of gcc about %c */ -size_t my_strftime(gchar *s, size_t max, const gchar *format, - const struct tm *tm) -{ - return strftime(s, max, format, tm); -} - -static FILE *log_fp = NULL; - -void set_log_file(const gchar *filename) -{ - if (log_fp) return; - log_fp = g_fopen(filename, "wb"); - if (!log_fp) - FILE_OP_ERROR(filename, "fopen"); -} - -void close_log_file(void) -{ - if (log_fp) { - fclose(log_fp); - log_fp = NULL; - } -} - -static guint log_verbosity_count = 0; - -void set_log_verbosity(gboolean verbose) -{ - if (verbose) - log_verbosity_count++; - else if (log_verbosity_count > 0) - log_verbosity_count--; -} - -gboolean get_debug_mode(void) -{ - return debug_mode; -} - -void set_debug_mode(gboolean enable) -{ - debug_mode = enable; -} - -static void log_dummy_func(const gchar *str) -{ -} - -static LogFunc log_print_ui_func = log_dummy_func; -static LogFunc log_message_ui_func = log_dummy_func; -static LogFunc log_warning_ui_func = log_dummy_func; -static LogFunc log_error_ui_func = log_dummy_func; - -static LogFunc log_show_status_func = log_dummy_func; - -void set_log_ui_func(LogFunc print_func, LogFunc message_func, - LogFunc warning_func, LogFunc error_func) -{ - log_print_ui_func = print_func; - log_message_ui_func = message_func; - log_warning_ui_func = warning_func; - log_error_ui_func = error_func; -} - -void set_log_show_status_func(LogFunc status_func) -{ - log_show_status_func = status_func; -} - -void debug_print(const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE]; - - if (!debug_mode) return; - - va_start(args, format); - g_vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - - g_print("%s", buf); -} - -#define TIME_LEN 11 - -void log_print(const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE + TIME_LEN]; - time_t t; - - time(&t); - strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); - - va_start(args, format); - g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); - va_end(args); - - if (debug_mode) fputs(buf, stdout); - log_print_ui_func(buf); - if (log_fp) { - fputs(buf, log_fp); - fflush(log_fp); - } - if (log_verbosity_count) - log_show_status_func(buf + TIME_LEN); -} - -void log_message(const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE + TIME_LEN]; - time_t t; - - time(&t); - strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); - - va_start(args, format); - g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); - va_end(args); - - if (debug_mode) g_message("%s", buf + TIME_LEN); - log_message_ui_func(buf + TIME_LEN); - if (log_fp) { - fwrite(buf, TIME_LEN, 1, log_fp); - fputs("* message: ", log_fp); - fputs(buf + TIME_LEN, log_fp); - fflush(log_fp); - } - log_show_status_func(buf + TIME_LEN); -} - -void log_warning(const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE + TIME_LEN]; - time_t t; - - time(&t); - strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); - - va_start(args, format); - g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); - va_end(args); - - g_warning("%s", buf); - log_warning_ui_func(buf + TIME_LEN); - if (log_fp) { - fwrite(buf, TIME_LEN, 1, log_fp); - fputs("** warning: ", log_fp); - fputs(buf + TIME_LEN, log_fp); - fflush(log_fp); - } -} - -void log_error(const gchar *format, ...) -{ - va_list args; - gchar buf[BUFFSIZE + TIME_LEN]; - time_t t; - - time(&t); - strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); - - va_start(args, format); - g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); - va_end(args); - - g_warning("%s", buf); - log_error_ui_func(buf + TIME_LEN); - if (log_fp) { - fwrite(buf, TIME_LEN, 1, log_fp); - fputs("*** error: ", log_fp); - fputs(buf + TIME_LEN, log_fp); - fflush(log_fp); - } -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index fbda26a3..00000000 --- a/src/utils.h +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __UTILS_H__ -#define __UTILS_H__ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_ALLOCA_H -# include -#endif -#if HAVE_WCHAR_H -# include -#endif - -/* Wrappers for C library function that take pathname arguments. */ -#if GLIB_CHECK_VERSION(2, 6, 0) -# include -#else - -#define g_open open -#define g_rename rename -#define g_mkdir mkdir -#define g_stat stat -#define g_lstat lstat -#define g_unlink unlink -#define g_remove remove -#define g_rmdir rmdir -#define g_fopen fopen -#define g_freopen freopen - -#endif /* GLIB_CHECK_VERSION */ - -#if !GLIB_CHECK_VERSION(2, 7, 0) - -#ifdef G_OS_UNIX -#define g_chdir chdir -#define g_chmod chmod -#else -gint g_chdir (const gchar *path); -gint g_chmod (const gchar *path, - gint mode); -#endif /* G_OS_UNIX */ - -#endif /* !GLIB_CHECK_VERSION */ - -/* The AC_CHECK_SIZEOF() in configure fails for some machines. - * we provide some fallback values here */ -#if !SIZEOF_UNSIGNED_SHORT - #undef SIZEOF_UNSIGNED_SHORT - #define SIZEOF_UNSIGNED_SHORT 2 -#endif -#if !SIZEOF_UNSIGNED_INT - #undef SIZEOF_UNSIGNED_INT - #define SIZEOF_UNSIGNED_INT 4 -#endif -#if !SIZEOF_UNSIGNED_LONG - #undef SIZEOF_UNSIGNED_LONG - #define SIZEOF_UNSIGNED_LONG 4 -#endif - -#ifndef HAVE_U32_TYPEDEF - #undef u32 /* maybe there is a macro with this name */ - typedef guint32 u32; - #define HAVE_U32_TYPEDEF -#endif - -#ifndef BIG_ENDIAN_HOST - #if (G_BYTE_ORDER == G_BIG_ENDIAN) - #define BIG_ENDIAN_HOST 1 - #endif -#endif - -#define CHDIR_RETURN_IF_FAIL(dir) \ -{ \ - if (change_dir(dir) < 0) return; \ -} - -#define CHDIR_RETURN_VAL_IF_FAIL(dir, val) \ -{ \ - if (change_dir(dir) < 0) return val; \ -} - -#define Xalloca(ptr, size, iffail) \ -{ \ - if ((ptr = alloca(size)) == NULL) { \ - g_warning("can't allocate memory\n"); \ - iffail; \ - } \ -} - -#define Xstrdup_a(ptr, str, iffail) \ -{ \ - gchar *__tmp; \ - \ - if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \ - g_warning("can't allocate memory\n"); \ - iffail; \ - } else \ - strcpy(__tmp, str); \ - \ - ptr = __tmp; \ -} - -#define Xstrndup_a(ptr, str, len, iffail) \ -{ \ - gchar *__tmp; \ - \ - if ((__tmp = alloca(len + 1)) == NULL) { \ - g_warning("can't allocate memory\n"); \ - iffail; \ - } else { \ - strncpy(__tmp, str, len); \ - __tmp[len] = '\0'; \ - } \ - \ - ptr = __tmp; \ -} - -#define Xstrcat_a(ptr, str1, str2, iffail) \ -{ \ - gchar *__tmp; \ - gint len1, len2; \ - \ - len1 = strlen(str1); \ - len2 = strlen(str2); \ - if ((__tmp = alloca(len1 + len2 + 1)) == NULL) { \ - g_warning("can't allocate memory\n"); \ - iffail; \ - } else { \ - memcpy(__tmp, str1, len1); \ - memcpy(__tmp + len1, str2, len2 + 1); \ - } \ - \ - ptr = __tmp; \ -} - -#define AUTORELEASE_STR(str, iffail) \ -{ \ - gchar *__str; \ - Xstrdup_a(__str, str, iffail); \ - g_free(str); \ - str = __str; \ -} - -#define FILE_OP_ERROR(file, func) \ -{ \ - fprintf(stderr, "%s: ", file); \ - fflush(stderr); \ - perror(func); \ -} - -typedef void (*LogFunc) (const gchar *str); - -/* for macro expansion */ -#define Str(x) #x -#define Xstr(x) Str(x) - -void list_free_strings (GList *list); -void slist_free_strings (GSList *list); - -void hash_free_strings (GHashTable *table); -void hash_free_value_mem (GHashTable *table); - -gint str_case_equal (gconstpointer v, - gconstpointer v2); -guint str_case_hash (gconstpointer key); - -void ptr_array_free_strings (GPtrArray *array); - -typedef gboolean (*StrFindFunc) (const gchar *haystack, - const gchar *needle); - -gboolean str_find (const gchar *haystack, - const gchar *needle); -gboolean str_case_find (const gchar *haystack, - const gchar *needle); -gboolean str_find_equal (const gchar *haystack, - const gchar *needle); -gboolean str_case_find_equal (const gchar *haystack, - const gchar *needle); - -/* number-string conversion */ -gint to_number (const gchar *nstr); -gchar *itos_buf (gchar *nstr, - gint n); -gchar *itos (gint n); -gchar *to_human_readable (off_t size); - -/* alternative string functions */ -gint strcmp2 (const gchar *s1, - const gchar *s2); -gint path_cmp (const gchar *s1, - const gchar *s2); -gchar *strretchomp (gchar *str); -gchar *strtailchomp (gchar *str, - gchar tail_char); -gchar *strcrchomp (gchar *str); -gchar *strcasestr (const gchar *haystack, - const gchar *needle); -gpointer my_memmem (gconstpointer haystack, - size_t haystacklen, - gconstpointer needle, - size_t needlelen); -gchar *strncpy2 (gchar *dest, - const gchar *src, - size_t n); - -/* wide-character functions */ -#if !HAVE_ISWALNUM -int iswalnum (wint_t wc); -#endif -#if !HAVE_ISWSPACE -int iswspace (wint_t wc); -#endif -#if !HAVE_TOWLOWER -wint_t towlower (wint_t wc); -#endif - -#if !HAVE_WCSLEN -size_t wcslen (const wchar_t *s); -#endif -#if !HAVE_WCSCPY -wchar_t *wcscpy (wchar_t *dest, - const wchar_t *src); -#endif -#if !HAVE_WCSNCPY -wchar_t *wcsncpy (wchar_t *dest, - const wchar_t *src, - size_t n); -#endif - -wchar_t *wcsdup (const wchar_t *s); -wchar_t *wcsndup (const wchar_t *s, - size_t n); -wchar_t *strdup_mbstowcs (const gchar *s); -gchar *strdup_wcstombs (const wchar_t *s); -gint wcsncasecmp (const wchar_t *s1, - const wchar_t *s2, - size_t n); -wchar_t *wcscasestr (const wchar_t *haystack, - const wchar_t *needle); -gint get_mbs_len (const gchar *s); - -gboolean is_next_nonascii (const gchar *s); -gint get_next_word_len (const gchar *s); - -/* functions for string parsing */ -gint subject_compare (const gchar *s1, - const gchar *s2); -gint subject_compare_for_sort (const gchar *s1, - const gchar *s2); -void trim_subject_for_compare (gchar *str); -void trim_subject_for_sort (gchar *str); -void trim_subject (gchar *str); -void eliminate_parenthesis (gchar *str, - gchar op, - gchar cl); -void extract_parenthesis (gchar *str, - gchar op, - gchar cl); - -void extract_parenthesis_with_skip_quote (gchar *str, - gchar quote_chr, - gchar op, - gchar cl); - -void eliminate_quote (gchar *str, - gchar quote_chr); -void extract_quote (gchar *str, - gchar quote_chr); -void eliminate_address_comment (gchar *str); -gchar *strchr_with_skip_quote (const gchar *str, - gint quote_chr, - gint c); -gchar *strrchr_with_skip_quote (const gchar *str, - gint quote_chr, - gint c); -void extract_address (gchar *str); -void extract_list_id_str (gchar *str); - -GSList *address_list_append (GSList *addr_list, - const gchar *str); -GSList *references_list_prepend (GSList *msgid_list, - const gchar *str); -GSList *references_list_append (GSList *msgid_list, - const gchar *str); -GSList *newsgroup_list_append (GSList *group_list, - const gchar *str); - -GList *add_history (GList *list, - const gchar *str); - -void remove_return (gchar *str); -void remove_space (gchar *str); -void unfold_line (gchar *str); -void subst_char (gchar *str, - gchar orig, - gchar subst); -void subst_chars (gchar *str, - gchar *orig, - gchar subst); -void subst_null (gchar *str, - gint len, - gchar subst); -void subst_for_filename (gchar *str); -gboolean is_header_line (const gchar *str); -gboolean is_ascii_str (const gchar *str); -gint get_quote_level (const gchar *str); -gint check_line_length (const gchar *str, - gint max_chars, - gint *line); - -gchar *strstr_with_skip_quote (const gchar *haystack, - const gchar *needle); -gchar *strchr_parenthesis_close (const gchar *str, - gchar op, - gchar cl); - -gchar **strsplit_parenthesis (const gchar *str, - gchar op, - gchar cl, - gint max_tokens); -gchar **strsplit_with_quote (const gchar *str, - const gchar *delim, - gint max_tokens); - -gchar *get_abbrev_newsgroup_name (const gchar *group, - gint len); -gchar *trim_string (const gchar *str, - gint len); -gchar *trim_string_before (const gchar *str, - gint len); - -GList *uri_list_extract_filenames (const gchar *uri_list); -gboolean is_uri_string (const gchar *str); -gchar *get_uri_path (const gchar *uri); -gint get_uri_len (const gchar *str); -void decode_uri (gchar *decoded_uri, - const gchar *encoded_uri); -gchar *encode_uri (const gchar *filename); -gint scan_mailto_url (const gchar *mailto, - gchar **to, - gchar **cc, - gchar **bcc, - gchar **subject, - gchar **body); - -/* return static strings */ -const gchar *get_home_dir (void); -const gchar *get_rc_dir (void); -const gchar *get_old_rc_dir (void); -const gchar *get_mail_base_dir (void); -const gchar *get_news_cache_dir (void); -const gchar *get_imap_cache_dir (void); -const gchar *get_mime_tmp_dir (void); -const gchar *get_template_dir (void); -const gchar *get_tmp_dir (void); -gchar *get_tmp_file (void); -const gchar *get_domain_name (void); - -/* file / directory handling */ -off_t get_file_size (const gchar *file); -off_t get_file_size_as_crlf (const gchar *file); -off_t get_left_file_size (FILE *fp); - -gboolean file_exist (const gchar *file, - gboolean allow_fifo); -gboolean is_dir_exist (const gchar *dir); -gboolean is_file_entry_exist (const gchar *file); -gboolean dirent_is_regular_file (struct dirent *d); -gboolean dirent_is_directory (struct dirent *d); - -#define is_file_exist(file) file_exist(file, FALSE) -#define is_file_or_fifo_exist(file) file_exist(file, TRUE) - -gint change_dir (const gchar *dir); -gint make_dir (const gchar *dir); -gint make_dir_hier (const gchar *dir); -gint remove_all_files (const gchar *dir); -gint remove_numbered_files (const gchar *dir, - guint first, - guint last); -gint remove_all_numbered_files (const gchar *dir); -gint remove_expired_files (const gchar *dir, - guint hours); -gint remove_dir_recursive (const gchar *dir); -gint rename_force (const gchar *oldpath, - const gchar *newpath); -gint copy_file (const gchar *src, - const gchar *dest, - gboolean keep_backup); -gint copy_dir (const gchar *src, - const gchar *dest); -gint move_file (const gchar *src, - const gchar *dest, - gboolean overwrite); -gint copy_file_part (FILE *fp, - off_t offset, - size_t length, - const gchar *dest); - -gchar *canonicalize_str (const gchar *str); -gint canonicalize_file (const gchar *src, - const gchar *dest); -gint canonicalize_file_replace (const gchar *file); -gint uncanonicalize_file (const gchar *src, - const gchar *dest); -gint uncanonicalize_file_replace(const gchar *file); - -gchar *normalize_newlines (const gchar *str); - -gchar *get_outgoing_rfc2822_str (FILE *fp); -gchar *generate_mime_boundary (const gchar *prefix); - -gint change_file_mode_rw (FILE *fp, - const gchar *file); -FILE *my_tmpfile (void); -FILE *str_open_as_stream (const gchar *str); -gint str_write_to_file (const gchar *str, - const gchar *file); -gchar *file_read_to_str (const gchar *file); -gchar *file_read_stream_to_str (FILE *fp); - -/* process execution */ -gint execute_async (gchar *const argv[]); -gint execute_sync (gchar *const argv[]); -gint execute_command_line (const gchar *cmdline, - gboolean async); -gchar *get_command_output (const gchar *cmdline); - -/* open URI with external browser */ -gint open_uri(const gchar *uri, const gchar *cmdline); - -/* time functions */ -time_t remote_tzoffset_sec (const gchar *zone); -time_t tzoffset_sec (time_t *now); -gchar *tzoffset (time_t *now); -void get_rfc822_date (gchar *buf, - gint len); - -size_t my_strftime (gchar *s, - size_t max, - const gchar *format, - const struct tm *tm); - -/* logging */ -void set_log_file (const gchar *filename); -void close_log_file (void); -void set_log_verbosity (gboolean verbose); -gboolean get_debug_mode (void); -void set_debug_mode (gboolean enable); - -void set_log_ui_func (LogFunc print_func, - LogFunc message_func, - LogFunc warning_func, - LogFunc error_func); - -void set_log_show_status_func (LogFunc status_func); - -void debug_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); -void log_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); -void log_message (const gchar *format, ...) G_GNUC_PRINTF(1, 2); -void log_warning (const gchar *format, ...) G_GNUC_PRINTF(1, 2); -void log_error (const gchar *format, ...) G_GNUC_PRINTF(1, 2); - -#endif /* __UTILS_H__ */ diff --git a/src/uuencode.c b/src/uuencode.c deleted file mode 100644 index e0b2e79a..00000000 --- a/src/uuencode.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999,2000 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 - -#define UUDECODE(c) (c=='`' ? 0 : c - ' ') -#define N64(i) (i & ~63) - -const char uudigit[64] = -{ - '`', '!', '"', '#', '$', '%', '&', '\'', - '(', ')', '*', '+', ',', '-', '.', '/', - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', ':', ';', '<', '=', '>', '?', - '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', - 'X', 'Y', 'Z', '[', '\\', ']', '^', '_' -}; - -int touufrombits(unsigned char *out, const unsigned char *in, int inlen) -{ - int len; - - if (inlen > 45) return -1; - len = (inlen * 4 + 2) / 3 + 1; - *out++ = uudigit[inlen]; - - for (; inlen >= 3; inlen -= 3) { - *out++ = uudigit[in[0] >> 2]; - *out++ = uudigit[((in[0] << 4) & 0x30) | (in[1] >> 4)]; - *out++ = uudigit[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; - *out++ = uudigit[in[2] & 0x3f]; - in += 3; - } - - if (inlen > 0) { - *out++ = uudigit[(in[0] >> 2)]; - if (inlen == 1) { - *out++ = uudigit[((in[0] << 4) & 0x30)]; - } else { - *out++ = uudigit[(((in[0] << 4) & 0x30) | (in[1] >> 4))] ; - *out++ = uudigit[((in[1] << 2) & 0x3c)]; - } - } - *out = '\0'; - - return len; -} - -int fromuutobits(char *out, const char *in) -{ - int len, outlen, inlen; - register unsigned char digit1, digit2; - - outlen = UUDECODE(in[0]); - in += 1; - if(outlen < 0 || outlen > 45) - return -2; - if(outlen == 0) - return 0; - inlen = (outlen * 4 + 2) / 3; - len = 0; - - for( ; inlen>0; inlen-=4) { - digit1 = UUDECODE(in[0]); - if (N64(digit1)) return -1; - digit2 = UUDECODE(in[1]); - if (N64(digit2)) return -1; - out[len++] = (digit1 << 2) | (digit2 >> 4); - if (inlen > 2) { - digit1 = UUDECODE(in[2]); - if (N64(digit1)) return -1; - out[len++] = (digit2 << 4) | (digit1 >> 2); - if (inlen > 3) { - digit2 = UUDECODE(in[3]); - if (N64(digit2)) return -1; - out[len++] = (digit1 << 6) | digit2; - } - } - in += 4; - } - - return len == outlen ? len : -3; -} diff --git a/src/uuencode.h b/src/uuencode.h deleted file mode 100644 index 3658ebc6..00000000 --- a/src/uuencode.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999,2000 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -void touufrombits(unsigned char *, const unsigned char *, int); -int fromuutobits(char *, const char *); - -#define X_UUENCODE_END_LINE '`' -#define UUENCODE_END_LINE ' ' diff --git a/src/xml.c b/src/xml.c deleted file mode 100644 index e9c4adf8..00000000 --- a/src/xml.c +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2005 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include -#include -#include - -#include "xml.h" -#include "main.h" -#include "utils.h" -#include "codeconv.h" - -#define SPARSE_MEMORY -/* if this is defined all attr.names and tag.names are stored - * in a hash table */ -#if defined(SPARSE_MEMORY) -#include "stringtable.h" - -static StringTable *xml_string_table; - -static void xml_string_table_create(void) -{ - if (xml_string_table == NULL) - xml_string_table = string_table_new(); -} -#define XML_STRING_ADD(str) \ - string_table_insert_string(xml_string_table, (str)) -#define XML_STRING_FREE(str) \ - string_table_free_string(xml_string_table, (str)) - -#define XML_STRING_TABLE_CREATE() \ - xml_string_table_create() - -#else /* !SPARSE_MEMORY */ - -#define XML_STRING_ADD(str) \ - g_strdup(str) -#define XML_STRING_FREE(str) \ - g_free(str) - -#define XML_STRING_TABLE_CREATE() - -#endif /* SPARSE_MEMORY */ - -static void xml_free_tag (XMLTag *tag); -static gint xml_get_parenthesis (XMLFile *file, - gchar *buf, - gint len); - -XMLFile *xml_open_file(const gchar *path) -{ - XMLFile *newfile; - - g_return_val_if_fail(path != NULL, NULL); - - XML_STRING_TABLE_CREATE(); - - newfile = g_new(XMLFile, 1); - - newfile->fp = g_fopen(path, "rb"); - if (!newfile->fp) { - g_free(newfile); - return NULL; - } - - newfile->buf = g_string_new(NULL); - newfile->bufp = newfile->buf->str; - - newfile->dtd = NULL; - newfile->encoding = NULL; - newfile->tag_stack = NULL; - newfile->level = 0; - newfile->is_empty_element = FALSE; - - return newfile; -} - -void xml_close_file(XMLFile *file) -{ - g_return_if_fail(file != NULL); - - if (file->fp) fclose(file->fp); - - g_string_free(file->buf, TRUE); - - g_free(file->dtd); - g_free(file->encoding); - - while (file->tag_stack != NULL) - xml_pop_tag(file); - - g_free(file); -} - -static GNode *xml_build_tree(XMLFile *file, GNode *parent, guint level) -{ - GNode *node = NULL; - XMLNode *xmlnode; - XMLTag *tag; - - while (xml_parse_next_tag(file) == 0) { - if (file->level < level) break; - if (file->level == level) { - g_warning("xml_build_tree(): Parse error\n"); - break; - } - - tag = xml_get_current_tag(file); - if (!tag) break; - xmlnode = xml_node_new(xml_copy_tag(tag), NULL); - xmlnode->element = xml_get_element(file); - if (!parent) - node = g_node_new(xmlnode); - else - node = g_node_append_data(parent, xmlnode); - - xml_build_tree(file, node, file->level); - if (file->level == 0) break; - } - - return node; -} - -GNode *xml_parse_file(const gchar *path) -{ - XMLFile *file; - GNode *node; - - file = xml_open_file(path); - g_return_val_if_fail(file != NULL, NULL); - - xml_get_dtd(file); - - node = xml_build_tree(file, NULL, file->level); - - xml_close_file(file); - -#if defined(SPARSE_MEMORY) - if (get_debug_mode()) - string_table_get_stats(xml_string_table); -#endif - - return node; -} - -gint xml_get_dtd(XMLFile *file) -{ - gchar buf[XMLBUFSIZE]; - gchar *bufp = buf; - - if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) return -1; - - if ((*bufp++ == '?') && - (bufp = strcasestr(bufp, "xml")) && - (bufp = strcasestr(bufp + 3, "version")) && - (bufp = strchr(bufp + 7, '?'))) { - file->dtd = g_strdup(buf); - if ((bufp = strcasestr(buf, "encoding=\""))) { - bufp += 9; - extract_quote(bufp, '"'); - file->encoding = g_strdup(bufp); - } else - file->encoding = g_strdup(CS_INTERNAL); - } else { - g_warning("Can't get xml dtd\n"); - return -1; - } - - return 0; -} - -gint xml_parse_next_tag(XMLFile *file) -{ - gchar buf[XMLBUFSIZE]; - gchar *bufp = buf; - gchar *tag_str; - XMLTag *tag; - gint len; - - if (file->is_empty_element == TRUE) { - file->is_empty_element = FALSE; - xml_pop_tag(file); - return 0; - } - - if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) { - g_warning("xml_parse_next_tag(): Can't parse next tag\n"); - return -1; - } - - /* end-tag */ - if (buf[0] == '/') { - if (strcmp(xml_get_current_tag(file)->tag, buf + 1) != 0) { - g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf); - return -1; - } - xml_pop_tag(file); - return 0; - } - - tag = xml_tag_new(NULL); - xml_push_tag(file, tag); - - len = strlen(buf); - if (len > 0 && buf[len - 1] == '/') { - file->is_empty_element = TRUE; - buf[len - 1] = '\0'; - g_strchomp(buf); - } - if (strlen(buf) == 0) { - g_warning("xml_parse_next_tag(): Tag name is empty\n"); - return -1; - } - - while (*bufp != '\0' && !g_ascii_isspace(*bufp)) bufp++; - if (*bufp == '\0') { - tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); - if (tag_str) { - tag->tag = XML_STRING_ADD(tag_str); - g_free(tag_str); - } else - tag->tag = XML_STRING_ADD(buf); - return 0; - } else { - *bufp++ = '\0'; - tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); - if (tag_str) { - tag->tag = XML_STRING_ADD(tag_str); - g_free(tag_str); - } else - tag->tag = XML_STRING_ADD(buf); - } - - /* parse attributes ( name=value ) */ - while (*bufp) { - XMLAttr *attr; - gchar *attr_name; - gchar *attr_value; - gchar *utf8_attr_name; - gchar *utf8_attr_value; - gchar *p; - gchar quote; - - while (g_ascii_isspace(*bufp)) bufp++; - attr_name = bufp; - if ((p = strchr(attr_name, '=')) == NULL) { - g_warning("xml_parse_next_tag(): Syntax error in tag\n"); - return -1; - } - bufp = p; - *bufp++ = '\0'; - while (g_ascii_isspace(*bufp)) bufp++; - - if (*bufp != '"' && *bufp != '\'') { - g_warning("xml_parse_next_tag(): Syntax error in tag\n"); - return -1; - } - quote = *bufp; - bufp++; - attr_value = bufp; - if ((p = strchr(attr_value, quote)) == NULL) { - g_warning("xml_parse_next_tag(): Syntax error in tag\n"); - return -1; - } - bufp = p; - *bufp++ = '\0'; - - g_strchomp(attr_name); - xml_unescape_str(attr_value); - utf8_attr_name = conv_codeset_strdup - (attr_name, file->encoding, CS_INTERNAL); - utf8_attr_value = conv_codeset_strdup - (attr_value, file->encoding, CS_INTERNAL); - if (!utf8_attr_name) - utf8_attr_name = g_strdup(attr_name); - if (!utf8_attr_value) - utf8_attr_value = g_strdup(attr_value); - - attr = xml_attr_new(utf8_attr_name, utf8_attr_value); - xml_tag_add_attr(tag, attr); - - g_free(utf8_attr_value); - g_free(utf8_attr_name); - } - - return 0; -} - -void xml_push_tag(XMLFile *file, XMLTag *tag) -{ - g_return_if_fail(tag != NULL); - - file->tag_stack = g_list_prepend(file->tag_stack, tag); - file->level++; -} - -void xml_pop_tag(XMLFile *file) -{ - XMLTag *tag; - - if (!file->tag_stack) return; - - tag = (XMLTag *)file->tag_stack->data; - - xml_free_tag(tag); - file->tag_stack = g_list_remove(file->tag_stack, tag); - file->level--; -} - -XMLTag *xml_get_current_tag(XMLFile *file) -{ - if (file->tag_stack) - return (XMLTag *)file->tag_stack->data; - else - return NULL; -} - -GList *xml_get_current_tag_attr(XMLFile *file) -{ - XMLTag *tag; - - tag = xml_get_current_tag(file); - if (!tag) return NULL; - - return tag->attr; -} - -gchar *xml_get_element(XMLFile *file) -{ - gchar *str; - gchar *new_str; - gchar *end; - - while ((end = strchr(file->bufp, '<')) == NULL) - if (xml_read_line(file) < 0) return NULL; - - if (end == file->bufp) - return NULL; - - str = g_strndup(file->bufp, end - file->bufp); - /* this is not XML1.0 strict */ - g_strstrip(str); - xml_unescape_str(str); - - file->bufp = end; - xml_truncate_buf(file); - - if (str[0] == '\0') { - g_free(str); - return NULL; - } - - new_str = conv_codeset_strdup(str, file->encoding, CS_INTERNAL); - if (!new_str) - new_str = g_strdup(str); - g_free(str); - - return new_str; -} - -gint xml_read_line(XMLFile *file) -{ - gchar buf[XMLBUFSIZE]; - gint index; - - if (fgets(buf, sizeof(buf), file->fp) == NULL) - return -1; - - index = file->bufp - file->buf->str; - - g_string_append(file->buf, buf); - - file->bufp = file->buf->str + index; - - return 0; -} - -void xml_truncate_buf(XMLFile *file) -{ - gint len; - - len = file->bufp - file->buf->str; - if (len > 0) { - g_string_erase(file->buf, 0, len); - file->bufp = file->buf->str; - } -} - -gboolean xml_compare_tag(XMLFile *file, const gchar *name) -{ - XMLTag *tag; - - tag = xml_get_current_tag(file); - - if (tag && strcmp(tag->tag, name) == 0) - return TRUE; - else - return FALSE; -} - -XMLNode *xml_node_new(XMLTag *tag, const gchar *text) -{ - XMLNode *node; - - node = g_new(XMLNode, 1); - node->tag = tag; - node->element = g_strdup(text); - - return node; -} - -XMLTag *xml_tag_new(const gchar *tag) -{ - XMLTag *new_tag; - - new_tag = g_new(XMLTag, 1); - if (tag) - new_tag->tag = XML_STRING_ADD(tag); - else - new_tag->tag = NULL; - new_tag->attr = NULL; - - return new_tag; -} - -XMLAttr *xml_attr_new(const gchar *name, const gchar *value) -{ - XMLAttr *new_attr; - - new_attr = g_new(XMLAttr, 1); - new_attr->name = XML_STRING_ADD(name); - new_attr->value = g_strdup(value); - - return new_attr; -} - -void xml_tag_add_attr(XMLTag *tag, XMLAttr *attr) -{ - tag->attr = g_list_append(tag->attr, attr); -} - -XMLTag *xml_copy_tag(XMLTag *tag) -{ - XMLTag *new_tag; - XMLAttr *attr; - GList *list; - - new_tag = xml_tag_new(tag->tag); - for (list = tag->attr; list != NULL; list = list->next) { - attr = xml_copy_attr((XMLAttr *)list->data); - xml_tag_add_attr(new_tag, attr); - } - - return new_tag; -} - -XMLAttr *xml_copy_attr(XMLAttr *attr) -{ - return xml_attr_new(attr->name, attr->value); -} - -gint xml_unescape_str(gchar *str) -{ - gchar *start; - gchar *end; - gchar *p = str; - gchar *esc_str; - gchar ch; - gint len; - - while ((start = strchr(p, '&')) != NULL) { - if ((end = strchr(start + 1, ';')) == NULL) { - g_warning("Unescaped `&' appeared\n"); - p = start + 1; - continue; - } - len = end - start + 1; - if (len < 3) { - p = end + 1; - continue; - } - - Xstrndup_a(esc_str, start, len, return -1); - if (!strcmp(esc_str, "<")) - ch = '<'; - else if (!strcmp(esc_str, ">")) - ch = '>'; - else if (!strcmp(esc_str, "&")) - ch = '&'; - else if (!strcmp(esc_str, "'")) - ch = '\''; - else if (!strcmp(esc_str, """)) - ch = '\"'; - else { - p = end + 1; - continue; - } - - *start = ch; - memmove(start + 1, end + 1, strlen(end + 1) + 1); - p = start + 1; - } - - return 0; -} - -gint xml_file_put_escape_str(FILE *fp, const gchar *str) -{ - const gchar *p; - - g_return_val_if_fail(fp != NULL, -1); - - if (!str) return 0; - - for (p = str; *p != '\0'; p++) { - switch (*p) { - case '<': - fputs("<", fp); - break; - case '>': - fputs(">", fp); - break; - case '&': - fputs("&", fp); - break; - case '\'': - fputs("'", fp); - break; - case '\"': - fputs(""", fp); - break; - default: - fputc(*p, fp); - } - } - - return 0; -} - -gint xml_file_put_xml_decl(FILE *fp) -{ - g_return_val_if_fail(fp != NULL, -1); - - fprintf(fp, "\n", CS_INTERNAL); - return 0; -} - -gint xml_file_put_node(FILE *fp, XMLNode *node) -{ - GList *cur; - - g_return_val_if_fail(fp != NULL, -1); - g_return_val_if_fail(node != NULL, -1); - - fprintf(fp, "<%s", node->tag->tag); - - for (cur = node->tag->attr; cur != NULL; cur = cur->next) { - XMLAttr *attr = (XMLAttr *)cur->data; - fprintf(fp, " %s=\"", attr->name); - xml_file_put_escape_str(fp, attr->value); - fputs("\"", fp); - } - - if (node->element) { - fputs(">", fp); - xml_file_put_escape_str(fp, node->element); - fprintf(fp, "\n", node->tag->tag); - } else { - fputs(" />\n", fp); - } - - return 0; -} - -void xml_free_node(XMLNode *node) -{ - if (!node) return; - - xml_free_tag(node->tag); - g_free(node->element); - g_free(node); -} - -static gboolean xml_free_func(GNode *node, gpointer data) -{ - XMLNode *xmlnode = node->data; - - xml_free_node(xmlnode); - return FALSE; -} - -void xml_free_tree(GNode *node) -{ - g_return_if_fail(node != NULL); - - g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_free_func, - NULL); - - g_node_destroy(node); -} - -static void xml_free_tag(XMLTag *tag) -{ - if (!tag) return; - - XML_STRING_FREE(tag->tag); - while (tag->attr != NULL) { - XMLAttr *attr = (XMLAttr *)tag->attr->data; - XML_STRING_FREE(attr->name); - g_free(attr->value); - g_free(attr); - tag->attr = g_list_remove(tag->attr, tag->attr->data); - } - g_free(tag); -} - -static gint xml_get_parenthesis(XMLFile *file, gchar *buf, gint len) -{ - gchar *start; - gchar *end; - - buf[0] = '\0'; - - while ((start = strchr(file->bufp, '<')) == NULL) - if (xml_read_line(file) < 0) return -1; - - start++; - file->bufp = start; - - while ((end = strchr(file->bufp, '>')) == NULL) - if (xml_read_line(file) < 0) return -1; - - strncpy2(buf, file->bufp, MIN(end - file->bufp + 1, len)); - g_strstrip(buf); - file->bufp = end + 1; - xml_truncate_buf(file); - - return 0; -} diff --git a/src/xml.h b/src/xml.h deleted file mode 100644 index b41449a4..00000000 --- a/src/xml.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2004 Hiroyuki Yamamoto - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __XML_H__ -#define __XML_H__ - -#include -#include - -#define XMLBUFSIZE 8192 - -typedef struct _XMLAttr XMLAttr; -typedef struct _XMLTag XMLTag; -typedef struct _XMLNode XMLNode; -typedef struct _XMLFile XMLFile; - -struct _XMLAttr -{ - gchar *name; - gchar *value; -}; - -struct _XMLTag -{ - gchar *tag; - GList *attr; -}; - -struct _XMLNode -{ - XMLTag *tag; - gchar *element; -}; - -struct _XMLFile -{ - FILE *fp; - - GString *buf; - gchar *bufp; - - gchar *dtd; - gchar *encoding; - - GList *tag_stack; - guint level; - - gboolean is_empty_element; -}; - -XMLFile *xml_open_file (const gchar *path); -void xml_close_file (XMLFile *file); -GNode *xml_parse_file (const gchar *path); - -gint xml_get_dtd (XMLFile *file); -gint xml_parse_next_tag (XMLFile *file); -void xml_push_tag (XMLFile *file, - XMLTag *tag); -void xml_pop_tag (XMLFile *file); - -XMLTag *xml_get_current_tag (XMLFile *file); -GList *xml_get_current_tag_attr(XMLFile *file); -gchar *xml_get_element (XMLFile *file); - -gint xml_read_line (XMLFile *file); -void xml_truncate_buf (XMLFile *file); -gboolean xml_compare_tag (XMLFile *file, - const gchar *name); - -XMLNode *xml_node_new (XMLTag *tag, - const gchar *text); -XMLTag *xml_tag_new (const gchar *tag); -XMLAttr *xml_attr_new (const gchar *name, - const gchar *value); -void xml_tag_add_attr (XMLTag *tag, - XMLAttr *attr); - -XMLTag *xml_copy_tag (XMLTag *tag); -XMLAttr *xml_copy_attr (XMLAttr *attr); - -gint xml_unescape_str (gchar *str); -gint xml_file_put_escape_str (FILE *fp, - const gchar *str); - -gint xml_file_put_xml_decl (FILE *fp); -gint xml_file_put_node (FILE *fp, - XMLNode *node); - -void xml_free_node (XMLNode *node); -void xml_free_tree (GNode *node); - -#endif /* __XML_H__ */ -- cgit v1.2.3