diff options
| author | Simeon Simeonov | 2018-02-26 11:23:00 +0100 |
|---|---|---|
| committer | Simeon Simeonov | 2018-02-26 11:23:00 +0100 |
| commit | 0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch) | |
| tree | af916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph | |
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph')
82 files changed, 45872 insertions, 0 deletions
diff --git a/libsylph/Makefile.am b/libsylph/Makefile.am new file mode 100644 index 0000000..8f2b76b --- /dev/null +++ b/libsylph/Makefile.am | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | |||
| 2 | AM_CPPFLAGS = \ | ||
| 3 | -DG_LOG_DOMAIN=\"LibSylph\" \ | ||
| 4 | -DSYSCONFDIR=\""$(sysconfdir)"\" \ | ||
| 5 | -DLOCALEDIR=\""$(localedir)"\" | ||
| 6 | |||
| 7 | INCLUDES = -I$(top_srcdir) $(GLIB_CFLAGS) $(LIBSYLPH_CFLAGS) | ||
| 8 | |||
| 9 | lib_LTLIBRARIES = libsylph-0.la | ||
| 10 | #noinst_LTLIBRARIES = libsylph.la | ||
| 11 | |||
| 12 | libsylph_0_la_SOURCES = \ | ||
| 13 | account.c \ | ||
| 14 | base64.c \ | ||
| 15 | codeconv.c \ | ||
| 16 | customheader.c \ | ||
| 17 | displayheader.c \ | ||
| 18 | filter.c \ | ||
| 19 | folder.c \ | ||
| 20 | html.c \ | ||
| 21 | imap.c \ | ||
| 22 | mbox.c \ | ||
| 23 | md5.c \ | ||
| 24 | md5_hmac.c \ | ||
| 25 | mh.c \ | ||
| 26 | news.c \ | ||
| 27 | nntp.c \ | ||
| 28 | pop.c \ | ||
| 29 | prefs.c \ | ||
| 30 | prefs_account.c \ | ||
| 31 | prefs_common.c \ | ||
| 32 | procheader.c \ | ||
| 33 | procmime.c \ | ||
| 34 | procmsg.c \ | ||
| 35 | quoted-printable.c \ | ||
| 36 | recv.c \ | ||
| 37 | session.c \ | ||
| 38 | smtp.c \ | ||
| 39 | socket.c \ | ||
| 40 | socks.c \ | ||
| 41 | ssl.c \ | ||
| 42 | ssl_hostname_validation.c \ | ||
| 43 | stringtable.c \ | ||
| 44 | sylmain.c \ | ||
| 45 | unmime.c \ | ||
| 46 | utils.c \ | ||
| 47 | uuencode.c \ | ||
| 48 | virtual.c \ | ||
| 49 | xml.c \ | ||
| 50 | syl-marshal.c | ||
| 51 | |||
| 52 | libsylph_0includedir=$(includedir)/sylpheed/sylph | ||
| 53 | libsylph_0include_HEADERS = \ | ||
| 54 | defs.h \ | ||
| 55 | enums.h \ | ||
| 56 | account.h \ | ||
| 57 | base64.h \ | ||
| 58 | codeconv.h \ | ||
| 59 | customheader.h \ | ||
| 60 | displayheader.h \ | ||
| 61 | filter.h \ | ||
| 62 | folder.h \ | ||
| 63 | html.h \ | ||
| 64 | imap.h \ | ||
| 65 | mbox.h \ | ||
| 66 | md5.h \ | ||
| 67 | md5_hmac.h \ | ||
| 68 | mh.h \ | ||
| 69 | news.h \ | ||
| 70 | nntp.h \ | ||
| 71 | pop.h \ | ||
| 72 | prefs.h \ | ||
| 73 | prefs_account.h \ | ||
| 74 | prefs_common.h \ | ||
| 75 | procheader.h \ | ||
| 76 | procmime.h \ | ||
| 77 | procmsg.h \ | ||
| 78 | quoted-printable.h \ | ||
| 79 | recv.h \ | ||
| 80 | session.h \ | ||
| 81 | smtp.h \ | ||
| 82 | socket.h \ | ||
| 83 | socks.h \ | ||
| 84 | ssl.h \ | ||
| 85 | ssl_hostname_validation.h \ | ||
| 86 | stringtable.h \ | ||
| 87 | sylmain.h \ | ||
| 88 | unmime.h \ | ||
| 89 | utils.h \ | ||
| 90 | uuencode.h \ | ||
| 91 | virtual.h \ | ||
| 92 | xml.h \ | ||
| 93 | syl-marshal.h | ||
| 94 | |||
| 95 | BUILT_SOURCES = \ | ||
| 96 | syl-marshal.c \ | ||
| 97 | syl-marshal.h | ||
| 98 | |||
| 99 | EXTRA_DIST = \ | ||
| 100 | syl-marshal.list \ | ||
| 101 | libsylph-0.def | ||
| 102 | |||
| 103 | if NATIVE_WIN32 | ||
| 104 | no_undefined = -no-undefined | ||
| 105 | export_symbols = -export-symbols libsylph-0.def | ||
| 106 | endif | ||
| 107 | |||
| 108 | libsylph_0_la_LDFLAGS = \ | ||
| 109 | -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ | ||
| 110 | -export-dynamic $(no_undefined) $(export_symbols) | ||
| 111 | |||
| 112 | libsylph_0_la_LIBADD = $(GLIB_LIBS) $(LIBICONV) $(LIBSYLPH_LIBS) | ||
| 113 | |||
| 114 | syl-marshal.h: syl-marshal.list | ||
| 115 | $(GLIB_GENMARSHAL) $< --header --prefix=syl_marshal > $@ | ||
| 116 | |||
| 117 | syl-marshal.c: syl-marshal.list | ||
| 118 | $(GLIB_GENMARSHAL) $< --body --prefix=syl_marshal > $@ | ||
diff --git a/libsylph/Makefile.in b/libsylph/Makefile.in new file mode 100644 index 0000000..6e3c999 --- /dev/null +++ b/libsylph/Makefile.in | |||
| @@ -0,0 +1,881 @@ | |||
| 1 | # Makefile.in generated by automake 1.14.1 from Makefile.am. | ||
| 2 | # @configure_input@ | ||
| 3 | |||
| 4 | # Copyright (C) 1994-2013 Free Software Foundation, Inc. | ||
| 5 | |||
| 6 | # This Makefile.in is free software; the Free Software Foundation | ||
| 7 | # gives unlimited permission to copy and/or distribute it, | ||
| 8 | # with or without modifications, as long as this notice is preserved. | ||
| 9 | |||
| 10 | # This program is distributed in the hope that it will be useful, | ||
| 11 | # but WITHOUT ANY WARRANTY, to the extent permitted by law; without | ||
| 12 | # even the implied warranty of MERCHANTABILITY or FITNESS FOR A | ||
| 13 | # PARTICULAR PURPOSE. | ||
| 14 | |||
| 15 | @SET_MAKE@ | ||
| 16 | |||
| 17 | |||
| 18 | VPATH = @srcdir@ | ||
| 19 | am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' | ||
| 20 | am__make_running_with_option = \ | ||
| 21 | case $${target_option-} in \ | ||
| 22 | ?) ;; \ | ||
| 23 | *) echo "am__make_running_with_option: internal error: invalid" \ | ||
| 24 | "target option '$${target_option-}' specified" >&2; \ | ||
| 25 | exit 1;; \ | ||
| 26 | esac; \ | ||
| 27 | has_opt=no; \ | ||
| 28 | sane_makeflags=$$MAKEFLAGS; \ | ||
| 29 | if $(am__is_gnu_make); then \ | ||
| 30 | sane_makeflags=$$MFLAGS; \ | ||
| 31 | else \ | ||
| 32 | case $$MAKEFLAGS in \ | ||
| 33 | *\\[\ \ ]*) \ | ||
| 34 | bs=\\; \ | ||
| 35 | sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | ||
| 36 | | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ | ||
| 37 | esac; \ | ||
| 38 | fi; \ | ||
| 39 | skip_next=no; \ | ||
| 40 | strip_trailopt () \ | ||
| 41 | { \ | ||
| 42 | flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ | ||
| 43 | }; \ | ||
| 44 | for flg in $$sane_makeflags; do \ | ||
| 45 | test $$skip_next = yes && { skip_next=no; continue; }; \ | ||
| 46 | case $$flg in \ | ||
| 47 | *=*|--*) continue;; \ | ||
| 48 | -*I) strip_trailopt 'I'; skip_next=yes;; \ | ||
| 49 | -*I?*) strip_trailopt 'I';; \ | ||
| 50 | -*O) strip_trailopt 'O'; skip_next=yes;; \ | ||
| 51 | -*O?*) strip_trailopt 'O';; \ | ||
| 52 | -*l) strip_trailopt 'l'; skip_next=yes;; \ | ||
| 53 | -*l?*) strip_trailopt 'l';; \ | ||
| 54 | -[dEDm]) skip_next=yes;; \ | ||
| 55 | -[JT]) skip_next=yes;; \ | ||
| 56 | esac; \ | ||
| 57 | case $$flg in \ | ||
| 58 | *$$target_option*) has_opt=yes; break;; \ | ||
| 59 | esac; \ | ||
| 60 | done; \ | ||
| 61 | test $$has_opt = yes | ||
| 62 | am__make_dryrun = (target_option=n; $(am__make_running_with_option)) | ||
| 63 | am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) | ||
| 64 | pkgdatadir = $(datadir)/@PACKAGE@ | ||
| 65 | pkgincludedir = $(includedir)/@PACKAGE@ | ||
| 66 | pkglibdir = $(libdir)/@PACKAGE@ | ||
| 67 | pkglibexecdir = $(libexecdir)/@PACKAGE@ | ||
| 68 | am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd | ||
| 69 | install_sh_DATA = $(install_sh) -c -m 644 | ||
| 70 | install_sh_PROGRAM = $(install_sh) -c | ||
| 71 | install_sh_SCRIPT = $(install_sh) -c | ||
| 72 | INSTALL_HEADER = $(INSTALL_DATA) | ||
| 73 | transform = $(program_transform_name) | ||
| 74 | NORMAL_INSTALL = : | ||
| 75 | PRE_INSTALL = : | ||
| 76 | POST_INSTALL = : | ||
| 77 | NORMAL_UNINSTALL = : | ||
| 78 | PRE_UNINSTALL = : | ||
| 79 | POST_UNINSTALL = : | ||
| 80 | build_triplet = @build@ | ||
| 81 | host_triplet = @host@ | ||
| 82 | target_triplet = @target@ | ||
| 83 | subdir = libsylph | ||
| 84 | DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ | ||
| 85 | $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp \ | ||
| 86 | $(libsylph_0include_HEADERS) | ||
| 87 | ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 | ||
| 88 | am__aclocal_m4_deps = $(top_srcdir)/ac/aclocal-include.m4 \ | ||
| 89 | $(top_srcdir)/ac/gnupg-check-typedef.m4 \ | ||
| 90 | $(top_srcdir)/configure.ac | ||
| 91 | am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ | ||
| 92 | $(ACLOCAL_M4) | ||
| 93 | mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs | ||
| 94 | CONFIG_HEADER = $(top_builddir)/config.h | ||
| 95 | CONFIG_CLEAN_FILES = | ||
| 96 | CONFIG_CLEAN_VPATH_FILES = | ||
| 97 | am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; | ||
| 98 | am__vpath_adj = case $$p in \ | ||
| 99 | $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ | ||
| 100 | *) f=$$p;; \ | ||
| 101 | esac; | ||
| 102 | am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; | ||
| 103 | am__install_max = 40 | ||
| 104 | am__nobase_strip_setup = \ | ||
| 105 | srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` | ||
| 106 | am__nobase_strip = \ | ||
| 107 | for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" | ||
| 108 | am__nobase_list = $(am__nobase_strip_setup); \ | ||
| 109 | for p in $$list; do echo "$$p $$p"; done | \ | ||
| 110 | sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ | ||
| 111 | $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ | ||
| 112 | if (++n[$$2] == $(am__install_max)) \ | ||
| 113 | { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ | ||
| 114 | END { for (dir in files) print dir, files[dir] }' | ||
| 115 | am__base_list = \ | ||
| 116 | sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ | ||
| 117 | sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | ||
| 118 | am__uninstall_files_from_dir = { \ | ||
| 119 | test -z "$$files" \ | ||
| 120 | || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ | ||
| 121 | || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ | ||
| 122 | $(am__cd) "$$dir" && rm -f $$files; }; \ | ||
| 123 | } | ||
| 124 | am__installdirs = "$(DESTDIR)$(libdir)" \ | ||
| 125 | "$(DESTDIR)$(libsylph_0includedir)" | ||
| 126 | LTLIBRARIES = $(lib_LTLIBRARIES) | ||
| 127 | am__DEPENDENCIES_1 = | ||
| 128 | libsylph_0_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ | ||
| 129 | $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) | ||
| 130 | am_libsylph_0_la_OBJECTS = account.lo base64.lo codeconv.lo \ | ||
| 131 | customheader.lo displayheader.lo filter.lo folder.lo html.lo \ | ||
| 132 | imap.lo mbox.lo md5.lo md5_hmac.lo mh.lo news.lo nntp.lo \ | ||
| 133 | pop.lo prefs.lo prefs_account.lo prefs_common.lo procheader.lo \ | ||
| 134 | procmime.lo procmsg.lo quoted-printable.lo recv.lo session.lo \ | ||
| 135 | smtp.lo socket.lo socks.lo ssl.lo ssl_hostname_validation.lo \ | ||
| 136 | stringtable.lo sylmain.lo unmime.lo utils.lo uuencode.lo \ | ||
| 137 | virtual.lo xml.lo syl-marshal.lo | ||
| 138 | libsylph_0_la_OBJECTS = $(am_libsylph_0_la_OBJECTS) | ||
| 139 | AM_V_lt = $(am__v_lt_@AM_V@) | ||
| 140 | am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) | ||
| 141 | am__v_lt_0 = --silent | ||
| 142 | am__v_lt_1 = | ||
| 143 | libsylph_0_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ | ||
| 144 | $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ | ||
| 145 | $(libsylph_0_la_LDFLAGS) $(LDFLAGS) -o $@ | ||
| 146 | AM_V_P = $(am__v_P_@AM_V@) | ||
| 147 | am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) | ||
| 148 | am__v_P_0 = false | ||
| 149 | am__v_P_1 = : | ||
| 150 | AM_V_GEN = $(am__v_GEN_@AM_V@) | ||
| 151 | am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) | ||
| 152 | am__v_GEN_0 = @echo " GEN " $@; | ||
| 153 | am__v_GEN_1 = | ||
| 154 | AM_V_at = $(am__v_at_@AM_V@) | ||
| 155 | am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) | ||
| 156 | am__v_at_0 = @ | ||
| 157 | am__v_at_1 = | ||
| 158 | DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) | ||
| 159 | depcomp = $(SHELL) $(top_srcdir)/depcomp | ||
| 160 | am__depfiles_maybe = depfiles | ||
| 161 | am__mv = mv -f | ||
| 162 | COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ | ||
| 163 | $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) | ||
| 164 | LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ | ||
| 165 | $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ | ||
| 166 | $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ | ||
| 167 | $(AM_CFLAGS) $(CFLAGS) | ||
| 168 | AM_V_CC = $(am__v_CC_@AM_V@) | ||
| 169 | am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) | ||
| 170 | am__v_CC_0 = @echo " CC " $@; | ||
| 171 | am__v_CC_1 = | ||
| 172 | CCLD = $(CC) | ||
| 173 | LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ | ||
| 174 | $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ | ||
| 175 | $(AM_LDFLAGS) $(LDFLAGS) -o $@ | ||
| 176 | AM_V_CCLD = $(am__v_CCLD_@AM_V@) | ||
| 177 | am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) | ||
| 178 | am__v_CCLD_0 = @echo " CCLD " $@; | ||
| 179 | am__v_CCLD_1 = | ||
| 180 | SOURCES = $(libsylph_0_la_SOURCES) | ||
| 181 | DIST_SOURCES = $(libsylph_0_la_SOURCES) | ||
| 182 | am__can_run_installinfo = \ | ||
| 183 | case $$AM_UPDATE_INFO_DIR in \ | ||
| 184 | n|no|NO) false;; \ | ||
| 185 | *) (install-info --version) >/dev/null 2>&1;; \ | ||
| 186 | esac | ||
| 187 | HEADERS = $(libsylph_0include_HEADERS) | ||
| 188 | am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) | ||
| 189 | # Read a list of newline-separated strings from the standard input, | ||
| 190 | # and print each of them once, without duplicates. Input order is | ||
| 191 | # *not* preserved. | ||
| 192 | am__uniquify_input = $(AWK) '\ | ||
| 193 | BEGIN { nonempty = 0; } \ | ||
| 194 | { items[$$0] = 1; nonempty = 1; } \ | ||
| 195 | END { if (nonempty) { for (i in items) print i; }; } \ | ||
| 196 | ' | ||
| 197 | # Make sure the list of sources is unique. This is necessary because, | ||
| 198 | # e.g., the same source file might be shared among _SOURCES variables | ||
| 199 | # for different programs/libraries. | ||
| 200 | am__define_uniq_tagged_files = \ | ||
| 201 | list='$(am__tagged_files)'; \ | ||
| 202 | unique=`for i in $$list; do \ | ||
| 203 | if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ | ||
| 204 | done | $(am__uniquify_input)` | ||
| 205 | ETAGS = etags | ||
| 206 | CTAGS = ctags | ||
| 207 | DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) | ||
| 208 | ACLOCAL = @ACLOCAL@ | ||
| 209 | ALLOCA = @ALLOCA@ | ||
| 210 | AMTAR = @AMTAR@ | ||
| 211 | AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ | ||
| 212 | AR = @AR@ | ||
| 213 | AUTOCONF = @AUTOCONF@ | ||
| 214 | AUTOHEADER = @AUTOHEADER@ | ||
| 215 | AUTOMAKE = @AUTOMAKE@ | ||
| 216 | AWK = @AWK@ | ||
| 217 | BUILD_REVISION = @BUILD_REVISION@ | ||
| 218 | CATALOGS = @CATALOGS@ | ||
| 219 | CATOBJEXT = @CATOBJEXT@ | ||
| 220 | CC = @CC@ | ||
| 221 | CCDEPMODE = @CCDEPMODE@ | ||
| 222 | CFLAGS = @CFLAGS@ | ||
| 223 | CPP = @CPP@ | ||
| 224 | CPPFLAGS = @CPPFLAGS@ | ||
| 225 | CYGPATH_W = @CYGPATH_W@ | ||
| 226 | DATADIRNAME = @DATADIRNAME@ | ||
| 227 | DEFS = @DEFS@ | ||
| 228 | DEPDIR = @DEPDIR@ | ||
| 229 | DEVEL_VERSION = @DEVEL_VERSION@ | ||
| 230 | DLLTOOL = @DLLTOOL@ | ||
| 231 | DSYMUTIL = @DSYMUTIL@ | ||
| 232 | DUMPBIN = @DUMPBIN@ | ||
| 233 | ECHO_C = @ECHO_C@ | ||
| 234 | ECHO_N = @ECHO_N@ | ||
| 235 | ECHO_T = @ECHO_T@ | ||
| 236 | EGREP = @EGREP@ | ||
| 237 | EXEEXT = @EXEEXT@ | ||
| 238 | EXTRA_VERSION = @EXTRA_VERSION@ | ||
| 239 | FGREP = @FGREP@ | ||
| 240 | GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ | ||
| 241 | GLIB_CFLAGS = @GLIB_CFLAGS@ | ||
| 242 | GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@ | ||
| 243 | GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ | ||
| 244 | GLIB_LIBS = @GLIB_LIBS@ | ||
| 245 | GLIB_MKENUMS = @GLIB_MKENUMS@ | ||
| 246 | GMOFILES = @GMOFILES@ | ||
| 247 | GMSGFMT = @GMSGFMT@ | ||
| 248 | GOBJECT_QUERY = @GOBJECT_QUERY@ | ||
| 249 | GPGME_CFLAGS = @GPGME_CFLAGS@ | ||
| 250 | GPGME_CONFIG = @GPGME_CONFIG@ | ||
| 251 | GPGME_LIBS = @GPGME_LIBS@ | ||
| 252 | GREP = @GREP@ | ||
| 253 | GTK_CFLAGS = @GTK_CFLAGS@ | ||
| 254 | GTK_LIBS = @GTK_LIBS@ | ||
| 255 | INSTALL = @INSTALL@ | ||
| 256 | INSTALL_DATA = @INSTALL_DATA@ | ||
| 257 | INSTALL_PROGRAM = @INSTALL_PROGRAM@ | ||
| 258 | INSTALL_SCRIPT = @INSTALL_SCRIPT@ | ||
| 259 | INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ | ||
| 260 | INSTOBJEXT = @INSTOBJEXT@ | ||
| 261 | INTLLIBS = @INTLLIBS@ | ||
| 262 | LD = @LD@ | ||
| 263 | LDAP_LIBS = @LDAP_LIBS@ | ||
| 264 | LDFLAGS = @LDFLAGS@ | ||
| 265 | LEX = @LEX@ | ||
| 266 | LEXLIB = @LEXLIB@ | ||
| 267 | LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ | ||
| 268 | LIBICONV = @LIBICONV@ | ||
| 269 | LIBOBJS = @LIBOBJS@ | ||
| 270 | LIBS = @LIBS@ | ||
| 271 | LIBSYLPH_CFLAGS = @LIBSYLPH_CFLAGS@ | ||
| 272 | LIBSYLPH_LIBS = @LIBSYLPH_LIBS@ | ||
| 273 | LIBTOOL = @LIBTOOL@ | ||
| 274 | LIPO = @LIPO@ | ||
| 275 | LN_S = @LN_S@ | ||
| 276 | LTLIBICONV = @LTLIBICONV@ | ||
| 277 | LTLIBOBJS = @LTLIBOBJS@ | ||
| 278 | LT_AGE = @LT_AGE@ | ||
| 279 | LT_CURRENT = @LT_CURRENT@ | ||
| 280 | LT_RELEASE = @LT_RELEASE@ | ||
| 281 | LT_REVISION = @LT_REVISION@ | ||
| 282 | MAINT = @MAINT@ | ||
| 283 | MAJOR_VERSION = @MAJOR_VERSION@ | ||
| 284 | MAKEINFO = @MAKEINFO@ | ||
| 285 | MANIFEST_TOOL = @MANIFEST_TOOL@ | ||
| 286 | MICRO_VERSION = @MICRO_VERSION@ | ||
| 287 | MINOR_VERSION = @MINOR_VERSION@ | ||
| 288 | MKDIR_P = @MKDIR_P@ | ||
| 289 | MKINSTALLDIRS = @MKINSTALLDIRS@ | ||
| 290 | MSGFMT = @MSGFMT@ | ||
| 291 | MSGFMT_OPTS = @MSGFMT_OPTS@ | ||
| 292 | NM = @NM@ | ||
| 293 | NMEDIT = @NMEDIT@ | ||
| 294 | OBJDUMP = @OBJDUMP@ | ||
| 295 | OBJEXT = @OBJEXT@ | ||
| 296 | OPENSSL_CFLAGS = @OPENSSL_CFLAGS@ | ||
| 297 | OPENSSL_LIBS = @OPENSSL_LIBS@ | ||
| 298 | OTOOL = @OTOOL@ | ||
| 299 | OTOOL64 = @OTOOL64@ | ||
| 300 | PACKAGE = @PACKAGE@ | ||
| 301 | PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ | ||
| 302 | PACKAGE_NAME = @PACKAGE_NAME@ | ||
| 303 | PACKAGE_STRING = @PACKAGE_STRING@ | ||
| 304 | PACKAGE_TARNAME = @PACKAGE_TARNAME@ | ||
| 305 | PACKAGE_URL = @PACKAGE_URL@ | ||
| 306 | PACKAGE_VERSION = @PACKAGE_VERSION@ | ||
| 307 | PATH_SEPARATOR = @PATH_SEPARATOR@ | ||
| 308 | PKG_CONFIG = @PKG_CONFIG@ | ||
| 309 | PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ | ||
| 310 | PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ | ||
| 311 | POFILES = @POFILES@ | ||
| 312 | POSUB = @POSUB@ | ||
| 313 | PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@ | ||
| 314 | PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@ | ||
| 315 | RANLIB = @RANLIB@ | ||
| 316 | SED = @SED@ | ||
| 317 | SET_MAKE = @SET_MAKE@ | ||
| 318 | SHELL = @SHELL@ | ||
| 319 | STRIP = @STRIP@ | ||
| 320 | SYLPHEED_LIBS = @SYLPHEED_LIBS@ | ||
| 321 | USE_NLS = @USE_NLS@ | ||
| 322 | VERSION = @VERSION@ | ||
| 323 | WINDRES = @WINDRES@ | ||
| 324 | XGETTEXT = @XGETTEXT@ | ||
| 325 | YACC = @YACC@ | ||
| 326 | YFLAGS = @YFLAGS@ | ||
| 327 | abs_builddir = @abs_builddir@ | ||
| 328 | abs_srcdir = @abs_srcdir@ | ||
| 329 | abs_top_builddir = @abs_top_builddir@ | ||
| 330 | abs_top_srcdir = @abs_top_srcdir@ | ||
| 331 | ac_ct_AR = @ac_ct_AR@ | ||
| 332 | ac_ct_CC = @ac_ct_CC@ | ||
| 333 | ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ | ||
| 334 | am__include = @am__include@ | ||
| 335 | am__leading_dot = @am__leading_dot@ | ||
| 336 | am__quote = @am__quote@ | ||
| 337 | am__tar = @am__tar@ | ||
| 338 | am__untar = @am__untar@ | ||
| 339 | bindir = @bindir@ | ||
| 340 | build = @build@ | ||
| 341 | build_alias = @build_alias@ | ||
| 342 | build_cpu = @build_cpu@ | ||
| 343 | build_os = @build_os@ | ||
| 344 | build_vendor = @build_vendor@ | ||
| 345 | builddir = @builddir@ | ||
| 346 | datadir = @datadir@ | ||
| 347 | datarootdir = @datarootdir@ | ||
| 348 | docdir = @docdir@ | ||
| 349 | dvidir = @dvidir@ | ||
| 350 | exec_prefix = @exec_prefix@ | ||
| 351 | faqdir = @faqdir@ | ||
| 352 | host = @host@ | ||
| 353 | host_alias = @host_alias@ | ||
| 354 | host_cpu = @host_cpu@ | ||
| 355 | host_os = @host_os@ | ||
| 356 | host_vendor = @host_vendor@ | ||
| 357 | htmldir = @htmldir@ | ||
| 358 | includedir = @includedir@ | ||
| 359 | infodir = @infodir@ | ||
| 360 | install_sh = @install_sh@ | ||
| 361 | libdir = @libdir@ | ||
| 362 | libexecdir = @libexecdir@ | ||
| 363 | localedir = @localedir@ | ||
| 364 | localstatedir = @localstatedir@ | ||
| 365 | mandir = @mandir@ | ||
| 366 | manualdir = @manualdir@ | ||
| 367 | mkdir_p = @mkdir_p@ | ||
| 368 | oldincludedir = @oldincludedir@ | ||
| 369 | pdfdir = @pdfdir@ | ||
| 370 | plugindir = @plugindir@ | ||
| 371 | prefix = @prefix@ | ||
| 372 | program_transform_name = @program_transform_name@ | ||
| 373 | psdir = @psdir@ | ||
| 374 | sbindir = @sbindir@ | ||
| 375 | sharedstatedir = @sharedstatedir@ | ||
| 376 | srcdir = @srcdir@ | ||
| 377 | sysconfdir = @sysconfdir@ | ||
| 378 | target = @target@ | ||
| 379 | target_alias = @target_alias@ | ||
| 380 | target_cpu = @target_cpu@ | ||
| 381 | target_os = @target_os@ | ||
| 382 | target_vendor = @target_vendor@ | ||
| 383 | themedir = @themedir@ | ||
| 384 | top_build_prefix = @top_build_prefix@ | ||
| 385 | top_builddir = @top_builddir@ | ||
| 386 | top_srcdir = @top_srcdir@ | ||
| 387 | AM_CPPFLAGS = \ | ||
| 388 | -DG_LOG_DOMAIN=\"LibSylph\" \ | ||
| 389 | -DSYSCONFDIR=\""$(sysconfdir)"\" \ | ||
| 390 | -DLOCALEDIR=\""$(localedir)"\" | ||
| 391 | |||
| 392 | INCLUDES = -I$(top_srcdir) $(GLIB_CFLAGS) $(LIBSYLPH_CFLAGS) | ||
| 393 | lib_LTLIBRARIES = libsylph-0.la | ||
| 394 | #noinst_LTLIBRARIES = libsylph.la | ||
| 395 | libsylph_0_la_SOURCES = \ | ||
| 396 | account.c \ | ||
| 397 | base64.c \ | ||
| 398 | codeconv.c \ | ||
| 399 | customheader.c \ | ||
| 400 | displayheader.c \ | ||
| 401 | filter.c \ | ||
| 402 | folder.c \ | ||
| 403 | html.c \ | ||
| 404 | imap.c \ | ||
| 405 | mbox.c \ | ||
| 406 | md5.c \ | ||
| 407 | md5_hmac.c \ | ||
| 408 | mh.c \ | ||
| 409 | news.c \ | ||
| 410 | nntp.c \ | ||
| 411 | pop.c \ | ||
| 412 | prefs.c \ | ||
| 413 | prefs_account.c \ | ||
| 414 | prefs_common.c \ | ||
| 415 | procheader.c \ | ||
| 416 | procmime.c \ | ||
| 417 | procmsg.c \ | ||
| 418 | quoted-printable.c \ | ||
| 419 | recv.c \ | ||
| 420 | session.c \ | ||
| 421 | smtp.c \ | ||
| 422 | socket.c \ | ||
| 423 | socks.c \ | ||
| 424 | ssl.c \ | ||
| 425 | ssl_hostname_validation.c \ | ||
| 426 | stringtable.c \ | ||
| 427 | sylmain.c \ | ||
| 428 | unmime.c \ | ||
| 429 | utils.c \ | ||
| 430 | uuencode.c \ | ||
| 431 | virtual.c \ | ||
| 432 | xml.c \ | ||
| 433 | syl-marshal.c | ||
| 434 | |||
| 435 | libsylph_0includedir = $(includedir)/sylpheed/sylph | ||
| 436 | libsylph_0include_HEADERS = \ | ||
| 437 | defs.h \ | ||
| 438 | enums.h \ | ||
| 439 | account.h \ | ||
| 440 | base64.h \ | ||
| 441 | codeconv.h \ | ||
| 442 | customheader.h \ | ||
| 443 | displayheader.h \ | ||
| 444 | filter.h \ | ||
| 445 | folder.h \ | ||
| 446 | html.h \ | ||
| 447 | imap.h \ | ||
| 448 | mbox.h \ | ||
| 449 | md5.h \ | ||
| 450 | md5_hmac.h \ | ||
| 451 | mh.h \ | ||
| 452 | news.h \ | ||
| 453 | nntp.h \ | ||
| 454 | pop.h \ | ||
| 455 | prefs.h \ | ||
| 456 | prefs_account.h \ | ||
| 457 | prefs_common.h \ | ||
| 458 | procheader.h \ | ||
| 459 | procmime.h \ | ||
| 460 | procmsg.h \ | ||
| 461 | quoted-printable.h \ | ||
| 462 | recv.h \ | ||
| 463 | session.h \ | ||
| 464 | smtp.h \ | ||
| 465 | socket.h \ | ||
| 466 | socks.h \ | ||
| 467 | ssl.h \ | ||
| 468 | ssl_hostname_validation.h \ | ||
| 469 | stringtable.h \ | ||
| 470 | sylmain.h \ | ||
| 471 | unmime.h \ | ||
| 472 | utils.h \ | ||
| 473 | uuencode.h \ | ||
| 474 | virtual.h \ | ||
| 475 | xml.h \ | ||
| 476 | syl-marshal.h | ||
| 477 | |||
| 478 | BUILT_SOURCES = \ | ||
| 479 | syl-marshal.c \ | ||
| 480 | syl-marshal.h | ||
| 481 | |||
| 482 | EXTRA_DIST = \ | ||
| 483 | syl-marshal.list \ | ||
| 484 | libsylph-0.def | ||
| 485 | |||
| 486 | @NATIVE_WIN32_TRUE@no_undefined = -no-undefined | ||
| 487 | @NATIVE_WIN32_TRUE@export_symbols = -export-symbols libsylph-0.def | ||
| 488 | libsylph_0_la_LDFLAGS = \ | ||
| 489 | -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ | ||
| 490 | -export-dynamic $(no_undefined) $(export_symbols) | ||
| 491 | |||
| 492 | libsylph_0_la_LIBADD = $(GLIB_LIBS) $(LIBICONV) $(LIBSYLPH_LIBS) | ||
| 493 | all: $(BUILT_SOURCES) | ||
| 494 | $(MAKE) $(AM_MAKEFLAGS) all-am | ||
| 495 | |||
| 496 | .SUFFIXES: | ||
| 497 | .SUFFIXES: .c .lo .o .obj | ||
| 498 | $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) | ||
| 499 | @for dep in $?; do \ | ||
| 500 | case '$(am__configure_deps)' in \ | ||
| 501 | *$$dep*) \ | ||
| 502 | ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ | ||
| 503 | && { if test -f $@; then exit 0; else break; fi; }; \ | ||
| 504 | exit 1;; \ | ||
| 505 | esac; \ | ||
| 506 | done; \ | ||
| 507 | echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign libsylph/Makefile'; \ | ||
| 508 | $(am__cd) $(top_srcdir) && \ | ||
| 509 | $(AUTOMAKE) --foreign libsylph/Makefile | ||
| 510 | .PRECIOUS: Makefile | ||
| 511 | Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status | ||
| 512 | @case '$?' in \ | ||
| 513 | *config.status*) \ | ||
| 514 | cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ | ||
| 515 | *) \ | ||
| 516 | echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ | ||
| 517 | cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ | ||
| 518 | esac; | ||
| 519 | |||
| 520 | $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) | ||
| 521 | cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh | ||
| 522 | |||
| 523 | $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) | ||
| 524 | cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh | ||
| 525 | $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) | ||
| 526 | cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh | ||
| 527 | $(am__aclocal_m4_deps): | ||
| 528 | |||
| 529 | install-libLTLIBRARIES: $(lib_LTLIBRARIES) | ||
| 530 | @$(NORMAL_INSTALL) | ||
| 531 | @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ | ||
| 532 | list2=; for p in $$list; do \ | ||
| 533 | if test -f $$p; then \ | ||
| 534 | list2="$$list2 $$p"; \ | ||
| 535 | else :; fi; \ | ||
| 536 | done; \ | ||
| 537 | test -z "$$list2" || { \ | ||
| 538 | echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ | ||
| 539 | $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ | ||
| 540 | echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ | ||
| 541 | $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ | ||
| 542 | } | ||
| 543 | |||
| 544 | uninstall-libLTLIBRARIES: | ||
| 545 | @$(NORMAL_UNINSTALL) | ||
| 546 | @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ | ||
| 547 | for p in $$list; do \ | ||
| 548 | $(am__strip_dir) \ | ||
| 549 | echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ | ||
| 550 | $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ | ||
| 551 | done | ||
| 552 | |||
| 553 | clean-libLTLIBRARIES: | ||
| 554 | -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) | ||
| 555 | @list='$(lib_LTLIBRARIES)'; \ | ||
| 556 | locs=`for p in $$list; do echo $$p; done | \ | ||
| 557 | sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ | ||
| 558 | sort -u`; \ | ||
| 559 | test -z "$$locs" || { \ | ||
| 560 | echo rm -f $${locs}; \ | ||
| 561 | rm -f $${locs}; \ | ||
| 562 | } | ||
| 563 | |||
| 564 | libsylph-0.la: $(libsylph_0_la_OBJECTS) $(libsylph_0_la_DEPENDENCIES) $(EXTRA_libsylph_0_la_DEPENDENCIES) | ||
| 565 | $(AM_V_CCLD)$(libsylph_0_la_LINK) -rpath $(libdir) $(libsylph_0_la_OBJECTS) $(libsylph_0_la_LIBADD) $(LIBS) | ||
| 566 | |||
| 567 | mostlyclean-compile: | ||
| 568 | -rm -f *.$(OBJEXT) | ||
| 569 | |||
| 570 | distclean-compile: | ||
| 571 | -rm -f *.tab.c | ||
| 572 | |||
| 573 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/account.Plo@am__quote@ | ||
| 574 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@ | ||
| 575 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/codeconv.Plo@am__quote@ | ||
| 576 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/customheader.Plo@am__quote@ | ||
| 577 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/displayheader.Plo@am__quote@ | ||
| 578 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter.Plo@am__quote@ | ||
| 579 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Plo@am__quote@ | ||
| 580 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/html.Plo@am__quote@ | ||
| 581 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Plo@am__quote@ | ||
| 582 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mbox.Plo@am__quote@ | ||
| 583 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Plo@am__quote@ | ||
| 584 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5_hmac.Plo@am__quote@ | ||
| 585 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mh.Plo@am__quote@ | ||
| 586 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/news.Plo@am__quote@ | ||
| 587 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nntp.Plo@am__quote@ | ||
| 588 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pop.Plo@am__quote@ | ||
| 589 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs.Plo@am__quote@ | ||
| 590 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_account.Plo@am__quote@ | ||
| 591 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prefs_common.Plo@am__quote@ | ||
| 592 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procheader.Plo@am__quote@ | ||
| 593 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procmime.Plo@am__quote@ | ||
| 594 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/procmsg.Plo@am__quote@ | ||
| 595 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quoted-printable.Plo@am__quote@ | ||
| 596 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/recv.Plo@am__quote@ | ||
| 597 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/session.Plo@am__quote@ | ||
| 598 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp.Plo@am__quote@ | ||
| 599 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Plo@am__quote@ | ||
| 600 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socks.Plo@am__quote@ | ||
| 601 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Plo@am__quote@ | ||
| 602 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl_hostname_validation.Plo@am__quote@ | ||
| 603 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringtable.Plo@am__quote@ | ||
| 604 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syl-marshal.Plo@am__quote@ | ||
| 605 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sylmain.Plo@am__quote@ | ||
| 606 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unmime.Plo@am__quote@ | ||
| 607 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ | ||
| 608 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/uuencode.Plo@am__quote@ | ||
| 609 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/virtual.Plo@am__quote@ | ||
| 610 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xml.Plo@am__quote@ | ||
| 611 | |||
| 612 | .c.o: | ||
| 613 | @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< | ||
| 614 | @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po | ||
| 615 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ | ||
| 616 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ | ||
| 617 | @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< | ||
| 618 | |||
| 619 | .c.obj: | ||
| 620 | @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` | ||
| 621 | @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po | ||
| 622 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ | ||
| 623 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ | ||
| 624 | @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` | ||
| 625 | |||
| 626 | .c.lo: | ||
| 627 | @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< | ||
| 628 | @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo | ||
| 629 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ | ||
| 630 | @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ | ||
| 631 | @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< | ||
| 632 | |||
| 633 | mostlyclean-libtool: | ||
| 634 | -rm -f *.lo | ||
| 635 | |||
| 636 | clean-libtool: | ||
| 637 | -rm -rf .libs _libs | ||
| 638 | install-libsylph_0includeHEADERS: $(libsylph_0include_HEADERS) | ||
| 639 | @$(NORMAL_INSTALL) | ||
| 640 | @list='$(libsylph_0include_HEADERS)'; test -n "$(libsylph_0includedir)" || list=; \ | ||
| 641 | if test -n "$$list"; then \ | ||
| 642 | echo " $(MKDIR_P) '$(DESTDIR)$(libsylph_0includedir)'"; \ | ||
| 643 | $(MKDIR_P) "$(DESTDIR)$(libsylph_0includedir)" || exit 1; \ | ||
| 644 | fi; \ | ||
| 645 | for p in $$list; do \ | ||
| 646 | if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ | ||
| 647 | echo "$$d$$p"; \ | ||
| 648 | done | $(am__base_list) | \ | ||
| 649 | while read files; do \ | ||
| 650 | echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libsylph_0includedir)'"; \ | ||
| 651 | $(INSTALL_HEADER) $$files "$(DESTDIR)$(libsylph_0includedir)" || exit $$?; \ | ||
| 652 | done | ||
| 653 | |||
| 654 | uninstall-libsylph_0includeHEADERS: | ||
| 655 | @$(NORMAL_UNINSTALL) | ||
| 656 | @list='$(libsylph_0include_HEADERS)'; test -n "$(libsylph_0includedir)" || list=; \ | ||
| 657 | files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ | ||
| 658 | dir='$(DESTDIR)$(libsylph_0includedir)'; $(am__uninstall_files_from_dir) | ||
| 659 | |||
| 660 | ID: $(am__tagged_files) | ||
| 661 | $(am__define_uniq_tagged_files); mkid -fID $$unique | ||
| 662 | tags: tags-am | ||
| 663 | TAGS: tags | ||
| 664 | |||
| 665 | tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) | ||
| 666 | set x; \ | ||
| 667 | here=`pwd`; \ | ||
| 668 | $(am__define_uniq_tagged_files); \ | ||
| 669 | shift; \ | ||
| 670 | if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ | ||
| 671 | test -n "$$unique" || unique=$$empty_fix; \ | ||
| 672 | if test $$# -gt 0; then \ | ||
| 673 | $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ | ||
| 674 | "$$@" $$unique; \ | ||
| 675 | else \ | ||
| 676 | $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ | ||
| 677 | $$unique; \ | ||
| 678 | fi; \ | ||
| 679 | fi | ||
| 680 | ctags: ctags-am | ||
| 681 | |||
| 682 | CTAGS: ctags | ||
| 683 | ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) | ||
| 684 | $(am__define_uniq_tagged_files); \ | ||
| 685 | test -z "$(CTAGS_ARGS)$$unique" \ | ||
| 686 | || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ | ||
| 687 | $$unique | ||
| 688 | |||
| 689 | GTAGS: | ||
| 690 | here=`$(am__cd) $(top_builddir) && pwd` \ | ||
| 691 | && $(am__cd) $(top_srcdir) \ | ||
| 692 | && gtags -i $(GTAGS_ARGS) "$$here" | ||
| 693 | cscopelist: cscopelist-am | ||
| 694 | |||
| 695 | cscopelist-am: $(am__tagged_files) | ||
| 696 | list='$(am__tagged_files)'; \ | ||
| 697 | case "$(srcdir)" in \ | ||
| 698 | [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ | ||
| 699 | *) sdir=$(subdir)/$(srcdir) ;; \ | ||
| 700 | esac; \ | ||
| 701 | for i in $$list; do \ | ||
| 702 | if test -f "$$i"; then \ | ||
| 703 | echo "$(subdir)/$$i"; \ | ||
| 704 | else \ | ||
| 705 | echo "$$sdir/$$i"; \ | ||
| 706 | fi; \ | ||
| 707 | done >> $(top_builddir)/cscope.files | ||
| 708 | |||
| 709 | distclean-tags: | ||
| 710 | -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags | ||
| 711 | |||
| 712 | distdir: $(DISTFILES) | ||
| 713 | @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ | ||
| 714 | topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ | ||
| 715 | list='$(DISTFILES)'; \ | ||
| 716 | dist_files=`for file in $$list; do echo $$file; done | \ | ||
| 717 | sed -e "s|^$$srcdirstrip/||;t" \ | ||
| 718 | -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ | ||
| 719 | case $$dist_files in \ | ||
| 720 | */*) $(MKDIR_P) `echo "$$dist_files" | \ | ||
| 721 | sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ | ||
| 722 | sort -u` ;; \ | ||
| 723 | esac; \ | ||
| 724 | for file in $$dist_files; do \ | ||
| 725 | if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ | ||
| 726 | if test -d $$d/$$file; then \ | ||
| 727 | dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ | ||
| 728 | if test -d "$(distdir)/$$file"; then \ | ||
| 729 | find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ | ||
| 730 | fi; \ | ||
| 731 | if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ | ||
| 732 | cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ | ||
| 733 | find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ | ||
| 734 | fi; \ | ||
| 735 | cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ | ||
| 736 | else \ | ||
| 737 | test -f "$(distdir)/$$file" \ | ||
| 738 | || cp -p $$d/$$file "$(distdir)/$$file" \ | ||
| 739 | || exit 1; \ | ||
| 740 | fi; \ | ||
| 741 | done | ||
| 742 | check-am: all-am | ||
| 743 | check: $(BUILT_SOURCES) | ||
| 744 | $(MAKE) $(AM_MAKEFLAGS) check-am | ||
| 745 | all-am: Makefile $(LTLIBRARIES) $(HEADERS) | ||
| 746 | installdirs: | ||
| 747 | for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libsylph_0includedir)"; do \ | ||
| 748 | test -z "$$dir" || $(MKDIR_P) "$$dir"; \ | ||
| 749 | done | ||
| 750 | install: $(BUILT_SOURCES) | ||
| 751 | $(MAKE) $(AM_MAKEFLAGS) install-am | ||
| 752 | install-exec: install-exec-am | ||
| 753 | install-data: install-data-am | ||
| 754 | uninstall: uninstall-am | ||
| 755 | |||
| 756 | install-am: all-am | ||
| 757 | @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am | ||
| 758 | |||
| 759 | installcheck: installcheck-am | ||
| 760 | install-strip: | ||
| 761 | if test -z '$(STRIP)'; then \ | ||
| 762 | $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ | ||
| 763 | install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ | ||
| 764 | install; \ | ||
| 765 | else \ | ||
| 766 | $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ | ||
| 767 | install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ | ||
| 768 | "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ | ||
| 769 | fi | ||
| 770 | mostlyclean-generic: | ||
| 771 | |||
| 772 | clean-generic: | ||
| 773 | |||
| 774 | distclean-generic: | ||
| 775 | -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) | ||
| 776 | -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) | ||
| 777 | |||
| 778 | maintainer-clean-generic: | ||
| 779 | @echo "This command is intended for maintainers to use" | ||
| 780 | @echo "it deletes files that may require special tools to rebuild." | ||
| 781 | -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) | ||
| 782 | clean: clean-am | ||
| 783 | |||
| 784 | clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ | ||
| 785 | mostlyclean-am | ||
| 786 | |||
| 787 | distclean: distclean-am | ||
| 788 | -rm -rf ./$(DEPDIR) | ||
| 789 | -rm -f Makefile | ||
| 790 | distclean-am: clean-am distclean-compile distclean-generic \ | ||
| 791 | distclean-tags | ||
| 792 | |||
| 793 | dvi: dvi-am | ||
| 794 | |||
| 795 | dvi-am: | ||
| 796 | |||
| 797 | html: html-am | ||
| 798 | |||
| 799 | html-am: | ||
| 800 | |||
| 801 | info: info-am | ||
| 802 | |||
| 803 | info-am: | ||
| 804 | |||
| 805 | install-data-am: install-libsylph_0includeHEADERS | ||
| 806 | |||
| 807 | install-dvi: install-dvi-am | ||
| 808 | |||
| 809 | install-dvi-am: | ||
| 810 | |||
| 811 | install-exec-am: install-libLTLIBRARIES | ||
| 812 | |||
| 813 | install-html: install-html-am | ||
| 814 | |||
| 815 | install-html-am: | ||
| 816 | |||
| 817 | install-info: install-info-am | ||
| 818 | |||
| 819 | install-info-am: | ||
| 820 | |||
| 821 | install-man: | ||
| 822 | |||
| 823 | install-pdf: install-pdf-am | ||
| 824 | |||
| 825 | install-pdf-am: | ||
| 826 | |||
| 827 | install-ps: install-ps-am | ||
| 828 | |||
| 829 | install-ps-am: | ||
| 830 | |||
| 831 | installcheck-am: | ||
| 832 | |||
| 833 | maintainer-clean: maintainer-clean-am | ||
| 834 | -rm -rf ./$(DEPDIR) | ||
| 835 | -rm -f Makefile | ||
| 836 | maintainer-clean-am: distclean-am maintainer-clean-generic | ||
| 837 | |||
| 838 | mostlyclean: mostlyclean-am | ||
| 839 | |||
| 840 | mostlyclean-am: mostlyclean-compile mostlyclean-generic \ | ||
| 841 | mostlyclean-libtool | ||
| 842 | |||
| 843 | pdf: pdf-am | ||
| 844 | |||
| 845 | pdf-am: | ||
| 846 | |||
| 847 | ps: ps-am | ||
| 848 | |||
| 849 | ps-am: | ||
| 850 | |||
| 851 | uninstall-am: uninstall-libLTLIBRARIES \ | ||
| 852 | uninstall-libsylph_0includeHEADERS | ||
| 853 | |||
| 854 | .MAKE: all check install install-am install-strip | ||
| 855 | |||
| 856 | .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ | ||
| 857 | clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ | ||
| 858 | ctags-am distclean distclean-compile distclean-generic \ | ||
| 859 | distclean-libtool distclean-tags distdir dvi dvi-am html \ | ||
| 860 | html-am info info-am install install-am install-data \ | ||
| 861 | install-data-am install-dvi install-dvi-am install-exec \ | ||
| 862 | install-exec-am install-html install-html-am install-info \ | ||
| 863 | install-info-am install-libLTLIBRARIES \ | ||
| 864 | install-libsylph_0includeHEADERS install-man install-pdf \ | ||
| 865 | install-pdf-am install-ps install-ps-am install-strip \ | ||
| 866 | installcheck installcheck-am installdirs maintainer-clean \ | ||
| 867 | maintainer-clean-generic mostlyclean mostlyclean-compile \ | ||
| 868 | mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ | ||
| 869 | tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ | ||
| 870 | uninstall-libsylph_0includeHEADERS | ||
| 871 | |||
| 872 | |||
| 873 | syl-marshal.h: syl-marshal.list | ||
| 874 | $(GLIB_GENMARSHAL) $< --header --prefix=syl_marshal > $@ | ||
| 875 | |||
| 876 | syl-marshal.c: syl-marshal.list | ||
| 877 | $(GLIB_GENMARSHAL) $< --body --prefix=syl_marshal > $@ | ||
| 878 | |||
| 879 | # Tell versions [3.59,3.63) of GNU make to not export all variables. | ||
| 880 | # Otherwise a system limit (for SysV at least) may be exceeded. | ||
| 881 | .NOEXPORT: | ||
diff --git a/libsylph/account.c b/libsylph/account.c new file mode 100644 index 0000000..2431bf3 --- /dev/null +++ b/libsylph/account.c | |||
| @@ -0,0 +1,491 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2009 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <errno.h> | ||
| 30 | |||
| 31 | #include "folder.h" | ||
| 32 | #include "account.h" | ||
| 33 | #include "prefs.h" | ||
| 34 | #include "prefs_account.h" | ||
| 35 | #include "procmsg.h" | ||
| 36 | #include "procheader.h" | ||
| 37 | #include "utils.h" | ||
| 38 | #include "sylmain.h" | ||
| 39 | |||
| 40 | PrefsAccount *cur_account; | ||
| 41 | |||
| 42 | static GList *account_list = NULL; | ||
| 43 | |||
| 44 | GHashTable *address_table; | ||
| 45 | |||
| 46 | |||
| 47 | void account_read_config_all(void) | ||
| 48 | { | ||
| 49 | GSList *ac_label_list = NULL, *cur; | ||
| 50 | gchar *rcpath; | ||
| 51 | FILE *fp; | ||
| 52 | gchar buf[PREFSBUFSIZE]; | ||
| 53 | PrefsAccount *ac_prefs; | ||
| 54 | |||
| 55 | debug_print(_("Reading all config for each account...\n")); | ||
| 56 | |||
| 57 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL); | ||
| 58 | if ((fp = g_fopen(rcpath, "rb")) == NULL) { | ||
| 59 | if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); | ||
| 60 | g_free(rcpath); | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | g_free(rcpath); | ||
| 64 | |||
| 65 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 66 | if (!strncmp(buf, "[Account: ", 10)) { | ||
| 67 | strretchomp(buf); | ||
| 68 | memmove(buf, buf + 1, strlen(buf)); | ||
| 69 | buf[strlen(buf) - 1] = '\0'; | ||
| 70 | debug_print("Found label: %s\n", buf); | ||
| 71 | ac_label_list = g_slist_append(ac_label_list, | ||
| 72 | g_strdup(buf)); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | fclose(fp); | ||
| 76 | |||
| 77 | /* read config data from file */ | ||
| 78 | cur_account = NULL; | ||
| 79 | for (cur = ac_label_list; cur != NULL; cur = cur->next) { | ||
| 80 | ac_prefs = prefs_account_new(); | ||
| 81 | prefs_account_read_config(ac_prefs, (gchar *)cur->data); | ||
| 82 | account_list = g_list_append(account_list, ac_prefs); | ||
| 83 | if (ac_prefs->is_default) | ||
| 84 | cur_account = ac_prefs; | ||
| 85 | } | ||
| 86 | /* if default is not set, assume first account as default */ | ||
| 87 | if (!cur_account && account_list) { | ||
| 88 | ac_prefs = (PrefsAccount *)account_list->data; | ||
| 89 | account_set_as_default(ac_prefs); | ||
| 90 | cur_account = ac_prefs; | ||
| 91 | } | ||
| 92 | |||
| 93 | while (ac_label_list) { | ||
| 94 | g_free(ac_label_list->data); | ||
| 95 | ac_label_list = g_slist_remove(ac_label_list, | ||
| 96 | ac_label_list->data); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | void account_write_config_all(void) | ||
| 101 | { | ||
| 102 | prefs_account_write_config_all(account_list); | ||
| 103 | } | ||
| 104 | |||
| 105 | PrefsAccount *account_get_current_account(void) | ||
| 106 | { | ||
| 107 | return cur_account; | ||
| 108 | } | ||
| 109 | |||
| 110 | PrefsAccount *account_find_from_smtp_server(const gchar *address, | ||
| 111 | const gchar *smtp_server) | ||
| 112 | { | ||
| 113 | GList *cur; | ||
| 114 | PrefsAccount *ac; | ||
| 115 | |||
| 116 | g_return_val_if_fail(address != NULL, NULL); | ||
| 117 | g_return_val_if_fail(smtp_server != NULL, NULL); | ||
| 118 | |||
| 119 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 120 | ac = (PrefsAccount *)cur->data; | ||
| 121 | if (!strcmp2(address, ac->address) && | ||
| 122 | !strcmp2(smtp_server, ac->smtp_server)) | ||
| 123 | return ac; | ||
| 124 | } | ||
| 125 | |||
| 126 | return NULL; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * account_find_from_address: | ||
| 131 | * @address: Email address string. | ||
| 132 | * | ||
| 133 | * Find a mail (not news) account with the specified email address. | ||
| 134 | * | ||
| 135 | * Return value: The found account, or NULL if not found. | ||
| 136 | */ | ||
| 137 | PrefsAccount *account_find_from_address(const gchar *address) | ||
| 138 | { | ||
| 139 | GList *cur; | ||
| 140 | PrefsAccount *ac; | ||
| 141 | |||
| 142 | g_return_val_if_fail(address != NULL, NULL); | ||
| 143 | |||
| 144 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 145 | ac = (PrefsAccount *)cur->data; | ||
| 146 | if (ac->protocol != A_NNTP && ac->address && | ||
| 147 | strcasestr(address, ac->address) != NULL) | ||
| 148 | return ac; | ||
| 149 | } | ||
| 150 | |||
| 151 | return NULL; | ||
| 152 | } | ||
| 153 | |||
| 154 | PrefsAccount *account_find_from_id(gint id) | ||
| 155 | { | ||
| 156 | GList *cur; | ||
| 157 | PrefsAccount *ac; | ||
| 158 | |||
| 159 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 160 | ac = (PrefsAccount *)cur->data; | ||
| 161 | if (id == ac->account_id) | ||
| 162 | return ac; | ||
| 163 | } | ||
| 164 | |||
| 165 | return NULL; | ||
| 166 | } | ||
| 167 | |||
| 168 | PrefsAccount *account_find_from_item(FolderItem *item) | ||
| 169 | { | ||
| 170 | PrefsAccount *ac; | ||
| 171 | |||
| 172 | g_return_val_if_fail(item != NULL, NULL); | ||
| 173 | |||
| 174 | ac = account_find_from_item_property(item); | ||
| 175 | if (!ac) | ||
| 176 | ac = item->folder->account; | ||
| 177 | |||
| 178 | return ac; | ||
| 179 | } | ||
| 180 | |||
| 181 | PrefsAccount *account_find_from_item_property(FolderItem *item) | ||
| 182 | { | ||
| 183 | PrefsAccount *ac; | ||
| 184 | |||
| 185 | g_return_val_if_fail(item != NULL, NULL); | ||
| 186 | |||
| 187 | ac = item->account; | ||
| 188 | if (!ac) { | ||
| 189 | FolderItem *cur_item = item->parent; | ||
| 190 | while (cur_item != NULL) { | ||
| 191 | if (cur_item->account && cur_item->ac_apply_sub) { | ||
| 192 | ac = cur_item->account; | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | cur_item = cur_item->parent; | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | return ac; | ||
| 200 | } | ||
| 201 | |||
| 202 | PrefsAccount *account_find_from_message_file(const gchar *file) | ||
| 203 | { | ||
| 204 | static HeaderEntry hentry[] = {{"From:", NULL, FALSE}, | ||
| 205 | {"X-Sylpheed-Account-Id:", NULL, FALSE}, | ||
| 206 | {"AID:", NULL, FALSE}, | ||
| 207 | {NULL, NULL, FALSE}}; | ||
| 208 | |||
| 209 | enum | ||
| 210 | { | ||
| 211 | H_FROM = 0, | ||
| 212 | H_X_SYLPHEED_ACCOUNT_ID = 1, | ||
| 213 | H_AID = 2 | ||
| 214 | }; | ||
| 215 | |||
| 216 | PrefsAccount *ac = NULL; | ||
| 217 | FILE *fp; | ||
| 218 | gchar *str; | ||
| 219 | gchar buf[BUFFSIZE]; | ||
| 220 | gint hnum; | ||
| 221 | |||
| 222 | g_return_val_if_fail(file != NULL, NULL); | ||
| 223 | |||
| 224 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 225 | FILE_OP_ERROR(file, "fopen"); | ||
| 226 | return NULL; | ||
| 227 | } | ||
| 228 | |||
| 229 | while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) | ||
| 230 | != -1) { | ||
| 231 | str = buf + strlen(hentry[hnum].name); | ||
| 232 | if (hnum == H_FROM) | ||
| 233 | ac = account_find_from_address(str); | ||
| 234 | else if (hnum == H_X_SYLPHEED_ACCOUNT_ID || hnum == H_AID) { | ||
| 235 | PrefsAccount *tmp_ac; | ||
| 236 | |||
| 237 | tmp_ac = account_find_from_id(atoi(str)); | ||
| 238 | if (tmp_ac) { | ||
| 239 | ac = tmp_ac; | ||
| 240 | break; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | fclose(fp); | ||
| 246 | return ac; | ||
| 247 | } | ||
| 248 | |||
| 249 | PrefsAccount *account_find_from_msginfo(MsgInfo *msginfo) | ||
| 250 | { | ||
| 251 | gchar *file; | ||
| 252 | PrefsAccount *ac; | ||
| 253 | |||
| 254 | file = procmsg_get_message_file(msginfo); | ||
| 255 | ac = account_find_from_message_file(file); | ||
| 256 | g_free(file); | ||
| 257 | |||
| 258 | if (!ac && msginfo->folder) | ||
| 259 | ac = account_find_from_item(msginfo->folder); | ||
| 260 | |||
| 261 | return ac; | ||
| 262 | } | ||
| 263 | |||
| 264 | gboolean account_address_exist(const gchar *address) | ||
| 265 | { | ||
| 266 | if (!address_table) { | ||
| 267 | GList *cur; | ||
| 268 | |||
| 269 | address_table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 270 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 271 | PrefsAccount *ac = (PrefsAccount *)cur->data; | ||
| 272 | |||
| 273 | if (ac->address) | ||
| 274 | g_hash_table_insert(address_table, ac->address, | ||
| 275 | GINT_TO_POINTER(1)); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | return (gboolean)g_hash_table_lookup(address_table, address); | ||
| 280 | } | ||
| 281 | |||
| 282 | void account_foreach(AccountFunc func, gpointer user_data) | ||
| 283 | { | ||
| 284 | GList *cur; | ||
| 285 | |||
| 286 | for (cur = account_list; cur != NULL; cur = cur->next) | ||
| 287 | if (func((PrefsAccount *)cur->data, user_data) != 0) | ||
| 288 | return; | ||
| 289 | } | ||
| 290 | |||
| 291 | GList *account_get_list(void) | ||
| 292 | { | ||
| 293 | return account_list; | ||
| 294 | } | ||
| 295 | |||
| 296 | void account_list_free(void) | ||
| 297 | { | ||
| 298 | g_list_free(account_list); | ||
| 299 | account_list = NULL; | ||
| 300 | } | ||
| 301 | |||
| 302 | void account_append(PrefsAccount *ac_prefs) | ||
| 303 | { | ||
| 304 | account_list = g_list_append(account_list, ac_prefs); | ||
| 305 | account_updated(); | ||
| 306 | } | ||
| 307 | |||
| 308 | void account_set_as_default(PrefsAccount *ac_prefs) | ||
| 309 | { | ||
| 310 | PrefsAccount *ap; | ||
| 311 | GList *cur; | ||
| 312 | |||
| 313 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 314 | ap = (PrefsAccount *)cur->data; | ||
| 315 | if (ap->is_default) | ||
| 316 | ap->is_default = FALSE; | ||
| 317 | } | ||
| 318 | |||
| 319 | ac_prefs->is_default = TRUE; | ||
| 320 | } | ||
| 321 | |||
| 322 | PrefsAccount *account_get_default(void) | ||
| 323 | { | ||
| 324 | PrefsAccount *ap; | ||
| 325 | GList *cur; | ||
| 326 | |||
| 327 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 328 | ap = (PrefsAccount *)cur->data; | ||
| 329 | if (ap->is_default) | ||
| 330 | return ap; | ||
| 331 | } | ||
| 332 | |||
| 333 | return NULL; | ||
| 334 | } | ||
| 335 | |||
| 336 | #if 0 | ||
| 337 | void account_set_missing_folder(void) | ||
| 338 | { | ||
| 339 | PrefsAccount *ap; | ||
| 340 | GList *cur; | ||
| 341 | |||
| 342 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 343 | ap = (PrefsAccount *)cur->data; | ||
| 344 | if ((ap->protocol == A_IMAP4 || ap->protocol == A_NNTP) && | ||
| 345 | !ap->folder) { | ||
| 346 | Folder *folder; | ||
| 347 | |||
| 348 | if (ap->protocol == A_IMAP4) { | ||
| 349 | folder = folder_new(F_IMAP, ap->account_name, | ||
| 350 | ap->recv_server); | ||
| 351 | } else { | ||
| 352 | folder = folder_new(F_NEWS, ap->account_name, | ||
| 353 | ap->nntp_server); | ||
| 354 | } | ||
| 355 | |||
| 356 | folder->account = ap; | ||
| 357 | ap->folder = REMOTE_FOLDER(folder); | ||
| 358 | folder_add(folder); | ||
| 359 | if (ap->protocol == A_IMAP4) { | ||
| 360 | if (main_window_toggle_online_if_offline | ||
| 361 | (main_window_get())) { | ||
| 362 | folder->klass->create_tree(folder); | ||
| 363 | statusbar_pop_all(); | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | #endif | ||
| 370 | |||
| 371 | FolderItem *account_get_special_folder(PrefsAccount *ac_prefs, | ||
| 372 | SpecialFolderItemType type) | ||
| 373 | { | ||
| 374 | FolderItem *item = NULL; | ||
| 375 | |||
| 376 | g_return_val_if_fail(ac_prefs != NULL, NULL); | ||
| 377 | |||
| 378 | switch (type) { | ||
| 379 | case F_INBOX: | ||
| 380 | if (ac_prefs->folder) | ||
| 381 | item = FOLDER(ac_prefs->folder)->inbox; | ||
| 382 | if (!item) | ||
| 383 | item = folder_get_default_inbox(); | ||
| 384 | break; | ||
| 385 | case F_OUTBOX: | ||
| 386 | if (ac_prefs->set_sent_folder && ac_prefs->sent_folder) { | ||
| 387 | item = folder_find_item_from_identifier | ||
| 388 | (ac_prefs->sent_folder); | ||
| 389 | } | ||
| 390 | if (!item) { | ||
| 391 | if (ac_prefs->folder) | ||
| 392 | item = FOLDER(ac_prefs->folder)->outbox; | ||
| 393 | if (!item) | ||
| 394 | item = folder_get_default_outbox(); | ||
| 395 | } | ||
| 396 | break; | ||
| 397 | case F_DRAFT: | ||
| 398 | if (ac_prefs->set_draft_folder && ac_prefs->draft_folder) { | ||
| 399 | item = folder_find_item_from_identifier | ||
| 400 | (ac_prefs->draft_folder); | ||
| 401 | } | ||
| 402 | if (!item) { | ||
| 403 | if (ac_prefs->folder) | ||
| 404 | item = FOLDER(ac_prefs->folder)->draft; | ||
| 405 | if (!item) | ||
| 406 | item = folder_get_default_draft(); | ||
| 407 | } | ||
| 408 | break; | ||
| 409 | case F_QUEUE: | ||
| 410 | if (ac_prefs->set_queue_folder && ac_prefs->queue_folder) { | ||
| 411 | item = folder_find_item_from_identifier | ||
| 412 | (ac_prefs->queue_folder); | ||
| 413 | /* only allow queue-type folder */ | ||
| 414 | if (item && item->stype != F_QUEUE) | ||
| 415 | item = NULL; | ||
| 416 | } | ||
| 417 | if (!item) { | ||
| 418 | if (ac_prefs->folder) | ||
| 419 | item = FOLDER(ac_prefs->folder)->queue; | ||
| 420 | if (!item) | ||
| 421 | item = folder_get_default_queue(); | ||
| 422 | } | ||
| 423 | break; | ||
| 424 | case F_TRASH: | ||
| 425 | if (ac_prefs->set_trash_folder && ac_prefs->trash_folder) { | ||
| 426 | item = folder_find_item_from_identifier | ||
| 427 | (ac_prefs->trash_folder); | ||
| 428 | } | ||
| 429 | if (!item) { | ||
| 430 | if (ac_prefs->folder) | ||
| 431 | item = FOLDER(ac_prefs->folder)->trash; | ||
| 432 | if (!item) | ||
| 433 | item = folder_get_default_trash(); | ||
| 434 | } | ||
| 435 | break; | ||
| 436 | default: | ||
| 437 | break; | ||
| 438 | } | ||
| 439 | |||
| 440 | return item; | ||
| 441 | } | ||
| 442 | |||
| 443 | void account_destroy(PrefsAccount *ac_prefs) | ||
| 444 | { | ||
| 445 | g_return_if_fail(ac_prefs != NULL); | ||
| 446 | |||
| 447 | folder_unref_account_all(ac_prefs); | ||
| 448 | |||
| 449 | account_list = g_list_remove(account_list, ac_prefs); | ||
| 450 | if (cur_account == ac_prefs) | ||
| 451 | cur_account = NULL; | ||
| 452 | prefs_account_free(ac_prefs); | ||
| 453 | |||
| 454 | if (!cur_account && account_list) { | ||
| 455 | cur_account = account_get_default(); | ||
| 456 | if (!cur_account) { | ||
| 457 | ac_prefs = (PrefsAccount *)account_list->data; | ||
| 458 | account_set_as_default(ac_prefs); | ||
| 459 | cur_account = ac_prefs; | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | account_updated(); | ||
| 464 | } | ||
| 465 | |||
| 466 | static guint account_update_lock_count = 0; | ||
| 467 | |||
| 468 | void account_update_lock(void) | ||
| 469 | { | ||
| 470 | account_update_lock_count++; | ||
| 471 | } | ||
| 472 | |||
| 473 | void account_update_unlock(void) | ||
| 474 | { | ||
| 475 | if (account_update_lock_count > 0) | ||
| 476 | account_update_lock_count--; | ||
| 477 | } | ||
| 478 | |||
| 479 | void account_updated(void) | ||
| 480 | { | ||
| 481 | if (account_update_lock_count) | ||
| 482 | return; | ||
| 483 | |||
| 484 | if (address_table) { | ||
| 485 | g_hash_table_destroy(address_table); | ||
| 486 | address_table = NULL; | ||
| 487 | } | ||
| 488 | |||
| 489 | if (syl_app_get()) | ||
| 490 | g_signal_emit_by_name(syl_app_get(), "account-updated"); | ||
| 491 | } | ||
diff --git a/libsylph/account.h b/libsylph/account.h new file mode 100644 index 0000000..4485114 --- /dev/null +++ b/libsylph/account.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2009 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __ACCOUNT_H__ | ||
| 21 | #define __ACCOUNT_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "prefs.h" | ||
| 26 | #include "prefs_account.h" | ||
| 27 | #include "folder.h" | ||
| 28 | #include "procmsg.h" | ||
| 29 | |||
| 30 | typedef gint (*AccountFunc) (PrefsAccount *ac_prefs, | ||
| 31 | gpointer user_data); | ||
| 32 | |||
| 33 | extern PrefsAccount *cur_account; | ||
| 34 | |||
| 35 | void account_read_config_all (void); | ||
| 36 | void account_write_config_all (void); | ||
| 37 | |||
| 38 | PrefsAccount *account_get_current_account (void); | ||
| 39 | |||
| 40 | PrefsAccount *account_find_from_smtp_server (const gchar *address, | ||
| 41 | const gchar *smtp_server); | ||
| 42 | PrefsAccount *account_find_from_address (const gchar *address); | ||
| 43 | PrefsAccount *account_find_from_id (gint id); | ||
| 44 | PrefsAccount *account_find_from_item (FolderItem *item); | ||
| 45 | PrefsAccount *account_find_from_item_property (FolderItem *item); | ||
| 46 | PrefsAccount *account_find_from_message_file (const gchar *file); | ||
| 47 | PrefsAccount *account_find_from_msginfo (MsgInfo *msginfo); | ||
| 48 | |||
| 49 | gboolean account_address_exist (const gchar *address); | ||
| 50 | |||
| 51 | void account_foreach (AccountFunc func, | ||
| 52 | gpointer user_data); | ||
| 53 | GList *account_get_list (void); | ||
| 54 | void account_list_free (void); | ||
| 55 | void account_append (PrefsAccount *ac_prefs); | ||
| 56 | |||
| 57 | void account_set_as_default (PrefsAccount *ac_prefs); | ||
| 58 | PrefsAccount *account_get_default (void); | ||
| 59 | |||
| 60 | //void account_set_missing_folder(void); | ||
| 61 | FolderItem *account_get_special_folder(PrefsAccount *ac_prefs, | ||
| 62 | SpecialFolderItemType type); | ||
| 63 | |||
| 64 | void account_destroy (PrefsAccount *ac_prefs); | ||
| 65 | |||
| 66 | void account_update_lock (void); | ||
| 67 | void account_update_unlock (void); | ||
| 68 | void account_updated (void); | ||
| 69 | |||
| 70 | #endif /* __ACCOUNT_H__ */ | ||
diff --git a/libsylph/base64.c b/libsylph/base64.c new file mode 100644 index 0000000..2e54da7 --- /dev/null +++ b/libsylph/base64.c | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <glib.h> | ||
| 21 | #include <ctype.h> | ||
| 22 | #include <string.h> | ||
| 23 | |||
| 24 | #include "base64.h" | ||
| 25 | |||
| 26 | static const gchar base64char[64] = | ||
| 27 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
| 28 | |||
| 29 | static const gchar base64val[128] = { | ||
| 30 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
| 31 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
| 32 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, | ||
| 33 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, | ||
| 34 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | ||
| 35 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, | ||
| 36 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | ||
| 37 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 | ||
| 38 | }; | ||
| 39 | |||
| 40 | #define BASE64VAL(c) (isascii((guchar)c) ? base64val[(gint)(c)] : -1) | ||
| 41 | |||
| 42 | void base64_encode(gchar *out, const guchar *in, gint inlen) | ||
| 43 | { | ||
| 44 | const guchar *inp = in; | ||
| 45 | gchar *outp = out; | ||
| 46 | |||
| 47 | while (inlen >= 3) { | ||
| 48 | *outp++ = base64char[(inp[0] >> 2) & 0x3f]; | ||
| 49 | *outp++ = base64char[((inp[0] & 0x03) << 4) | | ||
| 50 | ((inp[1] >> 4) & 0x0f)]; | ||
| 51 | *outp++ = base64char[((inp[1] & 0x0f) << 2) | | ||
| 52 | ((inp[2] >> 6) & 0x03)]; | ||
| 53 | *outp++ = base64char[inp[2] & 0x3f]; | ||
| 54 | |||
| 55 | inp += 3; | ||
| 56 | inlen -= 3; | ||
| 57 | } | ||
| 58 | |||
| 59 | if (inlen > 0) { | ||
| 60 | *outp++ = base64char[(inp[0] >> 2) & 0x3f]; | ||
| 61 | if (inlen == 1) { | ||
| 62 | *outp++ = base64char[(inp[0] & 0x03) << 4]; | ||
| 63 | *outp++ = '='; | ||
| 64 | } else { | ||
| 65 | *outp++ = base64char[((inp[0] & 0x03) << 4) | | ||
| 66 | ((inp[1] >> 4) & 0x0f)]; | ||
| 67 | *outp++ = base64char[((inp[1] & 0x0f) << 2)]; | ||
| 68 | } | ||
| 69 | *outp++ = '='; | ||
| 70 | } | ||
| 71 | |||
| 72 | *outp = '\0'; | ||
| 73 | } | ||
| 74 | |||
| 75 | gint base64_decode(guchar *out, const gchar *in, gint inlen) | ||
| 76 | { | ||
| 77 | const gchar *inp = in; | ||
| 78 | guchar *outp = out; | ||
| 79 | gchar buf[4]; | ||
| 80 | |||
| 81 | if (inlen < 0) | ||
| 82 | inlen = G_MAXINT; | ||
| 83 | |||
| 84 | while (inlen >= 4 && *inp != '\0') { | ||
| 85 | buf[0] = *inp++; | ||
| 86 | inlen--; | ||
| 87 | if (BASE64VAL(buf[0]) == -1) break; | ||
| 88 | |||
| 89 | buf[1] = *inp++; | ||
| 90 | inlen--; | ||
| 91 | if (BASE64VAL(buf[1]) == -1) break; | ||
| 92 | |||
| 93 | buf[2] = *inp++; | ||
| 94 | inlen--; | ||
| 95 | if (buf[2] != '=' && BASE64VAL(buf[2]) == -1) break; | ||
| 96 | |||
| 97 | buf[3] = *inp++; | ||
| 98 | inlen--; | ||
| 99 | if (buf[3] != '=' && BASE64VAL(buf[3]) == -1) break; | ||
| 100 | |||
| 101 | *outp++ = ((BASE64VAL(buf[0]) << 2) & 0xfc) | | ||
| 102 | ((BASE64VAL(buf[1]) >> 4) & 0x03); | ||
| 103 | if (buf[2] != '=') { | ||
| 104 | *outp++ = ((BASE64VAL(buf[1]) & 0x0f) << 4) | | ||
| 105 | ((BASE64VAL(buf[2]) >> 2) & 0x0f); | ||
| 106 | if (buf[3] != '=') { | ||
| 107 | *outp++ = ((BASE64VAL(buf[2]) & 0x03) << 6) | | ||
| 108 | (BASE64VAL(buf[3]) & 0x3f); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | return outp - out; | ||
| 114 | } | ||
| 115 | |||
| 116 | Base64Decoder *base64_decoder_new(void) | ||
| 117 | { | ||
| 118 | Base64Decoder *decoder; | ||
| 119 | |||
| 120 | decoder = g_new0(Base64Decoder, 1); | ||
| 121 | return decoder; | ||
| 122 | } | ||
| 123 | |||
| 124 | void base64_decoder_free(Base64Decoder *decoder) | ||
| 125 | { | ||
| 126 | g_free(decoder); | ||
| 127 | } | ||
| 128 | |||
| 129 | gint base64_decoder_decode(Base64Decoder *decoder, | ||
| 130 | const gchar *in, guchar *out) | ||
| 131 | { | ||
| 132 | gint len, total_len = 0; | ||
| 133 | gint buf_len; | ||
| 134 | gchar buf[4]; | ||
| 135 | |||
| 136 | g_return_val_if_fail(decoder != NULL, -1); | ||
| 137 | g_return_val_if_fail(in != NULL, -1); | ||
| 138 | g_return_val_if_fail(out != NULL, -1); | ||
| 139 | |||
| 140 | buf_len = decoder->buf_len; | ||
| 141 | memcpy(buf, decoder->buf, sizeof(buf)); | ||
| 142 | |||
| 143 | for (;;) { | ||
| 144 | while (buf_len < 4) { | ||
| 145 | gchar c = *in; | ||
| 146 | |||
| 147 | in++; | ||
| 148 | if (c == '\0') break; | ||
| 149 | if (c == '\r' || c == '\n') continue; | ||
| 150 | if (c != '=' && BASE64VAL(c) == -1) | ||
| 151 | return -1; | ||
| 152 | buf[buf_len++] = c; | ||
| 153 | } | ||
| 154 | if (buf_len < 4 || buf[0] == '=' || buf[1] == '=') { | ||
| 155 | decoder->buf_len = buf_len; | ||
| 156 | memcpy(decoder->buf, buf, sizeof(buf)); | ||
| 157 | return total_len; | ||
| 158 | } | ||
| 159 | len = base64_decode(out, buf, 4); | ||
| 160 | out += len; | ||
| 161 | total_len += len; | ||
| 162 | buf_len = 0; | ||
| 163 | if (len < 3) { | ||
| 164 | decoder->buf_len = 0; | ||
| 165 | return total_len; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
diff --git a/libsylph/base64.h b/libsylph/base64.h new file mode 100644 index 0000000..d3b2c19 --- /dev/null +++ b/libsylph/base64.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __BASE64_H__ | ||
| 21 | #define __BASE64_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | typedef struct _Base64Decoder Base64Decoder; | ||
| 26 | |||
| 27 | struct _Base64Decoder | ||
| 28 | { | ||
| 29 | gint buf_len; | ||
| 30 | gchar buf[4]; | ||
| 31 | }; | ||
| 32 | |||
| 33 | void base64_encode (gchar *out, | ||
| 34 | const guchar *in, | ||
| 35 | gint inlen); | ||
| 36 | gint base64_decode (guchar *out, | ||
| 37 | const gchar *in, | ||
| 38 | gint inlen); | ||
| 39 | |||
| 40 | Base64Decoder *base64_decoder_new (void); | ||
| 41 | void base64_decoder_free (Base64Decoder *decoder); | ||
| 42 | gint base64_decoder_decode (Base64Decoder *decoder, | ||
| 43 | const gchar *in, | ||
| 44 | guchar *out); | ||
| 45 | |||
| 46 | #endif /* __BASE64_H__ */ | ||
diff --git a/libsylph/codeconv.c b/libsylph/codeconv.c new file mode 100644 index 0000000..49d1b3d --- /dev/null +++ b/libsylph/codeconv.c | |||
| @@ -0,0 +1,2856 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <ctype.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <errno.h> | ||
| 32 | |||
| 33 | #if HAVE_LOCALE_H | ||
| 34 | # include <locale.h> | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #include <iconv.h> | ||
| 38 | |||
| 39 | #include "codeconv.h" | ||
| 40 | #include "prefs_common.h" | ||
| 41 | #include "unmime.h" | ||
| 42 | #include "base64.h" | ||
| 43 | #include "quoted-printable.h" | ||
| 44 | #include "utils.h" | ||
| 45 | |||
| 46 | typedef enum | ||
| 47 | { | ||
| 48 | JIS_ASCII, | ||
| 49 | JIS_KANJI, | ||
| 50 | JIS_HWKANA, | ||
| 51 | JIS_AUXKANJI, | ||
| 52 | JIS_UDC | ||
| 53 | } JISState; | ||
| 54 | |||
| 55 | #define SUBST_CHAR '_' | ||
| 56 | #define ESC '\033' | ||
| 57 | #define SO 0x0e | ||
| 58 | #define SI 0x0f | ||
| 59 | #define SS2 0x8e | ||
| 60 | #define SS3 0x8f | ||
| 61 | |||
| 62 | #define iseuckanji(c) \ | ||
| 63 | (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xfe) | ||
| 64 | #define iseuchwkana1(c) \ | ||
| 65 | (((c) & 0xff) == SS2) | ||
| 66 | #define iseuchwkana2(c) \ | ||
| 67 | (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) | ||
| 68 | #define iseucaux(c) \ | ||
| 69 | (((c) & 0xff) == SS3) | ||
| 70 | |||
| 71 | #define issjiskanji1(c) \ | ||
| 72 | ((((c) & 0xff) >= 0x81 && ((c) & 0xff) <= 0x9f) || \ | ||
| 73 | (((c) & 0xff) >= 0xe0 && ((c) & 0xff) <= 0xef)) | ||
| 74 | #define issjiskanji2(c) \ | ||
| 75 | ((((c) & 0xff) >= 0x40 && ((c) & 0xff) <= 0x7e) || \ | ||
| 76 | (((c) & 0xff) >= 0x80 && ((c) & 0xff) <= 0xfc)) | ||
| 77 | #define issjishwkana(c) \ | ||
| 78 | (((c) & 0xff) >= 0xa1 && ((c) & 0xff) <= 0xdf) | ||
| 79 | #define issjisext(c) \ | ||
| 80 | (((c) & 0xff) >= 0xf0 && ((c) & 0xff) <= 0xfc) | ||
| 81 | #define issjisudc(c) \ | ||
| 82 | (((c) & 0xff) >= 0xf0 && ((c) & 0xff) <= 0xf9) | ||
| 83 | #define issjisibmext(c1, c2) \ | ||
| 84 | ((((c1) & 0xff) >= 0xfa && ((c1) & 0xff) <= 0xfb && \ | ||
| 85 | issjiskanji2(c2)) || \ | ||
| 86 | (((c1) & 0xff) == 0xfc && \ | ||
| 87 | ((c2) & 0xff) >= 0x40 && ((c2) & 0xff) <= 0x4b)) | ||
| 88 | |||
| 89 | #define isjiskanji(c) \ | ||
| 90 | (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x7e) | ||
| 91 | #define isjishwkana(c) \ | ||
| 92 | (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x5f) | ||
| 93 | #define isjisudc(c) \ | ||
| 94 | (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x34) | ||
| 95 | #define isjisudclow(c) \ | ||
| 96 | (((c) & 0xff) >= 0x21 && ((c) & 0xff) <= 0x2a) | ||
| 97 | #define isjisudchigh(c) \ | ||
| 98 | (((c) & 0xff) >= 0x2b && ((c) & 0xff) <= 0x34) | ||
| 99 | |||
| 100 | /* U+0080 - U+07FF */ | ||
| 101 | #define isutf8_2_1(c) \ | ||
| 102 | (((c) & 0xe0) == 0xc0) | ||
| 103 | #define isutf8_2_2(c) \ | ||
| 104 | (((c) & 0xc0) == 0x80) | ||
| 105 | /* U+0800 - U+FFFF */ | ||
| 106 | #define isutf8_3_1(c) \ | ||
| 107 | (((c) & 0xf0) == 0xe0) | ||
| 108 | #define isutf8_3_2(c) \ | ||
| 109 | (((c) & 0xc0) == 0x80) | ||
| 110 | |||
| 111 | #define isutf8bom(s) \ | ||
| 112 | (((*(s)) & 0xff) == 0xef && ((*(s + 1)) & 0xff) == 0xbb && \ | ||
| 113 | ((*(s + 2)) & 0xff) == 0xbf) | ||
| 114 | |||
| 115 | #define K_IN() \ | ||
| 116 | if (state != JIS_KANJI) { \ | ||
| 117 | *out++ = ESC; \ | ||
| 118 | *out++ = '$'; \ | ||
| 119 | *out++ = 'B'; \ | ||
| 120 | state = JIS_KANJI; \ | ||
| 121 | } | ||
| 122 | |||
| 123 | #define K_OUT() \ | ||
| 124 | if (state != JIS_ASCII) { \ | ||
| 125 | *out++ = ESC; \ | ||
| 126 | *out++ = '('; \ | ||
| 127 | *out++ = 'B'; \ | ||
| 128 | state = JIS_ASCII; \ | ||
| 129 | } | ||
| 130 | |||
| 131 | #define HW_IN() \ | ||
| 132 | if (state != JIS_HWKANA) { \ | ||
| 133 | *out++ = ESC; \ | ||
| 134 | *out++ = '('; \ | ||
| 135 | *out++ = 'I'; \ | ||
| 136 | state = JIS_HWKANA; \ | ||
| 137 | } | ||
| 138 | |||
| 139 | #define AUX_IN() \ | ||
| 140 | if (state != JIS_AUXKANJI) { \ | ||
| 141 | *out++ = ESC; \ | ||
| 142 | *out++ = '$'; \ | ||
| 143 | *out++ = '('; \ | ||
| 144 | *out++ = 'D'; \ | ||
| 145 | state = JIS_AUXKANJI; \ | ||
| 146 | } | ||
| 147 | |||
| 148 | #define UDC_IN() \ | ||
| 149 | if (state != JIS_UDC) { \ | ||
| 150 | *out++ = ESC; \ | ||
| 151 | *out++ = '$'; \ | ||
| 152 | *out++ = '('; \ | ||
| 153 | *out++ = '?'; \ | ||
| 154 | state = JIS_UDC; \ | ||
| 155 | } | ||
| 156 | |||
| 157 | static ConvADType conv_ad_type = C_AD_BY_LOCALE; | ||
| 158 | |||
| 159 | static gchar *conv_jistoeuc(const gchar *inbuf, gint *error); | ||
| 160 | static gchar *conv_jistosjis(const gchar *inbuf, gint *error); | ||
| 161 | static gchar *conv_euctojis(const gchar *inbuf, gint *error); | ||
| 162 | static gchar *conv_sjistojis(const gchar *inbuf, gint *error); | ||
| 163 | static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error); | ||
| 164 | |||
| 165 | static gchar *conv_jistoutf8(const gchar *inbuf, gint *error); | ||
| 166 | static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error); | ||
| 167 | static gchar *conv_euctoutf8(const gchar *inbuf, gint *error); | ||
| 168 | static gchar *conv_anytoutf8(const gchar *inbuf, gint *error); | ||
| 169 | |||
| 170 | static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error); | ||
| 171 | static gchar *conv_utf8tojis(const gchar *inbuf, gint *error); | ||
| 172 | static gchar *conv_utf8tosjis(const gchar *inbuf, gint *error); | ||
| 173 | |||
| 174 | /* static void conv_unreadable_eucjp(gchar *str); */ | ||
| 175 | static void conv_unreadable_8bit(gchar *str); | ||
| 176 | /* static void conv_unreadable_latin(gchar *str); */ | ||
| 177 | |||
| 178 | static gchar *conv_jistodisp(const gchar *inbuf, gint *error); | ||
| 179 | static gchar *conv_sjistodisp(const gchar *inbuf, gint *error); | ||
| 180 | static gchar *conv_euctodisp(const gchar *inbuf, gint *error); | ||
| 181 | |||
| 182 | static gchar *conv_anytodisp(const gchar *inbuf, gint *error); | ||
| 183 | static gchar *conv_ustodisp(const gchar *inbuf, gint *error); | ||
| 184 | static gchar *conv_noconv(const gchar *inbuf, gint *error); | ||
| 185 | |||
| 186 | static gchar *conv_jistoeuc(const gchar *inbuf, gint *error) | ||
| 187 | { | ||
| 188 | gchar *outbuf; | ||
| 189 | const guchar *in = (guchar *)inbuf; | ||
| 190 | guchar *out; | ||
| 191 | JISState state = JIS_ASCII; | ||
| 192 | gint error_ = 0; | ||
| 193 | |||
| 194 | outbuf = g_malloc(strlen(inbuf) * 2 + 1); | ||
| 195 | out = (guchar *)outbuf; | ||
| 196 | |||
| 197 | while (*in != '\0') { | ||
| 198 | if (*in == ESC) { | ||
| 199 | in++; | ||
| 200 | if (*in == '$') { | ||
| 201 | if (*(in + 1) == '@' || *(in + 1) == 'B') { | ||
| 202 | state = JIS_KANJI; | ||
| 203 | in += 2; | ||
| 204 | } else if (*(in + 1) == '(' && | ||
| 205 | *(in + 2) == 'D') { | ||
| 206 | state = JIS_AUXKANJI; | ||
| 207 | in += 3; | ||
| 208 | } else { | ||
| 209 | /* unknown escape sequence */ | ||
| 210 | error_ = -1; | ||
| 211 | state = JIS_ASCII; | ||
| 212 | } | ||
| 213 | } else if (*in == '(') { | ||
| 214 | if (*(in + 1) == 'B' || *(in + 1) == 'J') { | ||
| 215 | state = JIS_ASCII; | ||
| 216 | in += 2; | ||
| 217 | } else if (*(in + 1) == 'I') { | ||
| 218 | state = JIS_HWKANA; | ||
| 219 | in += 2; | ||
| 220 | } else { | ||
| 221 | /* unknown escape sequence */ | ||
| 222 | error_ = -1; | ||
| 223 | state = JIS_ASCII; | ||
| 224 | } | ||
| 225 | } else { | ||
| 226 | /* unknown escape sequence */ | ||
| 227 | error_ = -1; | ||
| 228 | state = JIS_ASCII; | ||
| 229 | } | ||
| 230 | } else if (*in == 0x0e) { | ||
| 231 | state = JIS_HWKANA; | ||
| 232 | in++; | ||
| 233 | } else if (*in == 0x0f) { | ||
| 234 | state = JIS_ASCII; | ||
| 235 | in++; | ||
| 236 | } else { | ||
| 237 | switch (state) { | ||
| 238 | case JIS_ASCII: | ||
| 239 | *out++ = *in++; | ||
| 240 | break; | ||
| 241 | case JIS_KANJI: | ||
| 242 | *out++ = *in++ | 0x80; | ||
| 243 | if (*in == '\0') break; | ||
| 244 | *out++ = *in++ | 0x80; | ||
| 245 | break; | ||
| 246 | case JIS_HWKANA: | ||
| 247 | *out++ = 0x8e; | ||
| 248 | *out++ = *in++ | 0x80; | ||
| 249 | break; | ||
| 250 | case JIS_AUXKANJI: | ||
| 251 | *out++ = 0x8f; | ||
| 252 | *out++ = *in++ | 0x80; | ||
| 253 | if (*in == '\0') break; | ||
| 254 | *out++ = *in++ | 0x80; | ||
| 255 | break; | ||
| 256 | default: | ||
| 257 | *out++ = *in++; | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | *out = '\0'; | ||
| 264 | |||
| 265 | if (error) | ||
| 266 | *error = error_; | ||
| 267 | |||
| 268 | return outbuf; | ||
| 269 | } | ||
| 270 | |||
| 271 | static gchar *conv_jistosjis(const gchar *inbuf, gint *error) | ||
| 272 | { | ||
| 273 | gchar *outbuf; | ||
| 274 | const guchar *in = (guchar *)inbuf; | ||
| 275 | guchar *out; | ||
| 276 | JISState state = JIS_ASCII; | ||
| 277 | gint error_ = 0; | ||
| 278 | |||
| 279 | outbuf = g_malloc(strlen(inbuf) * 2 + 1); | ||
| 280 | out = (guchar *)outbuf; | ||
| 281 | |||
| 282 | while (*in != '\0') { | ||
| 283 | if (*in == ESC) { | ||
| 284 | in++; | ||
| 285 | if (*in == '$') { | ||
| 286 | if (*(in + 1) == '@' || *(in + 1) == 'B') { | ||
| 287 | state = JIS_KANJI; | ||
| 288 | in += 2; | ||
| 289 | } else if (*(in + 1) == '(' && | ||
| 290 | *(in + 2) == '?') { | ||
| 291 | /* ISO-2022-JP-MS extention */ | ||
| 292 | state = JIS_UDC; | ||
| 293 | in += 3; | ||
| 294 | } else { | ||
| 295 | /* unknown escape sequence */ | ||
| 296 | error_ = -1; | ||
| 297 | state = JIS_ASCII; | ||
| 298 | } | ||
| 299 | } else if (*in == '(') { | ||
| 300 | if (*(in + 1) == 'B' || *(in + 1) == 'J') { | ||
| 301 | state = JIS_ASCII; | ||
| 302 | in += 2; | ||
| 303 | } else if (*(in + 1) == 'I') { | ||
| 304 | state = JIS_HWKANA; | ||
| 305 | in += 2; | ||
| 306 | } else { | ||
| 307 | /* unknown escape sequence */ | ||
| 308 | error_ = -1; | ||
| 309 | state = JIS_ASCII; | ||
| 310 | } | ||
| 311 | } else { | ||
| 312 | /* unknown escape sequence */ | ||
| 313 | error_ = -1; | ||
| 314 | state = JIS_ASCII; | ||
| 315 | } | ||
| 316 | } else if (*in == SO) { | ||
| 317 | state = JIS_HWKANA; | ||
| 318 | in++; | ||
| 319 | } else if (*in == SI) { | ||
| 320 | state = JIS_ASCII; | ||
| 321 | in++; | ||
| 322 | } else { | ||
| 323 | switch (state) { | ||
| 324 | case JIS_ASCII: | ||
| 325 | *out++ = *in++; | ||
| 326 | break; | ||
| 327 | case JIS_HWKANA: | ||
| 328 | *out++ = *in++ | 0x80; | ||
| 329 | break; | ||
| 330 | case JIS_KANJI: | ||
| 331 | if ((isjiskanji(*in) || | ||
| 332 | (*in >= 0x7f && *in <= 0x97)) && | ||
| 333 | isjiskanji(*(in + 1))) { | ||
| 334 | *out++ = ((*in < 0x5f) | ||
| 335 | ? (((*in - 0x21) / 2) + 0x81) | ||
| 336 | : (((*in - 0x21) / 2) + 0xc1)); | ||
| 337 | *out++ = ((*in % 2) | ||
| 338 | ? ((*(in + 1) + ((*(in + 1) < 0x60) | ||
| 339 | ? 0x1f : 0x20))) | ||
| 340 | : *(in + 1) + 0x7e); | ||
| 341 | in += 2; | ||
| 342 | } else { | ||
| 343 | error_ = -1; | ||
| 344 | *out++ = SUBST_CHAR; | ||
| 345 | in++; | ||
| 346 | if (*in != '\0') { | ||
| 347 | *out++ = SUBST_CHAR; | ||
| 348 | in++; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | break; | ||
| 352 | case JIS_UDC: | ||
| 353 | if (isjisudc(*in) && isjiskanji(*(in + 1))) { | ||
| 354 | *out++ = (((*in - 0x21) / 2) + 0xf0); | ||
| 355 | *out++ = ((*in % 2) | ||
| 356 | ? ((*(in + 1) + ((*(in + 1) < 0x60) | ||
| 357 | ? 0x1f : 0x20))) | ||
| 358 | : *(in + 1) + 0x7e); | ||
| 359 | in += 2; | ||
| 360 | } else { | ||
| 361 | error_ = -1; | ||
| 362 | *out++ = SUBST_CHAR; | ||
| 363 | in++; | ||
| 364 | if (*in != '\0') { | ||
| 365 | *out++ = SUBST_CHAR; | ||
| 366 | in++; | ||
| 367 | } | ||
| 368 | } | ||
| 369 | break; | ||
| 370 | default: | ||
| 371 | *out++ = *in++; | ||
| 372 | break; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | *out = '\0'; | ||
| 378 | |||
| 379 | if (error) | ||
| 380 | *error = error_; | ||
| 381 | |||
| 382 | return outbuf; | ||
| 383 | } | ||
| 384 | |||
| 385 | #define JIS_HWDAKUTEN 0x5e | ||
| 386 | #define JIS_HWHANDAKUTEN 0x5f | ||
| 387 | |||
| 388 | static gint conv_jis_hantozen(guchar *outbuf, guchar jis_code, guchar sound_sym) | ||
| 389 | { | ||
| 390 | static guint16 h2z_tbl[] = { | ||
| 391 | /* 0x20 - 0x2f */ | ||
| 392 | 0x0000, 0x2123, 0x2156, 0x2157, 0x2122, 0x2126, 0x2572, 0x2521, | ||
| 393 | 0x2523, 0x2525, 0x2527, 0x2529, 0x2563, 0x2565, 0x2567, 0x2543, | ||
| 394 | /* 0x30 - 0x3f */ | ||
| 395 | 0x213c, 0x2522, 0x2524, 0x2526, 0x2528, 0x252a, 0x252b, 0x252d, | ||
| 396 | 0x252f, 0x2531, 0x2533, 0x2535, 0x2537, 0x2539, 0x253b, 0x253d, | ||
| 397 | /* 0x40 - 0x4f */ | ||
| 398 | 0x253f, 0x2541, 0x2544, 0x2546, 0x2548, 0x254a, 0x254b, 0x254c, | ||
| 399 | 0x254d, 0x254e, 0x254f, 0x2552, 0x2555, 0x2558, 0x255b, 0x255e, | ||
| 400 | /* 0x50 - 0x5f */ | ||
| 401 | 0x255f, 0x2560, 0x2561, 0x2562, 0x2564, 0x2566, 0x2568, 0x2569, | ||
| 402 | 0x256a, 0x256b, 0x256c, 0x256d, 0x256f, 0x2573, 0x212b, 0x212c | ||
| 403 | }; | ||
| 404 | |||
| 405 | static guint16 dakuten_tbl[] = { | ||
| 406 | /* 0x30 - 0x3f */ | ||
| 407 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x252c, 0x252e, | ||
| 408 | 0x2530, 0x2532, 0x2534, 0x2536, 0x2538, 0x253a, 0x253c, 0x253e, | ||
| 409 | /* 0x40 - 0x4f */ | ||
| 410 | 0x2540, 0x2542, 0x2545, 0x2547, 0x2549, 0x0000, 0x0000, 0x0000, | ||
| 411 | 0x0000, 0x0000, 0x2550, 0x2553, 0x2556, 0x2559, 0x255c, 0x0000 | ||
| 412 | }; | ||
| 413 | |||
| 414 | static guint16 handakuten_tbl[] = { | ||
| 415 | /* 0x4a - 0x4e */ | ||
| 416 | 0x2551, 0x2554, 0x2557, 0x255a, 0x255d | ||
| 417 | }; | ||
| 418 | |||
| 419 | guint16 out_code; | ||
| 420 | |||
| 421 | jis_code &= 0x7f; | ||
| 422 | sound_sym &= 0x7f; | ||
| 423 | |||
| 424 | if (jis_code < 0x21 || jis_code > 0x5f) | ||
| 425 | return 0; | ||
| 426 | |||
| 427 | if (sound_sym == JIS_HWDAKUTEN && | ||
| 428 | jis_code >= 0x36 && jis_code <= 0x4e) { | ||
| 429 | out_code = dakuten_tbl[jis_code - 0x30]; | ||
| 430 | if (out_code != 0) { | ||
| 431 | *outbuf = out_code >> 8; | ||
| 432 | *(outbuf + 1) = out_code & 0xff; | ||
| 433 | return 2; | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | if (sound_sym == JIS_HWHANDAKUTEN && | ||
| 438 | jis_code >= 0x4a && jis_code <= 0x4e) { | ||
| 439 | out_code = handakuten_tbl[jis_code - 0x4a]; | ||
| 440 | *outbuf = out_code >> 8; | ||
| 441 | *(outbuf + 1) = out_code & 0xff; | ||
| 442 | return 2; | ||
| 443 | } | ||
| 444 | |||
| 445 | out_code = h2z_tbl[jis_code - 0x20]; | ||
| 446 | *outbuf = out_code >> 8; | ||
| 447 | *(outbuf + 1) = out_code & 0xff; | ||
| 448 | return 1; | ||
| 449 | } | ||
| 450 | |||
| 451 | static gchar *conv_euctojis(const gchar *inbuf, gint *error) | ||
| 452 | { | ||
| 453 | gchar *outbuf; | ||
| 454 | const guchar *in = (guchar *)inbuf; | ||
| 455 | guchar *out; | ||
| 456 | JISState state = JIS_ASCII; | ||
| 457 | gint error_ = 0; | ||
| 458 | |||
| 459 | outbuf = g_malloc(strlen(inbuf) * 3 + 4); | ||
| 460 | out = (guchar *)outbuf; | ||
| 461 | |||
| 462 | while (*in != '\0') { | ||
| 463 | if (isascii(*in)) { | ||
| 464 | K_OUT(); | ||
| 465 | *out++ = *in++; | ||
| 466 | } else if (iseuckanji(*in)) { | ||
| 467 | if (iseuckanji(*(in + 1))) { | ||
| 468 | K_IN(); | ||
| 469 | *out++ = *in++ & 0x7f; | ||
| 470 | *out++ = *in++ & 0x7f; | ||
| 471 | } else { | ||
| 472 | error_ = -1; | ||
| 473 | K_OUT(); | ||
| 474 | *out++ = SUBST_CHAR; | ||
| 475 | in++; | ||
| 476 | if (*in != '\0' && !isascii(*in)) { | ||
| 477 | *out++ = SUBST_CHAR; | ||
| 478 | in++; | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } else if (iseuchwkana1(*in)) { | ||
| 482 | if (iseuchwkana2(*(in + 1))) { | ||
| 483 | if (prefs_common.allow_jisx0201_kana) { | ||
| 484 | HW_IN(); | ||
| 485 | in++; | ||
| 486 | *out++ = *in++ & 0x7f; | ||
| 487 | } else { | ||
| 488 | guchar jis_ch[2]; | ||
| 489 | gint len; | ||
| 490 | |||
| 491 | if (iseuchwkana1(*(in + 2)) && | ||
| 492 | iseuchwkana2(*(in + 3))) | ||
| 493 | len = conv_jis_hantozen | ||
| 494 | (jis_ch, | ||
| 495 | *(in + 1), *(in + 3)); | ||
| 496 | else | ||
| 497 | len = conv_jis_hantozen | ||
| 498 | (jis_ch, | ||
| 499 | *(in + 1), '\0'); | ||
| 500 | if (len == 0) | ||
| 501 | in += 2; | ||
| 502 | else { | ||
| 503 | K_IN(); | ||
| 504 | in += len * 2; | ||
| 505 | *out++ = jis_ch[0]; | ||
| 506 | *out++ = jis_ch[1]; | ||
| 507 | } | ||
| 508 | } | ||
| 509 | } else { | ||
| 510 | error_ = -1; | ||
| 511 | K_OUT(); | ||
| 512 | in++; | ||
| 513 | if (*in != '\0' && !isascii(*in)) { | ||
| 514 | *out++ = SUBST_CHAR; | ||
| 515 | in++; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } else if (iseucaux(*in)) { | ||
| 519 | in++; | ||
| 520 | if (iseuckanji(*in) && iseuckanji(*(in + 1))) { | ||
| 521 | AUX_IN(); | ||
| 522 | *out++ = *in++ & 0x7f; | ||
| 523 | *out++ = *in++ & 0x7f; | ||
| 524 | } else { | ||
| 525 | error_ = -1; | ||
| 526 | K_OUT(); | ||
| 527 | if (*in != '\0' && !isascii(*in)) { | ||
| 528 | *out++ = SUBST_CHAR; | ||
| 529 | in++; | ||
| 530 | if (*in != '\0' && !isascii(*in)) { | ||
| 531 | *out++ = SUBST_CHAR; | ||
| 532 | in++; | ||
| 533 | } | ||
| 534 | } | ||
| 535 | } | ||
| 536 | } else { | ||
| 537 | error_ = -1; | ||
| 538 | K_OUT(); | ||
| 539 | *out++ = SUBST_CHAR; | ||
| 540 | in++; | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | K_OUT(); | ||
| 545 | *out = '\0'; | ||
| 546 | |||
| 547 | if (error) | ||
| 548 | *error = error_; | ||
| 549 | |||
| 550 | return outbuf; | ||
| 551 | } | ||
| 552 | |||
| 553 | #define sjistoidx(c1, c2) \ | ||
| 554 | (((c1) > 0x9f) \ | ||
| 555 | ? (((c1) - 0xc1) * 188 + (c2) - (((c2) > 0x7e) ? 0x41 : 0x40)) \ | ||
| 556 | : (((c1) - 0x81) * 188 + (c2) - (((c2) > 0x7e) ? 0x41 : 0x40))) | ||
| 557 | #define idxtojis1(c) (((c) / 94) + 0x21) | ||
| 558 | #define idxtojis2(c) (((c) % 94) + 0x21) | ||
| 559 | |||
| 560 | static guint conv_idx_ibmtonec(guint idx) | ||
| 561 | { | ||
| 562 | if (idx >= sjistoidx(0xfa, 0x5c)) | ||
| 563 | idx -= sjistoidx(0xfa, 0x5c) | ||
| 564 | - sjistoidx(0xed, 0x40); | ||
| 565 | /* else if (idx == sjistoidx(0xfa, 0x5b)) */ | ||
| 566 | /* idx = sjistoidx(0x81, 0xe6); */ | ||
| 567 | /* else if (idx == sjistoidx(0xfa, 0x5a)) */ | ||
| 568 | /* idx = sjistoidx(0x87, 0x84); */ | ||
| 569 | /* else if (idx == sjistoidx(0xfa, 0x59)) */ | ||
| 570 | /* idx = sjistoidx(0x87, 0x82); */ | ||
| 571 | /* else if (idx == sjistoidx(0xfa, 0x58)) */ | ||
| 572 | /* idx = sjistoidx(0x87, 0x8a); */ | ||
| 573 | else if (idx >= sjistoidx(0xfa, 0x55)) | ||
| 574 | idx -= sjistoidx(0xfa, 0x55) | ||
| 575 | - sjistoidx(0xee, 0xfa); | ||
| 576 | /* else if (idx == sjistoidx(0xfa, 0x54)) */ | ||
| 577 | /* idx = sjistoidx(0x81, 0xca); */ | ||
| 578 | /* else if (idx >= sjistoidx(0xfa, 0x4a)) */ | ||
| 579 | /* idx -= sjistoidx(0xfa, 0x4a) */ | ||
| 580 | /* - sjistoidx(0x87, 0x54); */ | ||
| 581 | else if (idx >= sjistoidx(0xfa, 0x40)) | ||
| 582 | idx -= sjistoidx(0xfa, 0x40) | ||
| 583 | - sjistoidx(0xee, 0xef); | ||
| 584 | return idx; | ||
| 585 | } | ||
| 586 | |||
| 587 | static gchar *conv_sjistojis(const gchar *inbuf, gint *error) | ||
| 588 | { | ||
| 589 | gchar *outbuf; | ||
| 590 | const guchar *in = (guchar *)inbuf; | ||
| 591 | guchar *out; | ||
| 592 | JISState state = JIS_ASCII; | ||
| 593 | gint error_ = 0; | ||
| 594 | guint idx; | ||
| 595 | |||
| 596 | outbuf = g_malloc(strlen(inbuf) * 5 + 4); | ||
| 597 | out = (guchar *)outbuf; | ||
| 598 | |||
| 599 | while (*in != '\0') { | ||
| 600 | if (isascii(*in)) { | ||
| 601 | K_OUT(); | ||
| 602 | *out++ = *in++; | ||
| 603 | } else if (issjiskanji1(*in)) { | ||
| 604 | if (issjiskanji2(*(in + 1))) { | ||
| 605 | K_IN(); | ||
| 606 | idx = sjistoidx(*in, *(in + 1)); | ||
| 607 | *out++ = idxtojis1(idx); | ||
| 608 | *out++ = idxtojis2(idx); | ||
| 609 | in += 2; | ||
| 610 | } else { | ||
| 611 | error_ = -1; | ||
| 612 | K_OUT(); | ||
| 613 | *out++ = SUBST_CHAR; | ||
| 614 | in++; | ||
| 615 | if (*in != '\0' && !isascii(*in)) { | ||
| 616 | *out++ = SUBST_CHAR; | ||
| 617 | in++; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | } else if (issjishwkana(*in)) { | ||
| 621 | if (prefs_common.allow_jisx0201_kana) { | ||
| 622 | HW_IN(); | ||
| 623 | *out++ = *in++ & 0x7f; | ||
| 624 | } else { | ||
| 625 | guchar jis_ch[2]; | ||
| 626 | gint len; | ||
| 627 | |||
| 628 | if (issjishwkana(*(in + 1))) | ||
| 629 | len = conv_jis_hantozen | ||
| 630 | (jis_ch, | ||
| 631 | *in, *(in + 1)); | ||
| 632 | else | ||
| 633 | len = conv_jis_hantozen | ||
| 634 | (jis_ch, | ||
| 635 | *in, '\0'); | ||
| 636 | if (len == 0) | ||
| 637 | in++; | ||
| 638 | else { | ||
| 639 | K_IN(); | ||
| 640 | in += len; | ||
| 641 | *out++ = jis_ch[0]; | ||
| 642 | *out++ = jis_ch[1]; | ||
| 643 | } | ||
| 644 | } | ||
| 645 | } else if (issjisibmext(*in, *(in + 1))) { | ||
| 646 | K_IN(); | ||
| 647 | idx = sjistoidx(*in, *(in + 1)); | ||
| 648 | idx = conv_idx_ibmtonec(idx); | ||
| 649 | *out++ = idxtojis1(idx); | ||
| 650 | *out++ = idxtojis2(idx); | ||
| 651 | in += 2; | ||
| 652 | #if 0 | ||
| 653 | } else if (issjisudc(*in)) { | ||
| 654 | UDC_IN(); | ||
| 655 | idx = sjistoidx(*in, *(in + 1)) | ||
| 656 | - sjistoidx(0xf0, 0x40); | ||
| 657 | *out++ = idxtojis1(idx); | ||
| 658 | *out++ = idxtojis2(idx); | ||
| 659 | in += 2; | ||
| 660 | #endif | ||
| 661 | } else if (issjisext(*in)) { | ||
| 662 | error_ = -1; | ||
| 663 | K_OUT(); | ||
| 664 | *out++ = SUBST_CHAR; | ||
| 665 | in++; | ||
| 666 | if (*in != '\0' && !isascii(*in)) { | ||
| 667 | *out++ = SUBST_CHAR; | ||
| 668 | in++; | ||
| 669 | } | ||
| 670 | } else { | ||
| 671 | error_ = -1; | ||
| 672 | K_OUT(); | ||
| 673 | *out++ = SUBST_CHAR; | ||
| 674 | in++; | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | K_OUT(); | ||
| 679 | *out = '\0'; | ||
| 680 | |||
| 681 | if (error) | ||
| 682 | *error = error_; | ||
| 683 | |||
| 684 | return outbuf; | ||
| 685 | } | ||
| 686 | |||
| 687 | static gchar *conv_sjistoeuc(const gchar *inbuf, gint *error) | ||
| 688 | { | ||
| 689 | gchar *outbuf; | ||
| 690 | const guchar *in = (guchar *)inbuf; | ||
| 691 | guchar *out; | ||
| 692 | gint error_ = 0; | ||
| 693 | |||
| 694 | outbuf = g_malloc(strlen(inbuf) * 2 + 1); | ||
| 695 | out = (guchar *)outbuf; | ||
| 696 | |||
| 697 | while (*in != '\0') { | ||
| 698 | if (isascii(*in)) { | ||
| 699 | *out++ = *in++; | ||
| 700 | } else if (issjiskanji1(*in)) { | ||
| 701 | if (issjiskanji2(*(in + 1))) { | ||
| 702 | guchar out1 = *in; | ||
| 703 | guchar out2 = *(in + 1); | ||
| 704 | guchar row; | ||
| 705 | |||
| 706 | row = out1 < 0xa0 ? 0x70 : 0xb0; | ||
| 707 | if (out2 < 0x9f) { | ||
| 708 | out1 = (out1 - row) * 2 - 1; | ||
| 709 | out2 -= out2 > 0x7f ? 0x20 : 0x1f; | ||
| 710 | } else { | ||
| 711 | out1 = (out1 - row) * 2; | ||
| 712 | out2 -= 0x7e; | ||
| 713 | } | ||
| 714 | |||
| 715 | *out++ = out1 | 0x80; | ||
| 716 | *out++ = out2 | 0x80; | ||
| 717 | in += 2; | ||
| 718 | } else { | ||
| 719 | error_ = -1; | ||
| 720 | *out++ = SUBST_CHAR; | ||
| 721 | in++; | ||
| 722 | if (*in != '\0' && !isascii(*in)) { | ||
| 723 | *out++ = SUBST_CHAR; | ||
| 724 | in++; | ||
| 725 | } | ||
| 726 | } | ||
| 727 | } else if (issjishwkana(*in)) { | ||
| 728 | *out++ = SS2; | ||
| 729 | *out++ = *in++; | ||
| 730 | } else if (issjisext(*in)) { | ||
| 731 | error_ = -1; | ||
| 732 | *out++ = SUBST_CHAR; | ||
| 733 | in++; | ||
| 734 | if (*in != '\0' && !isascii(*in)) { | ||
| 735 | *out++ = SUBST_CHAR; | ||
| 736 | in++; | ||
| 737 | } | ||
| 738 | } else { | ||
| 739 | error_ = -1; | ||
| 740 | *out++ = SUBST_CHAR; | ||
| 741 | in++; | ||
| 742 | } | ||
| 743 | } | ||
| 744 | |||
| 745 | *out = '\0'; | ||
| 746 | |||
| 747 | if (error) | ||
| 748 | *error = error_; | ||
| 749 | |||
| 750 | return outbuf; | ||
| 751 | } | ||
| 752 | |||
| 753 | static gchar *conv_jistoutf8(const gchar *inbuf, gint *error) | ||
| 754 | { | ||
| 755 | gchar *tmpstr, *utf8str; | ||
| 756 | gint t_error = 0, u_error = 0; | ||
| 757 | |||
| 758 | if (strstr(inbuf, "\033$(D")) { | ||
| 759 | tmpstr = conv_jistoeuc(inbuf, &t_error); | ||
| 760 | utf8str = conv_euctoutf8(tmpstr, &u_error); | ||
| 761 | } else { | ||
| 762 | tmpstr = conv_jistosjis(inbuf, &t_error); | ||
| 763 | utf8str = conv_sjistoutf8(tmpstr, &u_error); | ||
| 764 | } | ||
| 765 | g_free(tmpstr); | ||
| 766 | |||
| 767 | if (error) | ||
| 768 | *error = (t_error | u_error); | ||
| 769 | |||
| 770 | return utf8str; | ||
| 771 | } | ||
| 772 | |||
| 773 | #if USE_THREADS | ||
| 774 | #define S_LOCK_DEFINE_STATIC(name) G_LOCK_DEFINE_STATIC(name) | ||
| 775 | #define S_LOCK(name) G_LOCK(name) | ||
| 776 | #define S_UNLOCK(name) G_UNLOCK(name) | ||
| 777 | #else | ||
| 778 | #define S_LOCK_DEFINE_STATIC(name) | ||
| 779 | #define S_LOCK(name) | ||
| 780 | #define S_UNLOCK(name) | ||
| 781 | #endif | ||
| 782 | |||
| 783 | static gchar *conv_sjistoutf8(const gchar *inbuf, gint *error) | ||
| 784 | { | ||
| 785 | static iconv_t cd = (iconv_t)-1; | ||
| 786 | static gboolean iconv_ok = TRUE; | ||
| 787 | S_LOCK_DEFINE_STATIC(cd); | ||
| 788 | gchar *ret; | ||
| 789 | |||
| 790 | S_LOCK(cd); | ||
| 791 | |||
| 792 | if (cd == (iconv_t)-1) { | ||
| 793 | if (!iconv_ok) { | ||
| 794 | S_UNLOCK(cd); | ||
| 795 | if (error) | ||
| 796 | *error = -1; | ||
| 797 | return g_strdup(inbuf); | ||
| 798 | } | ||
| 799 | |||
| 800 | cd = iconv_open(CS_UTF_8, CS_CP932); | ||
| 801 | if (cd == (iconv_t)-1) { | ||
| 802 | cd = iconv_open(CS_UTF_8, CS_SHIFT_JIS); | ||
| 803 | if (cd == (iconv_t)-1) { | ||
| 804 | g_warning("conv_sjistoutf8(): %s\n", | ||
| 805 | g_strerror(errno)); | ||
| 806 | iconv_ok = FALSE; | ||
| 807 | S_UNLOCK(cd); | ||
| 808 | if (error) | ||
| 809 | *error = -1; | ||
| 810 | return g_strdup(inbuf); | ||
| 811 | } | ||
| 812 | } | ||
| 813 | } | ||
| 814 | |||
| 815 | ret = conv_iconv_strdup_with_cd(inbuf, cd, error); | ||
| 816 | S_UNLOCK(cd); | ||
| 817 | return ret; | ||
| 818 | } | ||
| 819 | |||
| 820 | static gchar *conv_euctoutf8(const gchar *inbuf, gint *error) | ||
| 821 | { | ||
| 822 | static iconv_t cd = (iconv_t)-1; | ||
| 823 | static gboolean iconv_ok = TRUE; | ||
| 824 | S_LOCK_DEFINE_STATIC(cd); | ||
| 825 | gchar *ret; | ||
| 826 | |||
| 827 | S_LOCK(cd); | ||
| 828 | |||
| 829 | if (cd == (iconv_t)-1) { | ||
| 830 | if (!iconv_ok) { | ||
| 831 | S_UNLOCK(cd); | ||
| 832 | if (error) | ||
| 833 | *error = -1; | ||
| 834 | return g_strdup(inbuf); | ||
| 835 | } | ||
| 836 | |||
| 837 | cd = iconv_open(CS_UTF_8, CS_EUC_JP_MS); | ||
| 838 | if (cd == (iconv_t)-1) { | ||
| 839 | cd = iconv_open(CS_UTF_8, CS_EUC_JP); | ||
| 840 | if (cd == (iconv_t)-1) { | ||
| 841 | g_warning("conv_euctoutf8(): %s\n", | ||
| 842 | g_strerror(errno)); | ||
| 843 | iconv_ok = FALSE; | ||
| 844 | S_UNLOCK(cd); | ||
| 845 | if (error) | ||
| 846 | *error = -1; | ||
| 847 | return g_strdup(inbuf); | ||
| 848 | } | ||
| 849 | } | ||
| 850 | } | ||
| 851 | |||
| 852 | ret = conv_iconv_strdup_with_cd(inbuf, cd, error); | ||
| 853 | S_UNLOCK(cd); | ||
| 854 | return ret; | ||
| 855 | } | ||
| 856 | |||
| 857 | static gchar *conv_anytoutf8(const gchar *inbuf, gint *error) | ||
| 858 | { | ||
| 859 | switch (conv_guess_ja_encoding(inbuf)) { | ||
| 860 | case C_ISO_2022_JP: | ||
| 861 | return conv_jistoutf8(inbuf, error); | ||
| 862 | case C_SHIFT_JIS: | ||
| 863 | return conv_sjistoutf8(inbuf, error); | ||
| 864 | case C_EUC_JP: | ||
| 865 | return conv_euctoutf8(inbuf, error); | ||
| 866 | case C_UTF_8: | ||
| 867 | if (error) | ||
| 868 | *error = 0; | ||
| 869 | if (isutf8bom(inbuf)) | ||
| 870 | inbuf += 3; | ||
| 871 | return g_strdup(inbuf); | ||
| 872 | default: | ||
| 873 | if (error) | ||
| 874 | *error = 0; | ||
| 875 | return g_strdup(inbuf); | ||
| 876 | } | ||
| 877 | } | ||
| 878 | |||
| 879 | static gchar *conv_utf8tosjis(const gchar *inbuf, gint *error) | ||
| 880 | { | ||
| 881 | static iconv_t cd = (iconv_t)-1; | ||
| 882 | static gboolean iconv_ok = TRUE; | ||
| 883 | S_LOCK_DEFINE_STATIC(cd); | ||
| 884 | gchar *ret; | ||
| 885 | |||
| 886 | S_LOCK(cd); | ||
| 887 | |||
| 888 | if (cd == (iconv_t)-1) { | ||
| 889 | if (!iconv_ok) { | ||
| 890 | S_UNLOCK(cd); | ||
| 891 | if (error) | ||
| 892 | *error = -1; | ||
| 893 | return g_strdup(inbuf); | ||
| 894 | } | ||
| 895 | |||
| 896 | cd = iconv_open(CS_CP932, CS_UTF_8); | ||
| 897 | if (cd == (iconv_t)-1) { | ||
| 898 | cd = iconv_open(CS_SHIFT_JIS, CS_UTF_8); | ||
| 899 | if (cd == (iconv_t)-1) { | ||
| 900 | g_warning("conv_utf8tosjis(): %s\n", | ||
| 901 | g_strerror(errno)); | ||
| 902 | iconv_ok = FALSE; | ||
| 903 | S_UNLOCK(cd); | ||
| 904 | if (error) | ||
| 905 | *error = -1; | ||
| 906 | return g_strdup(inbuf); | ||
| 907 | } | ||
| 908 | } | ||
| 909 | } | ||
| 910 | |||
| 911 | if (isutf8bom(inbuf)) | ||
| 912 | inbuf += 3; | ||
| 913 | ret = conv_iconv_strdup_with_cd(inbuf, cd, error); | ||
| 914 | S_UNLOCK(cd); | ||
| 915 | return ret; | ||
| 916 | } | ||
| 917 | |||
| 918 | static gchar *conv_utf8toeuc(const gchar *inbuf, gint *error) | ||
| 919 | { | ||
| 920 | static iconv_t cd = (iconv_t)-1; | ||
| 921 | static gboolean iconv_ok = TRUE; | ||
| 922 | S_LOCK_DEFINE_STATIC(cd); | ||
| 923 | gchar *ret; | ||
| 924 | |||
| 925 | S_LOCK(cd); | ||
| 926 | |||
| 927 | if (cd == (iconv_t)-1) { | ||
| 928 | if (!iconv_ok) { | ||
| 929 | S_UNLOCK(cd); | ||
| 930 | if (error) | ||
| 931 | *error = -1; | ||
| 932 | return g_strdup(inbuf); | ||
| 933 | } | ||
| 934 | |||
| 935 | cd = iconv_open(CS_EUC_JP_MS, CS_UTF_8); | ||
| 936 | if (cd == (iconv_t)-1) { | ||
| 937 | cd = iconv_open(CS_EUC_JP, CS_UTF_8); | ||
| 938 | if (cd == (iconv_t)-1) { | ||
| 939 | g_warning("conv_utf8toeuc(): %s\n", | ||
| 940 | g_strerror(errno)); | ||
| 941 | iconv_ok = FALSE; | ||
| 942 | S_UNLOCK(cd); | ||
| 943 | if (error) | ||
| 944 | *error = -1; | ||
| 945 | return g_strdup(inbuf); | ||
| 946 | } | ||
| 947 | } | ||
| 948 | } | ||
| 949 | |||
| 950 | if (isutf8bom(inbuf)) | ||
| 951 | inbuf += 3; | ||
| 952 | ret = conv_iconv_strdup_with_cd(inbuf, cd, error); | ||
| 953 | S_UNLOCK(cd); | ||
| 954 | return ret; | ||
| 955 | } | ||
| 956 | |||
| 957 | static gchar *conv_utf8tojis(const gchar *inbuf, gint *error) | ||
| 958 | { | ||
| 959 | gchar *tmpstr, *jisstr; | ||
| 960 | gint t_error = 0, j_error = 0; | ||
| 961 | |||
| 962 | #if 1 | ||
| 963 | tmpstr = conv_utf8tosjis(inbuf, &t_error); | ||
| 964 | jisstr = conv_sjistojis(tmpstr, &j_error); | ||
| 965 | #else | ||
| 966 | tmpstr = conv_utf8toeuc(inbuf, &t_error); | ||
| 967 | jisstr = conv_euctojis(tmpstr, &j_error); | ||
| 968 | #endif | ||
| 969 | g_free(tmpstr); | ||
| 970 | |||
| 971 | if (error) | ||
| 972 | *error = (t_error | j_error); | ||
| 973 | |||
| 974 | return jisstr; | ||
| 975 | } | ||
| 976 | |||
| 977 | #if 0 | ||
| 978 | static gchar valid_eucjp_tbl[][96] = { | ||
| 979 | /* 0xa2a0 - 0xa2ff */ | ||
| 980 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | ||
| 981 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, | ||
| 982 | 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, | ||
| 983 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, | ||
| 984 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, | ||
| 985 | 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0 }, | ||
| 986 | |||
| 987 | /* 0xa3a0 - 0xa3ff */ | ||
| 988 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 989 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, | ||
| 990 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 991 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, | ||
| 992 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 993 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, | ||
| 994 | |||
| 995 | /* 0xa4a0 - 0xa4ff */ | ||
| 996 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 997 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 998 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 999 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1000 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1001 | 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | ||
| 1002 | |||
| 1003 | /* 0xa5a0 - 0xa5ff */ | ||
| 1004 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1005 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1006 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1007 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1008 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1009 | 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | ||
| 1010 | |||
| 1011 | /* 0xa6a0 - 0xa6ff */ | ||
| 1012 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1013 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | ||
| 1014 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1015 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | ||
| 1016 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1017 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | ||
| 1018 | |||
| 1019 | /* 0xa7a0 - 0xa7ff */ | ||
| 1020 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1021 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1022 | 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1023 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1024 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1025 | 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | ||
| 1026 | |||
| 1027 | /* 0xa8a0 - 0xa8ff */ | ||
| 1028 | { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1029 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | ||
| 1030 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1031 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1032 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 1033 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | ||
| 1034 | }; | ||
| 1035 | |||
| 1036 | static gboolean isprintableeuckanji(guchar c1, guchar c2) | ||
| 1037 | { | ||
| 1038 | if (c1 <= 0xa0 || c1 >= 0xf5) | ||
| 1039 | return FALSE; | ||
| 1040 | if (c2 <= 0xa0 || c2 == 0xff) | ||
| 1041 | return FALSE; | ||
| 1042 | |||
| 1043 | if (c1 >= 0xa9 && c1 <= 0xaf) | ||
| 1044 | return FALSE; | ||
| 1045 | |||
| 1046 | if (c1 >= 0xa2 && c1 <= 0xa8) | ||
| 1047 | return (gboolean)valid_eucjp_tbl[c1 - 0xa2][c2 - 0xa0]; | ||
| 1048 | |||
| 1049 | if (c1 == 0xcf) { | ||
| 1050 | if (c2 >= 0xd4 && c2 <= 0xfe) | ||
| 1051 | return FALSE; | ||
| 1052 | } else if (c1 == 0xf4) { | ||
| 1053 | if (c2 >= 0xa7 && c2 <= 0xfe) | ||
| 1054 | return FALSE; | ||
| 1055 | } | ||
| 1056 | |||
| 1057 | return TRUE; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | static void conv_unreadable_eucjp(gchar *str) | ||
| 1061 | { | ||
| 1062 | register guchar *p = str; | ||
| 1063 | |||
| 1064 | while (*p != '\0') { | ||
| 1065 | if (isascii(*p)) { | ||
| 1066 | /* convert CR+LF -> LF */ | ||
| 1067 | if (*p == '\r' && *(p + 1) == '\n') | ||
| 1068 | memmove(p, p + 1, strlen(p)); | ||
| 1069 | /* printable 7 bit code */ | ||
| 1070 | p++; | ||
| 1071 | } else if (iseuckanji(*p)) { | ||
| 1072 | if (isprintableeuckanji(*p, *(p + 1))) { | ||
| 1073 | /* printable euc-jp code */ | ||
| 1074 | p += 2; | ||
| 1075 | } else { | ||
| 1076 | /* substitute unprintable code */ | ||
| 1077 | *p++ = SUBST_CHAR; | ||
| 1078 | if (*p != '\0') { | ||
| 1079 | if (isascii(*p)) | ||
| 1080 | p++; | ||
| 1081 | else | ||
| 1082 | *p++ = SUBST_CHAR; | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | } else if (iseuchwkana1(*p)) { | ||
| 1086 | if (iseuchwkana2(*(p + 1))) | ||
| 1087 | /* euc-jp hankaku kana */ | ||
| 1088 | p += 2; | ||
| 1089 | else | ||
| 1090 | *p++ = SUBST_CHAR; | ||
| 1091 | } else if (iseucaux(*p)) { | ||
| 1092 | if (iseuckanji(*(p + 1)) && iseuckanji(*(p + 2))) { | ||
| 1093 | /* auxiliary kanji */ | ||
| 1094 | p += 3; | ||
| 1095 | } else | ||
| 1096 | *p++ = SUBST_CHAR; | ||
| 1097 | } else | ||
| 1098 | /* substitute unprintable 1 byte code */ | ||
| 1099 | *p++ = SUBST_CHAR; | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | #endif | ||
| 1103 | |||
| 1104 | static void conv_unreadable_8bit(gchar *str) | ||
| 1105 | { | ||
| 1106 | register gchar *p = str; | ||
| 1107 | |||
| 1108 | while (*p != '\0') { | ||
| 1109 | /* convert CR+LF -> LF */ | ||
| 1110 | if (*p == '\r' && *(p + 1) == '\n') | ||
| 1111 | memmove(p, p + 1, strlen(p)); | ||
| 1112 | else if (!isascii(*(guchar *)p)) *p = SUBST_CHAR; | ||
| 1113 | p++; | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | #if 0 | ||
| 1118 | static void conv_unreadable_latin(gchar *str) | ||
| 1119 | { | ||
| 1120 | register guchar *p = str; | ||
| 1121 | |||
| 1122 | while (*p != '\0') { | ||
| 1123 | /* convert CR+LF -> LF */ | ||
| 1124 | if (*p == '\r' && *(p + 1) == '\n') | ||
| 1125 | memmove(p, p + 1, strlen(p)); | ||
| 1126 | else if ((*p & 0xff) >= 0x7f && (*p & 0xff) <= 0x9f) | ||
| 1127 | *p = SUBST_CHAR; | ||
| 1128 | p++; | ||
| 1129 | } | ||
| 1130 | } | ||
| 1131 | #endif | ||
| 1132 | |||
| 1133 | #define NCV '\0' | ||
| 1134 | |||
| 1135 | void conv_mb_alnum(gchar *str) | ||
| 1136 | { | ||
| 1137 | static guchar char_tbl[] = { | ||
| 1138 | /* 0xa0 - 0xaf */ | ||
| 1139 | NCV, ' ', NCV, NCV, ',', '.', NCV, ':', | ||
| 1140 | ';', '?', '!', NCV, NCV, NCV, NCV, NCV, | ||
| 1141 | /* 0xb0 - 0xbf */ | ||
| 1142 | NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, | ||
| 1143 | NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, | ||
| 1144 | /* 0xc0 - 0xcf */ | ||
| 1145 | NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV, | ||
| 1146 | NCV, NCV, '(', ')', NCV, NCV, '[', ']', | ||
| 1147 | /* 0xd0 - 0xdf */ | ||
| 1148 | '{', '}', NCV, NCV, NCV, NCV, NCV, NCV, | ||
| 1149 | NCV, NCV, NCV, NCV, '+', '-', NCV, NCV, | ||
| 1150 | /* 0xe0 - 0xef */ | ||
| 1151 | NCV, '=', NCV, '<', '>', NCV, NCV, NCV, | ||
| 1152 | NCV, NCV, NCV, NCV, NCV, NCV, NCV, NCV | ||
| 1153 | }; | ||
| 1154 | |||
| 1155 | register guchar *p = (guchar *)str; | ||
| 1156 | register gint len; | ||
| 1157 | |||
| 1158 | len = strlen(str); | ||
| 1159 | |||
| 1160 | while (len > 1) { | ||
| 1161 | if (*p == 0xa3) { | ||
| 1162 | register guchar ch = *(p + 1); | ||
| 1163 | |||
| 1164 | if (ch >= 0xb0 && ch <= 0xfa) { | ||
| 1165 | /* [a-zA-Z] */ | ||
| 1166 | *p = ch & 0x7f; | ||
| 1167 | p++; | ||
| 1168 | len--; | ||
| 1169 | memmove(p, p + 1, len); | ||
| 1170 | len--; | ||
| 1171 | } else { | ||
| 1172 | p += 2; | ||
| 1173 | len -= 2; | ||
| 1174 | } | ||
| 1175 | } else if (*p == 0xa1) { | ||
| 1176 | register guchar ch = *(p + 1); | ||
| 1177 | |||
| 1178 | if (ch >= 0xa0 && ch <= 0xef && | ||
| 1179 | NCV != char_tbl[ch - 0xa0]) { | ||
| 1180 | *p = char_tbl[ch - 0xa0]; | ||
| 1181 | p++; | ||
| 1182 | len--; | ||
| 1183 | memmove(p, p + 1, len); | ||
| 1184 | len--; | ||
| 1185 | } else { | ||
| 1186 | p += 2; | ||
| 1187 | len -= 2; | ||
| 1188 | } | ||
| 1189 | } else if (iseuckanji(*p)) { | ||
| 1190 | p += 2; | ||
| 1191 | len -= 2; | ||
| 1192 | } else { | ||
| 1193 | p++; | ||
| 1194 | len--; | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | CharSet conv_guess_ja_encoding(const gchar *str) | ||
| 1200 | { | ||
| 1201 | const guchar *p = (const guchar *)str; | ||
| 1202 | CharSet guessed = C_US_ASCII; | ||
| 1203 | |||
| 1204 | while (*p != '\0') { | ||
| 1205 | if (*p == ESC && (*(p + 1) == '$' || *(p + 1) == '(')) { | ||
| 1206 | if (guessed == C_US_ASCII) | ||
| 1207 | return C_ISO_2022_JP; | ||
| 1208 | p += 2; | ||
| 1209 | } else if (isascii(*p)) { | ||
| 1210 | p++; | ||
| 1211 | } else if (iseuckanji(*p) && iseuckanji(*(p + 1))) { | ||
| 1212 | if (*p >= 0xfd && *p <= 0xfe) | ||
| 1213 | return C_EUC_JP; | ||
| 1214 | else if (guessed == C_SHIFT_JIS) { | ||
| 1215 | if ((issjiskanji1(*p) && | ||
| 1216 | issjiskanji2(*(p + 1))) || | ||
| 1217 | issjishwkana(*p)) | ||
| 1218 | guessed = C_SHIFT_JIS; | ||
| 1219 | else | ||
| 1220 | guessed = C_EUC_JP; | ||
| 1221 | } else | ||
| 1222 | guessed = C_EUC_JP; | ||
| 1223 | p += 2; | ||
| 1224 | } else if (issjiskanji1(*p) && issjiskanji2(*(p + 1))) { | ||
| 1225 | guessed = C_SHIFT_JIS; | ||
| 1226 | p += 2; | ||
| 1227 | } else if (issjishwkana(*p)) { | ||
| 1228 | guessed = C_SHIFT_JIS; | ||
| 1229 | p++; | ||
| 1230 | } else { | ||
| 1231 | if (guessed == C_US_ASCII) | ||
| 1232 | guessed = C_AUTO; | ||
| 1233 | p++; | ||
| 1234 | } | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | if (guessed != C_US_ASCII) { | ||
| 1238 | p = (const guchar *)str; | ||
| 1239 | |||
| 1240 | while (*p != '\0') { | ||
| 1241 | if (isascii(*p)) { | ||
| 1242 | p++; | ||
| 1243 | } else if (isutf8_3_1(*p) && | ||
| 1244 | isutf8_3_2(*(p + 1)) && | ||
| 1245 | isutf8_3_2(*(p + 2))) { | ||
| 1246 | p += 3; | ||
| 1247 | } else { | ||
| 1248 | return guessed; | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | return C_UTF_8; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | return guessed; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | static gchar *conv_jistodisp(const gchar *inbuf, gint *error) | ||
| 1259 | { | ||
| 1260 | return conv_jistoutf8(inbuf, error); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | static gchar *conv_sjistodisp(const gchar *inbuf, gint *error) | ||
| 1264 | { | ||
| 1265 | return conv_sjistoutf8(inbuf, error); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | static gchar *conv_euctodisp(const gchar *inbuf, gint *error) | ||
| 1269 | { | ||
| 1270 | return conv_euctoutf8(inbuf, error); | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | gchar *conv_utf8todisp(const gchar *inbuf, gint *error) | ||
| 1274 | { | ||
| 1275 | if (g_utf8_validate(inbuf, -1, NULL) == TRUE) { | ||
| 1276 | if (error) | ||
| 1277 | *error = 0; | ||
| 1278 | if (isutf8bom(inbuf)) | ||
| 1279 | inbuf += 3; | ||
| 1280 | return g_strdup(inbuf); | ||
| 1281 | } else | ||
| 1282 | return conv_ustodisp(inbuf, error); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | static gchar *conv_anytodisp(const gchar *inbuf, gint *error) | ||
| 1286 | { | ||
| 1287 | gchar *outbuf; | ||
| 1288 | |||
| 1289 | outbuf = conv_anytoutf8(inbuf, error); | ||
| 1290 | if (g_utf8_validate(outbuf, -1, NULL) != TRUE) { | ||
| 1291 | if (error) | ||
| 1292 | *error = -1; | ||
| 1293 | conv_unreadable_8bit(outbuf); | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | return outbuf; | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | static gchar *conv_ustodisp(const gchar *inbuf, gint *error) | ||
| 1300 | { | ||
| 1301 | gchar *outbuf; | ||
| 1302 | |||
| 1303 | outbuf = g_strdup(inbuf); | ||
| 1304 | conv_unreadable_8bit(outbuf); | ||
| 1305 | if (error) | ||
| 1306 | *error = 0; | ||
| 1307 | |||
| 1308 | return outbuf; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | gchar *conv_localetodisp(const gchar *inbuf, gint *error) | ||
| 1312 | { | ||
| 1313 | gchar *str; | ||
| 1314 | |||
| 1315 | str = conv_iconv_strdup(inbuf, conv_get_locale_charset_str(), | ||
| 1316 | CS_INTERNAL, error); | ||
| 1317 | if (!str) | ||
| 1318 | str = conv_utf8todisp(inbuf, NULL); | ||
| 1319 | |||
| 1320 | return str; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | static gchar *conv_noconv(const gchar *inbuf, gint *error) | ||
| 1324 | { | ||
| 1325 | if (error) | ||
| 1326 | *error = 0; | ||
| 1327 | return g_strdup(inbuf); | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | static const gchar * | ||
| 1331 | conv_get_fallback_for_private_encoding(const gchar *encoding) | ||
| 1332 | { | ||
| 1333 | if (encoding) { | ||
| 1334 | if ((encoding[0] == 'X' || encoding[0] == 'x') && | ||
| 1335 | encoding[1] == '-') { | ||
| 1336 | if (!g_ascii_strcasecmp(encoding, CS_X_GBK)) | ||
| 1337 | return CS_GBK; | ||
| 1338 | else if (!g_ascii_strcasecmp(encoding, CS_X_SJIS)) | ||
| 1339 | return CS_SHIFT_JIS; | ||
| 1340 | } else if ((encoding[0] == 'K' || encoding[0] == 'k') && | ||
| 1341 | (encoding[1] == 'S' || encoding[1] == 's')) { | ||
| 1342 | if (!g_ascii_strcasecmp(encoding, CS_KS_C_5601_1987)) | ||
| 1343 | return CS_EUC_KR; | ||
| 1344 | } | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | return encoding; | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | CodeConverter *conv_code_converter_new(const gchar *src_encoding, | ||
| 1351 | const gchar *dest_encoding) | ||
| 1352 | { | ||
| 1353 | CodeConverter *conv; | ||
| 1354 | |||
| 1355 | src_encoding = conv_get_fallback_for_private_encoding(src_encoding); | ||
| 1356 | |||
| 1357 | conv = g_new0(CodeConverter, 1); | ||
| 1358 | conv->code_conv_func = | ||
| 1359 | conv_get_code_conv_func(src_encoding, dest_encoding); | ||
| 1360 | conv->src_encoding = g_strdup(src_encoding); | ||
| 1361 | conv->dest_encoding = g_strdup(dest_encoding); | ||
| 1362 | |||
| 1363 | return conv; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | void conv_code_converter_destroy(CodeConverter *conv) | ||
| 1367 | { | ||
| 1368 | g_free(conv->src_encoding); | ||
| 1369 | g_free(conv->dest_encoding); | ||
| 1370 | g_free(conv); | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | gchar *conv_convert(CodeConverter *conv, const gchar *inbuf) | ||
| 1374 | { | ||
| 1375 | if (!inbuf) | ||
| 1376 | return NULL; | ||
| 1377 | else if (conv->code_conv_func != conv_noconv) | ||
| 1378 | return conv->code_conv_func(inbuf, NULL); | ||
| 1379 | else | ||
| 1380 | return conv_iconv_strdup | ||
| 1381 | (inbuf, conv->src_encoding, conv->dest_encoding, NULL); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | gchar *conv_codeset_strdup_full(const gchar *inbuf, | ||
| 1385 | const gchar *src_encoding, | ||
| 1386 | const gchar *dest_encoding, | ||
| 1387 | gint *error) | ||
| 1388 | { | ||
| 1389 | CodeConvFunc conv_func; | ||
| 1390 | |||
| 1391 | if (!inbuf) { | ||
| 1392 | if (error) | ||
| 1393 | *error = 0; | ||
| 1394 | return NULL; | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | src_encoding = conv_get_fallback_for_private_encoding(src_encoding); | ||
| 1398 | |||
| 1399 | conv_func = conv_get_code_conv_func(src_encoding, dest_encoding); | ||
| 1400 | if (conv_func != conv_noconv) | ||
| 1401 | return conv_func(inbuf, error); | ||
| 1402 | |||
| 1403 | return conv_iconv_strdup(inbuf, src_encoding, dest_encoding, error); | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | CodeConvFunc conv_get_code_conv_func(const gchar *src_encoding, | ||
| 1407 | const gchar *dest_encoding) | ||
| 1408 | { | ||
| 1409 | CodeConvFunc code_conv = conv_noconv; | ||
| 1410 | CharSet src_charset; | ||
| 1411 | CharSet dest_charset; | ||
| 1412 | |||
| 1413 | if (!src_encoding) | ||
| 1414 | src_charset = conv_get_locale_charset(); | ||
| 1415 | else | ||
| 1416 | src_charset = conv_get_charset_from_str(src_encoding); | ||
| 1417 | |||
| 1418 | /* auto detection mode */ | ||
| 1419 | if (!src_encoding && !dest_encoding) { | ||
| 1420 | if (conv_ad_type == C_AD_JAPANESE || | ||
| 1421 | (conv_ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) | ||
| 1422 | return conv_anytodisp; | ||
| 1423 | else | ||
| 1424 | return conv_noconv; | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | dest_charset = conv_get_charset_from_str(dest_encoding); | ||
| 1428 | |||
| 1429 | if (dest_charset == C_US_ASCII) | ||
| 1430 | return conv_ustodisp; | ||
| 1431 | |||
| 1432 | switch (src_charset) { | ||
| 1433 | case C_US_ASCII: | ||
| 1434 | case C_ISO_8859_1: | ||
| 1435 | case C_ISO_8859_2: | ||
| 1436 | case C_ISO_8859_3: | ||
| 1437 | case C_ISO_8859_4: | ||
| 1438 | case C_ISO_8859_5: | ||
| 1439 | case C_ISO_8859_6: | ||
| 1440 | case C_ISO_8859_7: | ||
| 1441 | case C_ISO_8859_8: | ||
| 1442 | case C_ISO_8859_9: | ||
| 1443 | case C_ISO_8859_10: | ||
| 1444 | case C_ISO_8859_11: | ||
| 1445 | case C_ISO_8859_13: | ||
| 1446 | case C_ISO_8859_14: | ||
| 1447 | case C_ISO_8859_15: | ||
| 1448 | case C_ISO_8859_16: | ||
| 1449 | break; | ||
| 1450 | case C_ISO_2022_JP: | ||
| 1451 | case C_ISO_2022_JP_2: | ||
| 1452 | case C_ISO_2022_JP_3: | ||
| 1453 | if (dest_charset == C_AUTO) | ||
| 1454 | code_conv = conv_jistodisp; | ||
| 1455 | else if (dest_charset == C_EUC_JP) | ||
| 1456 | code_conv = conv_jistoeuc; | ||
| 1457 | else if (dest_charset == C_SHIFT_JIS || | ||
| 1458 | dest_charset == C_CP932) | ||
| 1459 | code_conv = conv_jistosjis; | ||
| 1460 | else if (dest_charset == C_UTF_8) | ||
| 1461 | code_conv = conv_jistoutf8; | ||
| 1462 | break; | ||
| 1463 | case C_SHIFT_JIS: | ||
| 1464 | case C_CP932: | ||
| 1465 | if (dest_charset == C_AUTO) | ||
| 1466 | code_conv = conv_sjistodisp; | ||
| 1467 | else if (dest_charset == C_ISO_2022_JP || | ||
| 1468 | dest_charset == C_ISO_2022_JP_2 || | ||
| 1469 | dest_charset == C_ISO_2022_JP_3) | ||
| 1470 | code_conv = conv_sjistojis; | ||
| 1471 | else if (dest_charset == C_EUC_JP) | ||
| 1472 | code_conv = conv_sjistoeuc; | ||
| 1473 | else if (dest_charset == C_UTF_8) | ||
| 1474 | code_conv = conv_sjistoutf8; | ||
| 1475 | break; | ||
| 1476 | case C_EUC_JP: | ||
| 1477 | if (dest_charset == C_AUTO) | ||
| 1478 | code_conv = conv_euctodisp; | ||
| 1479 | else if (dest_charset == C_ISO_2022_JP || | ||
| 1480 | dest_charset == C_ISO_2022_JP_2 || | ||
| 1481 | dest_charset == C_ISO_2022_JP_3) | ||
| 1482 | code_conv = conv_euctojis; | ||
| 1483 | else if (dest_charset == C_UTF_8) | ||
| 1484 | code_conv = conv_euctoutf8; | ||
| 1485 | break; | ||
| 1486 | case C_UTF_8: | ||
| 1487 | if (dest_charset == C_EUC_JP) | ||
| 1488 | code_conv = conv_utf8toeuc; | ||
| 1489 | else if (dest_charset == C_ISO_2022_JP || | ||
| 1490 | dest_charset == C_ISO_2022_JP_2 || | ||
| 1491 | dest_charset == C_ISO_2022_JP_3) | ||
| 1492 | code_conv = conv_utf8tojis; | ||
| 1493 | else if (dest_charset == C_SHIFT_JIS || | ||
| 1494 | dest_charset == C_CP932) | ||
| 1495 | code_conv = conv_utf8tosjis; | ||
| 1496 | break; | ||
| 1497 | default: | ||
| 1498 | break; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | return code_conv; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | gchar *conv_iconv_strdup(const gchar *inbuf, | ||
| 1505 | const gchar *src_code, const gchar *dest_code, | ||
| 1506 | gint *error) | ||
| 1507 | { | ||
| 1508 | iconv_t cd; | ||
| 1509 | gchar *outbuf; | ||
| 1510 | |||
| 1511 | if (!src_code) | ||
| 1512 | src_code = conv_get_locale_charset_str(); | ||
| 1513 | if (!dest_code) | ||
| 1514 | dest_code = CS_INTERNAL; | ||
| 1515 | |||
| 1516 | cd = iconv_open(dest_code, src_code); | ||
| 1517 | if (cd == (iconv_t)-1) { | ||
| 1518 | if (error) | ||
| 1519 | *error = -1; | ||
| 1520 | return NULL; | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | outbuf = conv_iconv_strdup_with_cd(inbuf, cd, error); | ||
| 1524 | |||
| 1525 | iconv_close(cd); | ||
| 1526 | |||
| 1527 | return outbuf; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | gchar *conv_iconv_strdup_with_cd(const gchar *inbuf, iconv_t cd, gint *error) | ||
| 1531 | { | ||
| 1532 | const gchar *inbuf_p; | ||
| 1533 | gchar *outbuf; | ||
| 1534 | gchar *outbuf_p; | ||
| 1535 | size_t in_size; | ||
| 1536 | size_t in_left; | ||
| 1537 | size_t out_size; | ||
| 1538 | size_t out_left; | ||
| 1539 | size_t n_conv; | ||
| 1540 | size_t len; | ||
| 1541 | gint error_ = 0; | ||
| 1542 | |||
| 1543 | if (!inbuf) { | ||
| 1544 | if (error) | ||
| 1545 | *error = 0; | ||
| 1546 | return NULL; | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | inbuf_p = inbuf; | ||
| 1550 | in_size = strlen(inbuf); | ||
| 1551 | in_left = in_size; | ||
| 1552 | out_size = (in_size + 1) * 2; | ||
| 1553 | outbuf = g_malloc(out_size); | ||
| 1554 | outbuf_p = outbuf; | ||
| 1555 | out_left = out_size; | ||
| 1556 | |||
| 1557 | #define EXPAND_BUF() \ | ||
| 1558 | { \ | ||
| 1559 | len = outbuf_p - outbuf; \ | ||
| 1560 | out_size *= 2; \ | ||
| 1561 | outbuf = g_realloc(outbuf, out_size); \ | ||
| 1562 | outbuf_p = outbuf + len; \ | ||
| 1563 | out_left = out_size - len; \ | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | while ((n_conv = iconv(cd, (ICONV_CONST gchar **)&inbuf_p, &in_left, | ||
| 1567 | &outbuf_p, &out_left)) == (size_t)-1) { | ||
| 1568 | if (EILSEQ == errno) { | ||
| 1569 | /* g_print("iconv(): at %d: %s\n", in_size - in_left, g_strerror(errno)); */ | ||
| 1570 | error_ = -1; | ||
| 1571 | inbuf_p++; | ||
| 1572 | in_left--; | ||
| 1573 | if (out_left == 0) { | ||
| 1574 | EXPAND_BUF(); | ||
| 1575 | } | ||
| 1576 | *outbuf_p++ = SUBST_CHAR; | ||
| 1577 | out_left--; | ||
| 1578 | } else if (EINVAL == errno) { | ||
| 1579 | error_ = -1; | ||
| 1580 | break; | ||
| 1581 | } else if (E2BIG == errno) { | ||
| 1582 | EXPAND_BUF(); | ||
| 1583 | } else { | ||
| 1584 | g_warning("conv_iconv_strdup(): %s\n", | ||
| 1585 | g_strerror(errno)); | ||
| 1586 | error_ = -1; | ||
| 1587 | break; | ||
| 1588 | } | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | while ((n_conv = iconv(cd, NULL, NULL, &outbuf_p, &out_left)) == | ||
| 1592 | (size_t)-1) { | ||
| 1593 | if (E2BIG == errno) { | ||
| 1594 | EXPAND_BUF(); | ||
| 1595 | } else { | ||
| 1596 | g_warning("conv_iconv_strdup(): %s\n", | ||
| 1597 | g_strerror(errno)); | ||
| 1598 | error_ = -1; | ||
| 1599 | break; | ||
| 1600 | } | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | #undef EXPAND_BUF | ||
| 1604 | |||
| 1605 | len = outbuf_p - outbuf; | ||
| 1606 | outbuf = g_realloc(outbuf, len + 1); | ||
| 1607 | outbuf[len] = '\0'; | ||
| 1608 | |||
| 1609 | if (error) | ||
| 1610 | *error = error_; | ||
| 1611 | |||
| 1612 | return outbuf; | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | static const struct { | ||
| 1616 | CharSet charset; | ||
| 1617 | gchar *const name; | ||
| 1618 | } charsets[] = { | ||
| 1619 | {C_US_ASCII, CS_US_ASCII}, | ||
| 1620 | {C_US_ASCII, CS_ANSI_X3_4_1968}, | ||
| 1621 | {C_UTF_8, CS_UTF_8}, | ||
| 1622 | {C_UTF_7, CS_UTF_7}, | ||
| 1623 | {C_ISO_8859_1, CS_ISO_8859_1}, | ||
| 1624 | {C_ISO_8859_2, CS_ISO_8859_2}, | ||
| 1625 | {C_ISO_8859_3, CS_ISO_8859_3}, | ||
| 1626 | {C_ISO_8859_4, CS_ISO_8859_4}, | ||
| 1627 | {C_ISO_8859_5, CS_ISO_8859_5}, | ||
| 1628 | {C_ISO_8859_6, CS_ISO_8859_6}, | ||
| 1629 | {C_ISO_8859_7, CS_ISO_8859_7}, | ||
| 1630 | {C_ISO_8859_8, CS_ISO_8859_8}, | ||
| 1631 | {C_ISO_8859_9, CS_ISO_8859_9}, | ||
| 1632 | {C_ISO_8859_10, CS_ISO_8859_10}, | ||
| 1633 | {C_ISO_8859_11, CS_ISO_8859_11}, | ||
| 1634 | {C_ISO_8859_13, CS_ISO_8859_13}, | ||
| 1635 | {C_ISO_8859_14, CS_ISO_8859_14}, | ||
| 1636 | {C_ISO_8859_15, CS_ISO_8859_15}, | ||
| 1637 | {C_BALTIC, CS_BALTIC}, | ||
| 1638 | {C_CP932, CS_CP932}, | ||
| 1639 | {C_CP1250, CS_CP1250}, | ||
| 1640 | {C_CP1251, CS_CP1251}, | ||
| 1641 | {C_CP1252, CS_CP1252}, | ||
| 1642 | {C_CP1253, CS_CP1253}, | ||
| 1643 | {C_CP1254, CS_CP1254}, | ||
| 1644 | {C_CP1255, CS_CP1255}, | ||
| 1645 | {C_CP1256, CS_CP1256}, | ||
| 1646 | {C_CP1257, CS_CP1257}, | ||
| 1647 | {C_CP1258, CS_CP1258}, | ||
| 1648 | {C_WINDOWS_932, CS_WINDOWS_932}, | ||
| 1649 | {C_WINDOWS_1250, CS_WINDOWS_1250}, | ||
| 1650 | {C_WINDOWS_1251, CS_WINDOWS_1251}, | ||
| 1651 | {C_WINDOWS_1252, CS_WINDOWS_1252}, | ||
| 1652 | {C_WINDOWS_1253, CS_WINDOWS_1253}, | ||
| 1653 | {C_WINDOWS_1254, CS_WINDOWS_1254}, | ||
| 1654 | {C_WINDOWS_1255, CS_WINDOWS_1255}, | ||
| 1655 | {C_WINDOWS_1256, CS_WINDOWS_1256}, | ||
| 1656 | {C_WINDOWS_1257, CS_WINDOWS_1257}, | ||
| 1657 | {C_WINDOWS_1258, CS_WINDOWS_1258}, | ||
| 1658 | {C_KOI8_R, CS_KOI8_R}, | ||
| 1659 | {C_KOI8_T, CS_KOI8_T}, | ||
| 1660 | {C_KOI8_U, CS_KOI8_U}, | ||
| 1661 | {C_ISO_2022_JP, CS_ISO_2022_JP}, | ||
| 1662 | {C_ISO_2022_JP_2, CS_ISO_2022_JP_2}, | ||
| 1663 | {C_ISO_2022_JP_3, CS_ISO_2022_JP_3}, | ||
| 1664 | {C_EUC_JP, CS_EUC_JP}, | ||
| 1665 | {C_EUC_JP, CS_EUCJP}, | ||
| 1666 | {C_EUC_JP_MS, CS_EUC_JP_MS}, | ||
| 1667 | {C_SHIFT_JIS, CS_SHIFT_JIS}, | ||
| 1668 | {C_SHIFT_JIS, CS_SHIFT__JIS}, | ||
| 1669 | {C_SHIFT_JIS, CS_SJIS}, | ||
| 1670 | {C_ISO_2022_KR, CS_ISO_2022_KR}, | ||
| 1671 | {C_EUC_KR, CS_EUC_KR}, | ||
| 1672 | {C_ISO_2022_CN, CS_ISO_2022_CN}, | ||
| 1673 | {C_EUC_CN, CS_EUC_CN}, | ||
| 1674 | {C_GB2312, CS_GB2312}, | ||
| 1675 | {C_GBK, CS_GBK}, | ||
| 1676 | {C_EUC_TW, CS_EUC_TW}, | ||
| 1677 | {C_BIG5, CS_BIG5}, | ||
| 1678 | {C_BIG5_HKSCS, CS_BIG5_HKSCS}, | ||
| 1679 | {C_TIS_620, CS_TIS_620}, | ||
| 1680 | {C_WINDOWS_874, CS_WINDOWS_874}, | ||
| 1681 | {C_GEORGIAN_PS, CS_GEORGIAN_PS}, | ||
| 1682 | {C_TCVN5712_1, CS_TCVN5712_1}, | ||
| 1683 | {C_ISO_8859_16, CS_ISO_8859_16}, | ||
| 1684 | {C_UTF_16, CS_UTF_16}, | ||
| 1685 | {C_UTF_16BE, CS_UTF_16BE}, | ||
| 1686 | {C_UTF_16LE, CS_UTF_16LE}, | ||
| 1687 | }; | ||
| 1688 | |||
| 1689 | static const struct { | ||
| 1690 | gchar *const locale; | ||
| 1691 | CharSet charset; | ||
| 1692 | CharSet out_charset; | ||
| 1693 | } locale_table[] = { | ||
| 1694 | {"ja_JP.eucJP" , C_EUC_JP , C_ISO_2022_JP}, | ||
| 1695 | {"ja_JP.EUC-JP" , C_EUC_JP , C_ISO_2022_JP}, | ||
| 1696 | {"ja_JP.EUC" , C_EUC_JP , C_ISO_2022_JP}, | ||
| 1697 | {"ja_JP.ujis" , C_EUC_JP , C_ISO_2022_JP}, | ||
| 1698 | {"ja_JP.SJIS" , C_SHIFT_JIS , C_ISO_2022_JP}, | ||
| 1699 | {"ja_JP.JIS" , C_ISO_2022_JP , C_ISO_2022_JP}, | ||
| 1700 | #ifdef G_OS_WIN32 | ||
| 1701 | {"ja_JP" , C_CP932 , C_ISO_2022_JP}, | ||
| 1702 | #elif defined(__APPLE__) | ||
| 1703 | {"ja_JP" , C_UTF_8 , C_ISO_2022_JP}, | ||
| 1704 | #else | ||
| 1705 | {"ja_JP" , C_EUC_JP , C_ISO_2022_JP}, | ||
| 1706 | #endif | ||
| 1707 | {"ko_KR.EUC-KR" , C_EUC_KR , C_EUC_KR}, | ||
| 1708 | {"ko_KR" , C_EUC_KR , C_EUC_KR}, | ||
| 1709 | {"zh_CN.GB2312" , C_GB2312 , C_GB2312}, | ||
| 1710 | {"zh_CN.GBK" , C_GBK , C_GBK}, | ||
| 1711 | {"zh_CN" , C_GB2312 , C_GB2312}, | ||
| 1712 | {"zh_HK" , C_BIG5_HKSCS , C_BIG5_HKSCS}, | ||
| 1713 | {"zh_TW.eucTW" , C_EUC_TW , C_BIG5}, | ||
| 1714 | {"zh_TW.EUC-TW" , C_EUC_TW , C_BIG5}, | ||
| 1715 | {"zh_TW.Big5" , C_BIG5 , C_BIG5}, | ||
| 1716 | {"zh_TW" , C_BIG5 , C_BIG5}, | ||
| 1717 | |||
| 1718 | {"ru_RU.KOI8-R" , C_KOI8_R , C_KOI8_R}, | ||
| 1719 | {"ru_RU.KOI8R" , C_KOI8_R , C_KOI8_R}, | ||
| 1720 | {"ru_RU.CP1251" , C_WINDOWS_1251, C_KOI8_R}, | ||
| 1721 | {"ru_RU" , C_ISO_8859_5 , C_KOI8_R}, | ||
| 1722 | {"tg_TJ" , C_KOI8_T , C_KOI8_T}, | ||
| 1723 | {"ru_UA" , C_KOI8_U , C_KOI8_U}, | ||
| 1724 | {"uk_UA.CP1251" , C_WINDOWS_1251, C_KOI8_U}, | ||
| 1725 | {"uk_UA" , C_KOI8_U , C_KOI8_U}, | ||
| 1726 | |||
| 1727 | {"be_BY" , C_WINDOWS_1251, C_WINDOWS_1251}, | ||
| 1728 | {"bg_BG" , C_WINDOWS_1251, C_WINDOWS_1251}, | ||
| 1729 | |||
| 1730 | {"yi_US" , C_WINDOWS_1255, C_WINDOWS_1255}, | ||
| 1731 | |||
| 1732 | {"af_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1733 | {"br_FR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1734 | {"ca_ES" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1735 | {"da_DK" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1736 | {"de_AT" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1737 | {"de_BE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1738 | {"de_CH" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1739 | {"de_DE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1740 | {"de_LU" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1741 | {"en_AU" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1742 | {"en_BW" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1743 | {"en_CA" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1744 | {"en_DK" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1745 | {"en_GB" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1746 | {"en_HK" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1747 | {"en_IE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1748 | {"en_NZ" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1749 | {"en_PH" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1750 | {"en_SG" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1751 | {"en_US" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1752 | {"en_ZA" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1753 | {"en_ZW" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1754 | {"es_AR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1755 | {"es_BO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1756 | {"es_CL" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1757 | {"es_CO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1758 | {"es_CR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1759 | {"es_DO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1760 | {"es_EC" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1761 | {"es_ES" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1762 | {"es_GT" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1763 | {"es_HN" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1764 | {"es_MX" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1765 | {"es_NI" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1766 | {"es_PA" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1767 | {"es_PE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1768 | {"es_PR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1769 | {"es_PY" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1770 | {"es_SV" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1771 | {"es_US" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1772 | {"es_UY" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1773 | {"es_VE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1774 | {"et_EE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1775 | {"eu_ES" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1776 | {"fi_FI" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1777 | {"fo_FO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1778 | {"fr_BE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1779 | {"fr_CA" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1780 | {"fr_CH" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1781 | {"fr_FR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1782 | {"fr_LU" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1783 | {"ga_IE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1784 | {"gl_ES" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1785 | {"gv_GB" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1786 | {"id_ID" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1787 | {"is_IS" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1788 | {"it_CH" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1789 | {"it_IT" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1790 | {"kl_GL" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1791 | {"kw_GB" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1792 | {"ms_MY" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1793 | {"nl_BE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1794 | {"nl_NL" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1795 | {"nn_NO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1796 | {"no_NO" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1797 | {"oc_FR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1798 | {"pt_BR" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1799 | {"pt_PT" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1800 | {"sq_AL" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1801 | {"sv_FI" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1802 | {"sv_SE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1803 | {"tl_PH" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1804 | {"uz_UZ" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1805 | {"wa_BE" , C_ISO_8859_1 , C_ISO_8859_1}, | ||
| 1806 | |||
| 1807 | {"bs_BA" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1808 | {"cs_CZ" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1809 | {"hr_HR" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1810 | {"hu_HU" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1811 | {"pl_PL" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1812 | {"ro_RO" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1813 | {"sk_SK" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1814 | {"sl_SI" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1815 | |||
| 1816 | {"sr_YU@cyrillic" , C_ISO_8859_5 , C_ISO_8859_5}, | ||
| 1817 | {"sr_YU" , C_ISO_8859_2 , C_ISO_8859_2}, | ||
| 1818 | |||
| 1819 | {"mt_MT" , C_ISO_8859_3 , C_ISO_8859_3}, | ||
| 1820 | |||
| 1821 | {"lt_LT.iso88594" , C_ISO_8859_4 , C_ISO_8859_4}, | ||
| 1822 | {"lt_LT.ISO8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, | ||
| 1823 | {"lt_LT.ISO_8859-4" , C_ISO_8859_4 , C_ISO_8859_4}, | ||
| 1824 | {"lt_LT" , C_ISO_8859_13 , C_ISO_8859_13}, | ||
| 1825 | |||
| 1826 | {"mk_MK" , C_ISO_8859_5 , C_ISO_8859_5}, | ||
| 1827 | |||
| 1828 | {"ar_AE" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1829 | {"ar_BH" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1830 | {"ar_DZ" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1831 | {"ar_EG" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1832 | {"ar_IQ" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1833 | {"ar_JO" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1834 | {"ar_KW" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1835 | {"ar_LB" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1836 | {"ar_LY" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1837 | {"ar_MA" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1838 | {"ar_OM" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1839 | {"ar_QA" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1840 | {"ar_SA" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1841 | {"ar_SD" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1842 | {"ar_SY" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1843 | {"ar_TN" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1844 | {"ar_YE" , C_ISO_8859_6 , C_ISO_8859_6}, | ||
| 1845 | |||
| 1846 | {"el_GR" , C_ISO_8859_7 , C_ISO_8859_7}, | ||
| 1847 | {"he_IL" , C_ISO_8859_8 , C_ISO_8859_8}, | ||
| 1848 | {"iw_IL" , C_ISO_8859_8 , C_ISO_8859_8}, | ||
| 1849 | {"tr_TR" , C_ISO_8859_9 , C_ISO_8859_9}, | ||
| 1850 | |||
| 1851 | {"lv_LV" , C_ISO_8859_13 , C_ISO_8859_13}, | ||
| 1852 | {"mi_NZ" , C_ISO_8859_13 , C_ISO_8859_13}, | ||
| 1853 | |||
| 1854 | {"cy_GB" , C_ISO_8859_14 , C_ISO_8859_14}, | ||
| 1855 | |||
| 1856 | {"ar_IN" , C_UTF_8 , C_UTF_8}, | ||
| 1857 | {"en_IN" , C_UTF_8 , C_UTF_8}, | ||
| 1858 | {"se_NO" , C_UTF_8 , C_UTF_8}, | ||
| 1859 | {"ta_IN" , C_UTF_8 , C_UTF_8}, | ||
| 1860 | {"te_IN" , C_UTF_8 , C_UTF_8}, | ||
| 1861 | {"ur_PK" , C_UTF_8 , C_UTF_8}, | ||
| 1862 | |||
| 1863 | {"th_TH" , C_TIS_620 , C_TIS_620}, | ||
| 1864 | /* {"th_TH" , C_WINDOWS_874}, */ | ||
| 1865 | /* {"th_TH" , C_ISO_8859_11}, */ | ||
| 1866 | |||
| 1867 | {"ka_GE" , C_GEORGIAN_PS , C_GEORGIAN_PS}, | ||
| 1868 | {"vi_VN.TCVN" , C_TCVN5712_1 , C_TCVN5712_1}, | ||
| 1869 | |||
| 1870 | {"C" , C_US_ASCII , C_US_ASCII}, | ||
| 1871 | {"POSIX" , C_US_ASCII , C_US_ASCII}, | ||
| 1872 | {"ANSI_X3.4-1968" , C_US_ASCII , C_US_ASCII}, | ||
| 1873 | }; | ||
| 1874 | |||
| 1875 | static GHashTable *conv_get_charset_to_str_table(void) | ||
| 1876 | { | ||
| 1877 | static GHashTable *table; | ||
| 1878 | gint i; | ||
| 1879 | S_LOCK_DEFINE_STATIC(table); | ||
| 1880 | |||
| 1881 | S_LOCK(table); | ||
| 1882 | |||
| 1883 | if (table) { | ||
| 1884 | S_UNLOCK(table); | ||
| 1885 | return table; | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | table = g_hash_table_new(NULL, g_direct_equal); | ||
| 1889 | |||
| 1890 | for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { | ||
| 1891 | if (g_hash_table_lookup(table, GUINT_TO_POINTER(charsets[i].charset)) | ||
| 1892 | == NULL) { | ||
| 1893 | g_hash_table_insert | ||
| 1894 | (table, GUINT_TO_POINTER(charsets[i].charset), | ||
| 1895 | charsets[i].name); | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | |||
| 1899 | S_UNLOCK(table); | ||
| 1900 | return table; | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | static GHashTable *conv_get_charset_from_str_table(void) | ||
| 1904 | { | ||
| 1905 | static GHashTable *table; | ||
| 1906 | S_LOCK_DEFINE_STATIC(table); | ||
| 1907 | |||
| 1908 | gint i; | ||
| 1909 | |||
| 1910 | S_LOCK(table); | ||
| 1911 | |||
| 1912 | if (table) { | ||
| 1913 | S_UNLOCK(table); | ||
| 1914 | return table; | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | table = g_hash_table_new(str_case_hash, str_case_equal); | ||
| 1918 | |||
| 1919 | for (i = 0; i < sizeof(charsets) / sizeof(charsets[0]); i++) { | ||
| 1920 | g_hash_table_insert(table, charsets[i].name, | ||
| 1921 | GUINT_TO_POINTER(charsets[i].charset)); | ||
| 1922 | } | ||
| 1923 | |||
| 1924 | S_UNLOCK(table); | ||
| 1925 | return table; | ||
| 1926 | } | ||
| 1927 | |||
| 1928 | const gchar *conv_get_charset_str(CharSet charset) | ||
| 1929 | { | ||
| 1930 | GHashTable *table; | ||
| 1931 | |||
| 1932 | table = conv_get_charset_to_str_table(); | ||
| 1933 | return g_hash_table_lookup(table, GUINT_TO_POINTER(charset)); | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | CharSet conv_get_charset_from_str(const gchar *charset) | ||
| 1937 | { | ||
| 1938 | GHashTable *table; | ||
| 1939 | |||
| 1940 | if (!charset) return C_AUTO; | ||
| 1941 | |||
| 1942 | table = conv_get_charset_from_str_table(); | ||
| 1943 | return GPOINTER_TO_UINT(g_hash_table_lookup(table, charset)); | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | CharSet conv_get_locale_charset(void) | ||
| 1947 | { | ||
| 1948 | static CharSet cur_charset = -1; | ||
| 1949 | const gchar *cur_locale; | ||
| 1950 | const gchar *p; | ||
| 1951 | #if !defined(G_OS_WIN32) && !defined(__APPLE__) | ||
| 1952 | gint i; | ||
| 1953 | #endif | ||
| 1954 | S_LOCK_DEFINE_STATIC(cur_charset); | ||
| 1955 | |||
| 1956 | S_LOCK(cur_charset); | ||
| 1957 | |||
| 1958 | if (cur_charset != -1) { | ||
| 1959 | S_UNLOCK(cur_charset); | ||
| 1960 | return cur_charset; | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | cur_locale = conv_get_current_locale(); | ||
| 1964 | if (!cur_locale) { | ||
| 1965 | cur_charset = C_US_ASCII; | ||
| 1966 | S_UNLOCK(cur_charset); | ||
| 1967 | return cur_charset; | ||
| 1968 | } | ||
| 1969 | |||
| 1970 | if (strcasestr(cur_locale, "UTF-8") || strcasestr(cur_locale, "utf8")) { | ||
| 1971 | cur_charset = C_UTF_8; | ||
| 1972 | S_UNLOCK(cur_charset); | ||
| 1973 | return cur_charset; | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { | ||
| 1977 | cur_charset = C_ISO_8859_15; | ||
| 1978 | S_UNLOCK(cur_charset); | ||
| 1979 | return cur_charset; | ||
| 1980 | } | ||
| 1981 | |||
| 1982 | #if defined(G_OS_WIN32) || defined(__APPLE__) | ||
| 1983 | cur_charset = conv_get_charset_from_str(conv_get_locale_charset_str()); | ||
| 1984 | |||
| 1985 | S_UNLOCK(cur_charset); | ||
| 1986 | return cur_charset; | ||
| 1987 | #else | ||
| 1988 | for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { | ||
| 1989 | const gchar *p; | ||
| 1990 | |||
| 1991 | /* "ja_JP.EUC" matches with "ja_JP.eucJP", "ja_JP.EUC" and | ||
| 1992 | "ja_JP". "ja_JP" matches with "ja_JP.xxxx" and "ja" */ | ||
| 1993 | if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, | ||
| 1994 | strlen(locale_table[i].locale))) { | ||
| 1995 | cur_charset = locale_table[i].charset; | ||
| 1996 | S_UNLOCK(cur_charset); | ||
| 1997 | return cur_charset; | ||
| 1998 | } else if ((p = strchr(locale_table[i].locale, '_')) && | ||
| 1999 | !strchr(p + 1, '.')) { | ||
| 2000 | if (strlen(cur_locale) == 2 && | ||
| 2001 | !g_ascii_strncasecmp(cur_locale, | ||
| 2002 | locale_table[i].locale, 2)) { | ||
| 2003 | cur_charset = locale_table[i].charset; | ||
| 2004 | S_UNLOCK(cur_charset); | ||
| 2005 | return cur_charset; | ||
| 2006 | } | ||
| 2007 | } | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | cur_charset = C_AUTO; | ||
| 2011 | S_UNLOCK(cur_charset); | ||
| 2012 | return cur_charset; | ||
| 2013 | #endif | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | const gchar *conv_get_locale_charset_str(void) | ||
| 2017 | { | ||
| 2018 | static const gchar *codeset = NULL; | ||
| 2019 | S_LOCK_DEFINE_STATIC(codeset); | ||
| 2020 | |||
| 2021 | S_LOCK(codeset); | ||
| 2022 | |||
| 2023 | if (!codeset) { | ||
| 2024 | #if defined(G_OS_WIN32) || defined(__APPLE__) | ||
| 2025 | g_get_charset(&codeset); | ||
| 2026 | if (!strcmp(codeset, CS_US_ASCII) || | ||
| 2027 | !strcmp(codeset, CS_ANSI_X3_4_1968)) | ||
| 2028 | codeset = CS_INTERNAL; | ||
| 2029 | #else | ||
| 2030 | codeset = conv_get_charset_str(conv_get_locale_charset()); | ||
| 2031 | #endif | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | if (codeset) { | ||
| 2035 | S_UNLOCK(codeset); | ||
| 2036 | return codeset; | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | S_UNLOCK(codeset); | ||
| 2040 | return CS_INTERNAL; | ||
| 2041 | } | ||
| 2042 | |||
| 2043 | CharSet conv_get_internal_charset(void) | ||
| 2044 | { | ||
| 2045 | return C_INTERNAL; | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | const gchar *conv_get_internal_charset_str(void) | ||
| 2049 | { | ||
| 2050 | return CS_INTERNAL; | ||
| 2051 | } | ||
| 2052 | |||
| 2053 | CharSet conv_get_outgoing_charset(void) | ||
| 2054 | { | ||
| 2055 | static CharSet out_charset = -1; | ||
| 2056 | const gchar *cur_locale; | ||
| 2057 | const gchar *p; | ||
| 2058 | gint i; | ||
| 2059 | S_LOCK_DEFINE_STATIC(out_charset); | ||
| 2060 | |||
| 2061 | S_LOCK(out_charset); | ||
| 2062 | |||
| 2063 | if (out_charset != -1) { | ||
| 2064 | S_UNLOCK(out_charset); | ||
| 2065 | return out_charset; | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | cur_locale = conv_get_current_locale(); | ||
| 2069 | if (!cur_locale) { | ||
| 2070 | out_charset = C_AUTO; | ||
| 2071 | S_UNLOCK(out_charset); | ||
| 2072 | return out_charset; | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | if ((p = strcasestr(cur_locale, "@euro")) && p[5] == '\0') { | ||
| 2076 | out_charset = C_ISO_8859_15; | ||
| 2077 | S_UNLOCK(out_charset); | ||
| 2078 | return out_charset; | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | for (i = 0; i < sizeof(locale_table) / sizeof(locale_table[0]); i++) { | ||
| 2082 | const gchar *p; | ||
| 2083 | |||
| 2084 | if (!g_ascii_strncasecmp(cur_locale, locale_table[i].locale, | ||
| 2085 | strlen(locale_table[i].locale))) { | ||
| 2086 | out_charset = locale_table[i].out_charset; | ||
| 2087 | break; | ||
| 2088 | } else if ((p = strchr(locale_table[i].locale, '_')) && | ||
| 2089 | !strchr(p + 1, '.')) { | ||
| 2090 | if (strlen(cur_locale) == 2 && | ||
| 2091 | !g_ascii_strncasecmp(cur_locale, | ||
| 2092 | locale_table[i].locale, 2)) { | ||
| 2093 | out_charset = locale_table[i].out_charset; | ||
| 2094 | break; | ||
| 2095 | } | ||
| 2096 | } | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | S_UNLOCK(out_charset); | ||
| 2100 | return out_charset; | ||
| 2101 | } | ||
| 2102 | |||
| 2103 | const gchar *conv_get_outgoing_charset_str(void) | ||
| 2104 | { | ||
| 2105 | CharSet out_charset; | ||
| 2106 | const gchar *str; | ||
| 2107 | |||
| 2108 | out_charset = conv_get_outgoing_charset(); | ||
| 2109 | str = conv_get_charset_str(out_charset); | ||
| 2110 | |||
| 2111 | return str ? str : CS_UTF_8; | ||
| 2112 | } | ||
| 2113 | |||
| 2114 | gboolean conv_is_multibyte_encoding(CharSet encoding) | ||
| 2115 | { | ||
| 2116 | switch (encoding) { | ||
| 2117 | case C_EUC_JP: | ||
| 2118 | case C_EUC_JP_MS: | ||
| 2119 | case C_EUC_KR: | ||
| 2120 | case C_EUC_TW: | ||
| 2121 | case C_EUC_CN: | ||
| 2122 | case C_ISO_2022_JP: | ||
| 2123 | case C_ISO_2022_JP_2: | ||
| 2124 | case C_ISO_2022_JP_3: | ||
| 2125 | case C_ISO_2022_KR: | ||
| 2126 | case C_ISO_2022_CN: | ||
| 2127 | case C_SHIFT_JIS: | ||
| 2128 | case C_CP932: | ||
| 2129 | case C_GB2312: | ||
| 2130 | case C_GBK: | ||
| 2131 | case C_BIG5: | ||
| 2132 | case C_UTF_8: | ||
| 2133 | case C_UTF_7: | ||
| 2134 | return TRUE; | ||
| 2135 | default: | ||
| 2136 | return FALSE; | ||
| 2137 | } | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | const gchar *conv_get_current_locale(void) | ||
| 2141 | { | ||
| 2142 | static const gchar *cur_locale; | ||
| 2143 | S_LOCK_DEFINE_STATIC(cur_locale); | ||
| 2144 | |||
| 2145 | S_LOCK(cur_locale); | ||
| 2146 | |||
| 2147 | if (!cur_locale) { | ||
| 2148 | #ifdef G_OS_WIN32 | ||
| 2149 | cur_locale = g_win32_getlocale(); | ||
| 2150 | #else | ||
| 2151 | cur_locale = g_getenv("LC_ALL"); | ||
| 2152 | if (!cur_locale || *cur_locale == '\0') | ||
| 2153 | cur_locale = g_getenv("LC_CTYPE"); | ||
| 2154 | if (!cur_locale || *cur_locale == '\0') | ||
| 2155 | cur_locale = g_getenv("LANG"); | ||
| 2156 | #ifdef HAVE_LOCALE_H | ||
| 2157 | if (!cur_locale || *cur_locale == '\0') | ||
| 2158 | cur_locale = setlocale(LC_CTYPE, NULL); | ||
| 2159 | #endif /* HAVE_LOCALE_H */ | ||
| 2160 | #endif /* G_OS_WIN32 */ | ||
| 2161 | |||
| 2162 | debug_print("current locale: %s\n", | ||
| 2163 | cur_locale ? cur_locale : "(none)"); | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | S_UNLOCK(cur_locale); | ||
| 2167 | return cur_locale; | ||
| 2168 | } | ||
| 2169 | |||
| 2170 | gboolean conv_is_ja_locale(void) | ||
| 2171 | { | ||
| 2172 | static gint is_ja_locale = -1; | ||
| 2173 | const gchar *cur_locale; | ||
| 2174 | S_LOCK_DEFINE_STATIC(is_ja_locale); | ||
| 2175 | |||
| 2176 | S_LOCK(is_ja_locale); | ||
| 2177 | |||
| 2178 | if (is_ja_locale != -1) { | ||
| 2179 | S_UNLOCK(is_ja_locale); | ||
| 2180 | return is_ja_locale != 0; | ||
| 2181 | } | ||
| 2182 | |||
| 2183 | is_ja_locale = 0; | ||
| 2184 | cur_locale = conv_get_current_locale(); | ||
| 2185 | if (cur_locale) { | ||
| 2186 | if (g_ascii_strncasecmp(cur_locale, "ja", 2) == 0) | ||
| 2187 | is_ja_locale = 1; | ||
| 2188 | } | ||
| 2189 | |||
| 2190 | S_UNLOCK(is_ja_locale); | ||
| 2191 | return is_ja_locale != 0; | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | void conv_set_autodetect_type(ConvADType type) | ||
| 2195 | { | ||
| 2196 | conv_ad_type = type; | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | ConvADType conv_get_autodetect_type(void) | ||
| 2200 | { | ||
| 2201 | return conv_ad_type; | ||
| 2202 | } | ||
| 2203 | |||
| 2204 | gchar *conv_unmime_header(const gchar *str, const gchar *default_encoding) | ||
| 2205 | { | ||
| 2206 | gchar *buf; | ||
| 2207 | gchar *decoded_str; | ||
| 2208 | |||
| 2209 | if (is_ascii_str(str)) | ||
| 2210 | return unmime_header(str); | ||
| 2211 | |||
| 2212 | if (default_encoding) { | ||
| 2213 | buf = conv_codeset_strdup | ||
| 2214 | (str, default_encoding, CS_INTERNAL); | ||
| 2215 | if (buf) { | ||
| 2216 | decoded_str = unmime_header(buf); | ||
| 2217 | g_free(buf); | ||
| 2218 | return decoded_str; | ||
| 2219 | } | ||
| 2220 | } | ||
| 2221 | |||
| 2222 | if (conv_ad_type == C_AD_JAPANESE || | ||
| 2223 | (conv_ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) | ||
| 2224 | buf = conv_anytodisp(str, NULL); | ||
| 2225 | else | ||
| 2226 | buf = conv_localetodisp(str, NULL); | ||
| 2227 | |||
| 2228 | decoded_str = unmime_header(buf); | ||
| 2229 | g_free(buf); | ||
| 2230 | |||
| 2231 | return decoded_str; | ||
| 2232 | } | ||
| 2233 | |||
| 2234 | #define MAX_LINELEN 76 | ||
| 2235 | #define MAX_HARD_LINELEN 996 | ||
| 2236 | #define MIMESEP_BEGIN "=?" | ||
| 2237 | #define MIMESEP_END "?=" | ||
| 2238 | |||
| 2239 | #define B64LEN(len) ((len) / 3 * 4 + ((len) % 3 ? 4 : 0)) | ||
| 2240 | |||
| 2241 | #define LBREAK_IF_REQUIRED(cond, is_plain_text) \ | ||
| 2242 | { \ | ||
| 2243 | if (len - (destp - dest) < MAX_LINELEN + 2) { \ | ||
| 2244 | *destp = '\0'; \ | ||
| 2245 | return; \ | ||
| 2246 | } \ | ||
| 2247 | \ | ||
| 2248 | if ((cond) && *srcp) { \ | ||
| 2249 | if (destp > dest && left < MAX_LINELEN - 1) { \ | ||
| 2250 | if (g_ascii_isspace(*(destp - 1))) \ | ||
| 2251 | destp--; \ | ||
| 2252 | else if (is_plain_text && \ | ||
| 2253 | g_ascii_isspace(*srcp)) \ | ||
| 2254 | srcp++; \ | ||
| 2255 | if (*srcp) { \ | ||
| 2256 | *destp++ = '\n'; \ | ||
| 2257 | *destp++ = ' '; \ | ||
| 2258 | left = MAX_LINELEN - 1; \ | ||
| 2259 | } \ | ||
| 2260 | } \ | ||
| 2261 | } \ | ||
| 2262 | } | ||
| 2263 | |||
| 2264 | void conv_encode_header(gchar *dest, gint len, const gchar *src, | ||
| 2265 | gint header_len, gboolean addr_field, | ||
| 2266 | const gchar *out_encoding) | ||
| 2267 | { | ||
| 2268 | const gchar *src_encoding; | ||
| 2269 | gint mimestr_len; | ||
| 2270 | gchar *mimesep_enc; | ||
| 2271 | gint left; | ||
| 2272 | const gchar *srcp = src; | ||
| 2273 | gchar *destp = dest; | ||
| 2274 | gboolean use_base64; | ||
| 2275 | |||
| 2276 | g_return_if_fail(g_utf8_validate(src, -1, NULL) == TRUE); | ||
| 2277 | |||
| 2278 | src_encoding = CS_INTERNAL; | ||
| 2279 | if (!out_encoding) | ||
| 2280 | out_encoding = conv_get_outgoing_charset_str(); | ||
| 2281 | if (!strcmp(out_encoding, CS_US_ASCII)) | ||
| 2282 | out_encoding = CS_ISO_8859_1; | ||
| 2283 | |||
| 2284 | if (!g_ascii_strncasecmp(out_encoding, "ISO-8859-", 9) || | ||
| 2285 | !g_ascii_strncasecmp(out_encoding, "KOI8-", 5) || | ||
| 2286 | !g_ascii_strncasecmp(out_encoding, "Windows-", 8)) { | ||
| 2287 | use_base64 = FALSE; | ||
| 2288 | mimesep_enc = "?Q?"; | ||
| 2289 | } else { | ||
| 2290 | use_base64 = TRUE; | ||
| 2291 | mimesep_enc = "?B?"; | ||
| 2292 | } | ||
| 2293 | |||
| 2294 | mimestr_len = strlen(MIMESEP_BEGIN) + strlen(mimesep_enc) + | ||
| 2295 | strlen(MIMESEP_END); | ||
| 2296 | |||
| 2297 | left = MAX_LINELEN - header_len; | ||
| 2298 | |||
| 2299 | while (*srcp) { | ||
| 2300 | gboolean in_quote = FALSE; | ||
| 2301 | |||
| 2302 | LBREAK_IF_REQUIRED(left <= 0, TRUE); | ||
| 2303 | |||
| 2304 | while (g_ascii_isspace(*srcp)) { | ||
| 2305 | *destp++ = *srcp++; | ||
| 2306 | left--; | ||
| 2307 | LBREAK_IF_REQUIRED(left <= 0, TRUE); | ||
| 2308 | } | ||
| 2309 | |||
| 2310 | /* output as it is if the next word is ASCII string */ | ||
| 2311 | if (!is_next_nonascii(srcp)) { | ||
| 2312 | gint word_len; | ||
| 2313 | |||
| 2314 | word_len = get_next_word_len(srcp); | ||
| 2315 | LBREAK_IF_REQUIRED(left < word_len, TRUE); | ||
| 2316 | while (word_len > 0) { | ||
| 2317 | LBREAK_IF_REQUIRED(left + (MAX_HARD_LINELEN - MAX_LINELEN) <= 0, TRUE) | ||
| 2318 | *destp++ = *srcp++; | ||
| 2319 | left--; | ||
| 2320 | word_len--; | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | continue; | ||
| 2324 | } | ||
| 2325 | |||
| 2326 | /* don't include parentheses in encoded strings */ | ||
| 2327 | if (addr_field && (*srcp == '(' || *srcp == ')')) { | ||
| 2328 | LBREAK_IF_REQUIRED(left < 2, FALSE); | ||
| 2329 | *destp++ = *srcp++; | ||
| 2330 | left--; | ||
| 2331 | } | ||
| 2332 | |||
| 2333 | while (1) { | ||
| 2334 | gint mb_len = 0; | ||
| 2335 | gint cur_len = 0; | ||
| 2336 | gchar *part_str; | ||
| 2337 | gchar *out_str; | ||
| 2338 | gchar *enc_str; | ||
| 2339 | const gchar *p = srcp; | ||
| 2340 | const gchar *block_encoding = out_encoding; | ||
| 2341 | gint out_str_len; | ||
| 2342 | gint out_enc_str_len; | ||
| 2343 | gint mime_block_len; | ||
| 2344 | gint error = 0; | ||
| 2345 | gboolean cont = FALSE; | ||
| 2346 | |||
| 2347 | while (*p != '\0') { | ||
| 2348 | if (*p == '"') | ||
| 2349 | in_quote ^= TRUE; | ||
| 2350 | else if (!in_quote) { | ||
| 2351 | if (g_ascii_isspace(*p) && | ||
| 2352 | !is_next_nonascii(p + 1)) | ||
| 2353 | break; | ||
| 2354 | /* don't include parentheses in encoded | ||
| 2355 | strings */ | ||
| 2356 | if (addr_field && | ||
| 2357 | (*p == '(' || *p == ')')) | ||
| 2358 | break; | ||
| 2359 | } | ||
| 2360 | |||
| 2361 | mb_len = g_utf8_skip[*(guchar *)p]; | ||
| 2362 | |||
| 2363 | part_str = g_strndup(srcp, cur_len + mb_len); | ||
| 2364 | out_str = conv_codeset_strdup_full | ||
| 2365 | (part_str, src_encoding, block_encoding, | ||
| 2366 | &error); | ||
| 2367 | if (!out_str || error != 0) { | ||
| 2368 | g_warning("conv_encode_header(): code conversion failed. Keeping UTF-8.\n"); | ||
| 2369 | out_str = g_strdup(part_str); | ||
| 2370 | block_encoding = CS_UTF_8; | ||
| 2371 | } | ||
| 2372 | out_str_len = strlen(out_str); | ||
| 2373 | |||
| 2374 | if (use_base64) | ||
| 2375 | out_enc_str_len = B64LEN(out_str_len); | ||
| 2376 | else | ||
| 2377 | out_enc_str_len = | ||
| 2378 | qp_get_q_encoding_len | ||
| 2379 | ((guchar *)out_str); | ||
| 2380 | |||
| 2381 | g_free(out_str); | ||
| 2382 | g_free(part_str); | ||
| 2383 | |||
| 2384 | if (mimestr_len + strlen(block_encoding) + out_enc_str_len <= left) { | ||
| 2385 | cur_len += mb_len; | ||
| 2386 | p += mb_len; | ||
| 2387 | } else if (cur_len == 0) { | ||
| 2388 | LBREAK_IF_REQUIRED(1, FALSE); | ||
| 2389 | if (*p == '"') | ||
| 2390 | in_quote ^= TRUE; | ||
| 2391 | continue; | ||
| 2392 | } else { | ||
| 2393 | cont = TRUE; | ||
| 2394 | if (*p == '"') | ||
| 2395 | in_quote ^= TRUE; | ||
| 2396 | break; | ||
| 2397 | } | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | if (cur_len > 0) { | ||
| 2401 | error = 0; | ||
| 2402 | part_str = g_strndup(srcp, cur_len); | ||
| 2403 | out_str = conv_codeset_strdup_full | ||
| 2404 | (part_str, src_encoding, block_encoding, | ||
| 2405 | &error); | ||
| 2406 | if (!out_str || error != 0) { | ||
| 2407 | g_warning("conv_encode_header(): code conversion failed\n"); | ||
| 2408 | out_str = g_strdup(part_str); | ||
| 2409 | block_encoding = CS_UTF_8; | ||
| 2410 | } | ||
| 2411 | out_str_len = strlen(out_str); | ||
| 2412 | |||
| 2413 | if (use_base64) | ||
| 2414 | out_enc_str_len = B64LEN(out_str_len); | ||
| 2415 | else | ||
| 2416 | out_enc_str_len = | ||
| 2417 | qp_get_q_encoding_len | ||
| 2418 | ((guchar *)out_str); | ||
| 2419 | |||
| 2420 | enc_str = g_malloc(out_enc_str_len + 1); | ||
| 2421 | if (use_base64) | ||
| 2422 | base64_encode(enc_str, | ||
| 2423 | (guchar *)out_str, | ||
| 2424 | out_str_len); | ||
| 2425 | else | ||
| 2426 | qp_q_encode(enc_str, (guchar *)out_str); | ||
| 2427 | |||
| 2428 | /* output MIME-encoded string block */ | ||
| 2429 | mime_block_len = mimestr_len + | ||
| 2430 | strlen(block_encoding) + | ||
| 2431 | strlen(enc_str); | ||
| 2432 | g_snprintf(destp, mime_block_len + 1, | ||
| 2433 | MIMESEP_BEGIN "%s%s%s" MIMESEP_END, | ||
| 2434 | block_encoding, mimesep_enc, | ||
| 2435 | enc_str); | ||
| 2436 | destp += mime_block_len; | ||
| 2437 | srcp += cur_len; | ||
| 2438 | |||
| 2439 | left -= mime_block_len; | ||
| 2440 | |||
| 2441 | g_free(enc_str); | ||
| 2442 | g_free(out_str); | ||
| 2443 | g_free(part_str); | ||
| 2444 | } | ||
| 2445 | |||
| 2446 | LBREAK_IF_REQUIRED(cont, FALSE); | ||
| 2447 | |||
| 2448 | if (cur_len == 0) | ||
| 2449 | break; | ||
| 2450 | } | ||
| 2451 | } | ||
| 2452 | |||
| 2453 | *destp = '\0'; | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | #undef LBREAK_IF_REQUIRED | ||
| 2457 | |||
| 2458 | #define INT_TO_HEX_UPPER(outp, val) \ | ||
| 2459 | { \ | ||
| 2460 | if ((val) < 10) \ | ||
| 2461 | *outp = '0' + (val); \ | ||
| 2462 | else \ | ||
| 2463 | *outp = 'A' + (val) - 10; \ | ||
| 2464 | } | ||
| 2465 | |||
| 2466 | #define IS_ESCAPE_CHAR(c) \ | ||
| 2467 | (c < 0x20 || c > 0x7f || \ | ||
| 2468 | strchr("\t \r\n*'%!#$&~`,{}|()<>@,;:\\\"/[]?=", c)) | ||
| 2469 | |||
| 2470 | static gchar *encode_rfc2231_filename(const gchar *str) | ||
| 2471 | { | ||
| 2472 | const gchar *p; | ||
| 2473 | gchar *out; | ||
| 2474 | gchar *outp; | ||
| 2475 | |||
| 2476 | outp = out = g_malloc(strlen(str) * 3 + 1); | ||
| 2477 | |||
| 2478 | for (p = str; *p != '\0'; ++p) { | ||
| 2479 | guchar ch = *(guchar *)p; | ||
| 2480 | |||
| 2481 | if (IS_ESCAPE_CHAR(ch)) { | ||
| 2482 | *outp++ = '%'; | ||
| 2483 | INT_TO_HEX_UPPER(outp, ch >> 4); | ||
| 2484 | ++outp; | ||
| 2485 | INT_TO_HEX_UPPER(outp, ch & 0x0f); | ||
| 2486 | ++outp; | ||
| 2487 | } else | ||
| 2488 | *outp++ = ch; | ||
| 2489 | } | ||
| 2490 | |||
| 2491 | *outp = '\0'; | ||
| 2492 | return out; | ||
| 2493 | } | ||
| 2494 | |||
| 2495 | gchar *conv_encode_filename(const gchar *src, const gchar *param_name, | ||
| 2496 | const gchar *out_encoding) | ||
| 2497 | { | ||
| 2498 | gint name_len, max_linelen; | ||
| 2499 | gchar *out_str, *enc_str; | ||
| 2500 | gchar cur_param[80]; | ||
| 2501 | GString *string; | ||
| 2502 | gint count = 0; | ||
| 2503 | gint cur_left_len; | ||
| 2504 | gchar *p; | ||
| 2505 | |||
| 2506 | g_return_val_if_fail(src != NULL, NULL); | ||
| 2507 | g_return_val_if_fail(param_name != NULL, NULL); | ||
| 2508 | |||
| 2509 | if (is_ascii_str(src)) | ||
| 2510 | return g_strdup_printf(" %s=\"%s\"", param_name, src); | ||
| 2511 | |||
| 2512 | name_len = strlen(param_name); | ||
| 2513 | max_linelen = MAX_LINELEN - name_len - 3; | ||
| 2514 | |||
| 2515 | if (!out_encoding) | ||
| 2516 | out_encoding = conv_get_outgoing_charset_str(); | ||
| 2517 | if (!strcmp(out_encoding, CS_US_ASCII)) | ||
| 2518 | out_encoding = CS_ISO_8859_1; | ||
| 2519 | |||
| 2520 | out_str = conv_codeset_strdup(src, CS_INTERNAL, out_encoding); | ||
| 2521 | if (!out_str) | ||
| 2522 | return NULL; | ||
| 2523 | enc_str = encode_rfc2231_filename(out_str); | ||
| 2524 | g_free(out_str); | ||
| 2525 | |||
| 2526 | if (strlen(enc_str) <= max_linelen) { | ||
| 2527 | gchar *ret; | ||
| 2528 | ret = g_strdup_printf(" %s*=%s''%s", | ||
| 2529 | param_name, out_encoding, enc_str); | ||
| 2530 | g_free(enc_str); | ||
| 2531 | return ret; | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | string = g_string_new(NULL); | ||
| 2535 | g_string_printf(string, " %s*0*=%s''", param_name, out_encoding); | ||
| 2536 | cur_left_len = MAX_LINELEN - string->len; | ||
| 2537 | |||
| 2538 | p = enc_str; | ||
| 2539 | |||
| 2540 | while (*p != '\0') { | ||
| 2541 | if ((*p == '%' && cur_left_len < 4) || | ||
| 2542 | (*p != '%' && cur_left_len < 2)) { | ||
| 2543 | gint len; | ||
| 2544 | |||
| 2545 | g_string_append(string, ";\n"); | ||
| 2546 | ++count; | ||
| 2547 | len = g_snprintf(cur_param, sizeof(cur_param), | ||
| 2548 | " %s*%d*=", param_name, count); | ||
| 2549 | g_string_append(string, cur_param); | ||
| 2550 | cur_left_len = MAX_LINELEN - len; | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | if (*p == '%') { | ||
| 2554 | g_string_append_len(string, p, 3); | ||
| 2555 | p += 3; | ||
| 2556 | cur_left_len -= 3; | ||
| 2557 | } else { | ||
| 2558 | g_string_append_c(string, *p); | ||
| 2559 | ++p; | ||
| 2560 | --cur_left_len; | ||
| 2561 | } | ||
| 2562 | } | ||
| 2563 | |||
| 2564 | g_free(enc_str); | ||
| 2565 | |||
| 2566 | return g_string_free(string, FALSE); | ||
| 2567 | } | ||
| 2568 | |||
| 2569 | static gint conv_copy_file_with_gconvert(const gchar *src, const gchar *dest, | ||
| 2570 | const gchar *encoding) | ||
| 2571 | { | ||
| 2572 | gchar *src_s = NULL; | ||
| 2573 | gsize len = 0, dlen = 0; | ||
| 2574 | gchar *dest_s; | ||
| 2575 | GError *error = NULL; | ||
| 2576 | |||
| 2577 | g_return_val_if_fail(src != NULL, -1); | ||
| 2578 | g_return_val_if_fail(dest != NULL, -1); | ||
| 2579 | g_return_val_if_fail(encoding != NULL, -1); | ||
| 2580 | |||
| 2581 | if (g_file_get_contents(src, &src_s, &len, &error) == FALSE) { | ||
| 2582 | g_warning("conv_copy_utf16_file(): %s: %s", src, error->message); | ||
| 2583 | g_error_free(error); | ||
| 2584 | return -1; | ||
| 2585 | } | ||
| 2586 | |||
| 2587 | dest_s = g_convert(src_s, len, CS_UTF_8, encoding, NULL, &dlen, &error); | ||
| 2588 | if (!dest_s) { | ||
| 2589 | g_warning("conv_copy_utf16_file(): %s: %s", src, error->message); | ||
| 2590 | g_error_free(error); | ||
| 2591 | g_free(src_s); | ||
| 2592 | return -1; | ||
| 2593 | } | ||
| 2594 | |||
| 2595 | if (g_file_set_contents(dest, dest_s, dlen, &error) == FALSE) { | ||
| 2596 | g_warning("conv_copy_utf16_file(): %s: %s", dest, error->message); | ||
| 2597 | g_error_free(error); | ||
| 2598 | g_free(dest_s); | ||
| 2599 | g_free(src_s); | ||
| 2600 | return -1; | ||
| 2601 | } | ||
| 2602 | |||
| 2603 | g_free(dest_s); | ||
| 2604 | g_free(src_s); | ||
| 2605 | return 0; | ||
| 2606 | } | ||
| 2607 | |||
| 2608 | gint conv_copy_file(const gchar *src, const gchar *dest, const gchar *encoding) | ||
| 2609 | { | ||
| 2610 | FILE *src_fp, *dest_fp; | ||
| 2611 | gchar buf[BUFFSIZE]; | ||
| 2612 | CodeConverter *conv; | ||
| 2613 | gboolean err = FALSE; | ||
| 2614 | CharSet charset; | ||
| 2615 | |||
| 2616 | charset = conv_get_charset_from_str(encoding); | ||
| 2617 | if (charset == C_UTF_16 || charset == C_UTF_16BE || charset == C_UTF_16LE) { | ||
| 2618 | return conv_copy_file_with_gconvert(src, dest, encoding); | ||
| 2619 | } | ||
| 2620 | |||
| 2621 | if ((src_fp = g_fopen(src, "rb")) == NULL) { | ||
| 2622 | FILE_OP_ERROR(src, "fopen"); | ||
| 2623 | return -1; | ||
| 2624 | } | ||
| 2625 | if ((dest_fp = g_fopen(dest, "wb")) == NULL) { | ||
| 2626 | FILE_OP_ERROR(dest, "fopen"); | ||
| 2627 | fclose(src_fp); | ||
| 2628 | return -1; | ||
| 2629 | } | ||
| 2630 | |||
| 2631 | if (change_file_mode_rw(dest_fp, dest) < 0) { | ||
| 2632 | FILE_OP_ERROR(dest, "chmod"); | ||
| 2633 | g_warning("can't change file mode\n"); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | conv = conv_code_converter_new(encoding, NULL); | ||
| 2637 | |||
| 2638 | while (fgets(buf, sizeof(buf), src_fp) != NULL) { | ||
| 2639 | gchar *outbuf; | ||
| 2640 | |||
| 2641 | outbuf = conv_convert(conv, buf); | ||
| 2642 | if (outbuf) { | ||
| 2643 | fputs(outbuf, dest_fp); | ||
| 2644 | g_free(outbuf); | ||
| 2645 | } else | ||
| 2646 | fputs(buf, dest_fp); | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | conv_code_converter_destroy(conv); | ||
| 2650 | |||
| 2651 | if (ferror(src_fp)) { | ||
| 2652 | FILE_OP_ERROR(src, "fgets"); | ||
| 2653 | err = TRUE; | ||
| 2654 | } | ||
| 2655 | fclose(src_fp); | ||
| 2656 | if (fclose(dest_fp) == EOF) { | ||
| 2657 | FILE_OP_ERROR(dest, "fclose"); | ||
| 2658 | err = TRUE; | ||
| 2659 | } | ||
| 2660 | if (err) { | ||
| 2661 | g_unlink(dest); | ||
| 2662 | return -1; | ||
| 2663 | } | ||
| 2664 | |||
| 2665 | return 0; | ||
| 2666 | } | ||
| 2667 | |||
| 2668 | gint conv_copy_dir(const gchar *src, const gchar *dest, const gchar *encoding) | ||
| 2669 | { | ||
| 2670 | GDir *dir; | ||
| 2671 | const gchar *dir_name; | ||
| 2672 | gchar *src_file; | ||
| 2673 | gchar *dest_file; | ||
| 2674 | |||
| 2675 | if ((dir = g_dir_open(src, 0, NULL)) == NULL) { | ||
| 2676 | g_warning("failed to open directory: %s\n", src); | ||
| 2677 | return -1; | ||
| 2678 | } | ||
| 2679 | |||
| 2680 | if (make_dir_hier(dest) < 0) { | ||
| 2681 | g_dir_close(dir); | ||
| 2682 | return -1; | ||
| 2683 | } | ||
| 2684 | |||
| 2685 | while ((dir_name = g_dir_read_name(dir)) != NULL) { | ||
| 2686 | src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); | ||
| 2687 | dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, | ||
| 2688 | NULL); | ||
| 2689 | if (is_file_exist(src_file)) | ||
| 2690 | conv_copy_file(src_file, dest_file, encoding); | ||
| 2691 | g_free(dest_file); | ||
| 2692 | g_free(src_file); | ||
| 2693 | } | ||
| 2694 | |||
| 2695 | g_dir_close(dir); | ||
| 2696 | |||
| 2697 | return 0; | ||
| 2698 | } | ||
| 2699 | |||
| 2700 | CharSet conv_check_file_encoding(const gchar *file) | ||
| 2701 | { | ||
| 2702 | FILE *fp; | ||
| 2703 | gchar buf[BUFFSIZE]; | ||
| 2704 | CharSet enc; | ||
| 2705 | const gchar *enc_str; | ||
| 2706 | gboolean is_locale = TRUE, is_utf8 = TRUE; | ||
| 2707 | size_t size; | ||
| 2708 | |||
| 2709 | g_return_val_if_fail(file != NULL, C_AUTO); | ||
| 2710 | |||
| 2711 | enc = conv_get_locale_charset(); | ||
| 2712 | enc_str = conv_get_locale_charset_str(); | ||
| 2713 | if (enc == C_UTF_8) | ||
| 2714 | is_locale = FALSE; | ||
| 2715 | |||
| 2716 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 2717 | FILE_OP_ERROR(file, "fopen"); | ||
| 2718 | return C_AUTO; | ||
| 2719 | } | ||
| 2720 | |||
| 2721 | /* UTF-16 check */ | ||
| 2722 | if ((size = fread(buf, 2, BUFFSIZE / 2, fp)) > 0) { | ||
| 2723 | CharSet guess_enc = C_AUTO; | ||
| 2724 | |||
| 2725 | debug_print("conv_check_file_encoding: check first %d bytes of file %s\n", size * 2, file); | ||
| 2726 | |||
| 2727 | /* BOM check */ | ||
| 2728 | if ((buf[0] & 0xff) == 0xfe && (buf[1] & 0xff) == 0xff) { | ||
| 2729 | debug_print("conv_check_file_encoding: UTF-16 BOM (BE) found\n"); | ||
| 2730 | guess_enc = C_UTF_16; /* UTF-16BE */ | ||
| 2731 | } else if ((buf[0] & 0xff) == 0xff && (buf[1] & 0xff) == 0xfe) { | ||
| 2732 | debug_print("conv_check_file_encoding: UTF-16 BOM (LE) found\n"); | ||
| 2733 | guess_enc = C_UTF_16; /* UTF-16LE */ | ||
| 2734 | } | ||
| 2735 | if (guess_enc != C_AUTO) { | ||
| 2736 | fclose(fp); | ||
| 2737 | return guess_enc; | ||
| 2738 | } | ||
| 2739 | |||
| 2740 | /* search UTF-16 CR/LF */ | ||
| 2741 | if (memchr(buf, 0x00, size * 2) != NULL) { | ||
| 2742 | gint i; | ||
| 2743 | guchar c1, c2; | ||
| 2744 | |||
| 2745 | for (i = 0; i < size; i++) { | ||
| 2746 | c1 = buf[i * 2] & 0xff; | ||
| 2747 | c2 = buf[i * 2 + 1] & 0xff; | ||
| 2748 | if (c1 == 0x00 && c2 == 0x0d) { /* UTF-16BE CR */ | ||
| 2749 | i++; | ||
| 2750 | if (i >= size) { | ||
| 2751 | break; | ||
| 2752 | } | ||
| 2753 | c1 = buf[i * 2] & 0xff; | ||
| 2754 | c2 = buf[i * 2 + 1] & 0xff; | ||
| 2755 | if (c1 == 0x00 && c2 == 0x0a) { /* UTF-16BE LF */ | ||
| 2756 | guess_enc = C_UTF_16BE; | ||
| 2757 | break; | ||
| 2758 | } | ||
| 2759 | } else if (c1 == 0x0d && c2 == 0x00) { /* UTF-16LE CR */ | ||
| 2760 | i++; | ||
| 2761 | if (i >= size) { | ||
| 2762 | break; | ||
| 2763 | } | ||
| 2764 | c1 = buf[i * 2] & 0xff; | ||
| 2765 | c2 = buf[i * 2 + 1] & 0xff; | ||
| 2766 | if (c1 == 0x0a && c2 == 0x00) { /* UTF-16LE LF */ | ||
| 2767 | guess_enc = C_UTF_16LE; | ||
| 2768 | break; | ||
| 2769 | } | ||
| 2770 | } else if (c1 == 0x00 && c2 == 0x0a) { /* UTF-16BE LF */ | ||
| 2771 | guess_enc = C_UTF_16BE; | ||
| 2772 | break; | ||
| 2773 | } else if (c1 == 0x0a && c2 == 0x00) { /* UTF-16LE LF */ | ||
| 2774 | guess_enc = C_UTF_16LE; | ||
| 2775 | break; | ||
| 2776 | } | ||
| 2777 | } | ||
| 2778 | |||
| 2779 | if (guess_enc != C_AUTO) { | ||
| 2780 | debug_print("conv_check_file_encoding: %s detected\n", | ||
| 2781 | conv_get_charset_str(guess_enc)); | ||
| 2782 | fclose(fp); | ||
| 2783 | return guess_enc; | ||
| 2784 | } | ||
| 2785 | } | ||
| 2786 | } | ||
| 2787 | |||
| 2788 | rewind(fp); | ||
| 2789 | |||
| 2790 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 2791 | gchar *str; | ||
| 2792 | gint error = 0; | ||
| 2793 | |||
| 2794 | if (is_locale) { | ||
| 2795 | str = conv_codeset_strdup_full(buf, enc_str, | ||
| 2796 | CS_INTERNAL, &error); | ||
| 2797 | if (!str || error != 0) | ||
| 2798 | is_locale = FALSE; | ||
| 2799 | g_free(str); | ||
| 2800 | } | ||
| 2801 | |||
| 2802 | if (is_utf8 && g_utf8_validate(buf, -1, NULL) == FALSE) { | ||
| 2803 | is_utf8 = FALSE; | ||
| 2804 | } | ||
| 2805 | |||
| 2806 | if (!is_locale && !is_utf8) | ||
| 2807 | break; | ||
| 2808 | } | ||
| 2809 | |||
| 2810 | fclose(fp); | ||
| 2811 | |||
| 2812 | if (is_locale) | ||
| 2813 | return enc; | ||
| 2814 | else if (is_utf8) | ||
| 2815 | return C_UTF_8; | ||
| 2816 | else | ||
| 2817 | return C_AUTO; | ||
| 2818 | } | ||
| 2819 | |||
| 2820 | gchar *conv_filename_from_utf8(const gchar *utf8_file) | ||
| 2821 | { | ||
| 2822 | gchar *fs_file; | ||
| 2823 | GError *error = NULL; | ||
| 2824 | |||
| 2825 | g_return_val_if_fail(utf8_file != NULL, NULL); | ||
| 2826 | |||
| 2827 | fs_file = g_filename_from_utf8(utf8_file, -1, NULL, NULL, &error); | ||
| 2828 | if (error) { | ||
| 2829 | g_warning("failed to convert encoding of file name: %s\n", | ||
| 2830 | error->message); | ||
| 2831 | g_error_free(error); | ||
| 2832 | } | ||
| 2833 | if (!fs_file) | ||
| 2834 | fs_file = g_strdup(utf8_file); | ||
| 2835 | |||
| 2836 | return fs_file; | ||
| 2837 | } | ||
| 2838 | |||
| 2839 | gchar *conv_filename_to_utf8(const gchar *fs_file) | ||
| 2840 | { | ||
| 2841 | gchar *utf8_file; | ||
| 2842 | GError *error = NULL; | ||
| 2843 | |||
| 2844 | g_return_val_if_fail(fs_file != NULL, NULL); | ||
| 2845 | |||
| 2846 | utf8_file = g_filename_to_utf8(fs_file, -1, NULL, NULL, &error); | ||
| 2847 | if (error) { | ||
| 2848 | g_warning("failed to convert encoding of file name: %s\n", | ||
| 2849 | error->message); | ||
| 2850 | g_error_free(error); | ||
| 2851 | } | ||
| 2852 | if (!utf8_file) | ||
| 2853 | utf8_file = g_strdup(fs_file); | ||
| 2854 | |||
| 2855 | return utf8_file; | ||
| 2856 | } | ||
diff --git a/libsylph/codeconv.h b/libsylph/codeconv.h new file mode 100644 index 0000000..5143fd4 --- /dev/null +++ b/libsylph/codeconv.h | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __CODECONV_H__ | ||
| 21 | #define __CODECONV_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <iconv.h> | ||
| 29 | |||
| 30 | typedef struct _CodeConverter CodeConverter; | ||
| 31 | |||
| 32 | typedef enum | ||
| 33 | { | ||
| 34 | C_AUTO, | ||
| 35 | C_US_ASCII, | ||
| 36 | C_UTF_8, | ||
| 37 | C_UTF_7, | ||
| 38 | C_ISO_8859_1, | ||
| 39 | C_ISO_8859_2, | ||
| 40 | C_ISO_8859_3, | ||
| 41 | C_ISO_8859_4, | ||
| 42 | C_ISO_8859_5, | ||
| 43 | C_ISO_8859_6, | ||
| 44 | C_ISO_8859_7, | ||
| 45 | C_ISO_8859_8, | ||
| 46 | C_ISO_8859_9, | ||
| 47 | C_ISO_8859_10, | ||
| 48 | C_ISO_8859_11, | ||
| 49 | C_ISO_8859_13, | ||
| 50 | C_ISO_8859_14, | ||
| 51 | C_ISO_8859_15, | ||
| 52 | C_BALTIC, | ||
| 53 | C_CP932, | ||
| 54 | C_CP1250, | ||
| 55 | C_CP1251, | ||
| 56 | C_CP1252, | ||
| 57 | C_CP1253, | ||
| 58 | C_CP1254, | ||
| 59 | C_CP1255, | ||
| 60 | C_CP1256, | ||
| 61 | C_CP1257, | ||
| 62 | C_CP1258, | ||
| 63 | C_WINDOWS_932, | ||
| 64 | C_WINDOWS_1250, | ||
| 65 | C_WINDOWS_1251, | ||
| 66 | C_WINDOWS_1252, | ||
| 67 | C_WINDOWS_1253, | ||
| 68 | C_WINDOWS_1254, | ||
| 69 | C_WINDOWS_1255, | ||
| 70 | C_WINDOWS_1256, | ||
| 71 | C_WINDOWS_1257, | ||
| 72 | C_WINDOWS_1258, | ||
| 73 | C_KOI8_R, | ||
| 74 | C_KOI8_T, | ||
| 75 | C_KOI8_U, | ||
| 76 | C_ISO_2022_JP, | ||
| 77 | C_ISO_2022_JP_2, | ||
| 78 | C_ISO_2022_JP_3, | ||
| 79 | C_EUC_JP, | ||
| 80 | C_EUC_JP_MS, | ||
| 81 | C_SHIFT_JIS, | ||
| 82 | C_ISO_2022_KR, | ||
| 83 | C_EUC_KR, | ||
| 84 | C_ISO_2022_CN, | ||
| 85 | C_EUC_CN, | ||
| 86 | C_GB2312, | ||
| 87 | C_GBK, | ||
| 88 | C_EUC_TW, | ||
| 89 | C_BIG5, | ||
| 90 | C_BIG5_HKSCS, | ||
| 91 | C_TIS_620, | ||
| 92 | C_WINDOWS_874, | ||
| 93 | C_GEORGIAN_PS, | ||
| 94 | C_TCVN5712_1, | ||
| 95 | C_ISO_8859_16, | ||
| 96 | C_UTF_16, | ||
| 97 | C_UTF_16BE, | ||
| 98 | C_UTF_16LE | ||
| 99 | } CharSet; | ||
| 100 | |||
| 101 | typedef enum | ||
| 102 | { | ||
| 103 | C_AD_BY_LOCALE, | ||
| 104 | C_AD_NEVER, | ||
| 105 | C_AD_JAPANESE | ||
| 106 | } ConvADType; | ||
| 107 | |||
| 108 | typedef gchar *(*CodeConvFunc) (const gchar *inbuf, gint *error); | ||
| 109 | |||
| 110 | struct _CodeConverter | ||
| 111 | { | ||
| 112 | CodeConvFunc code_conv_func; | ||
| 113 | gchar *src_encoding; | ||
| 114 | gchar *dest_encoding; | ||
| 115 | }; | ||
| 116 | |||
| 117 | #define CS_AUTO "AUTO" | ||
| 118 | #define CS_US_ASCII "US-ASCII" | ||
| 119 | #define CS_ANSI_X3_4_1968 "ANSI_X3.4-1968" | ||
| 120 | #define CS_UTF_8 "UTF-8" | ||
| 121 | #define CS_UTF_7 "UTF-7" | ||
| 122 | #define CS_ISO_8859_1 "ISO-8859-1" | ||
| 123 | #define CS_ISO_8859_2 "ISO-8859-2" | ||
| 124 | #define CS_ISO_8859_3 "ISO-8859-3" | ||
| 125 | #define CS_ISO_8859_4 "ISO-8859-4" | ||
| 126 | #define CS_ISO_8859_5 "ISO-8859-5" | ||
| 127 | #define CS_ISO_8859_6 "ISO-8859-6" | ||
| 128 | #define CS_ISO_8859_7 "ISO-8859-7" | ||
| 129 | #define CS_ISO_8859_8 "ISO-8859-8" | ||
| 130 | #define CS_ISO_8859_9 "ISO-8859-9" | ||
| 131 | #define CS_ISO_8859_10 "ISO-8859-10" | ||
| 132 | #define CS_ISO_8859_11 "ISO-8859-11" | ||
| 133 | #define CS_ISO_8859_13 "ISO-8859-13" | ||
| 134 | #define CS_ISO_8859_14 "ISO-8859-14" | ||
| 135 | #define CS_ISO_8859_15 "ISO-8859-15" | ||
| 136 | #define CS_BALTIC "BALTIC" | ||
| 137 | #define CS_CP932 "CP932" | ||
| 138 | #define CS_CP1250 "CP1250" | ||
| 139 | #define CS_CP1251 "CP1251" | ||
| 140 | #define CS_CP1252 "CP1252" | ||
| 141 | #define CS_CP1253 "CP1253" | ||
| 142 | #define CS_CP1254 "CP1254" | ||
| 143 | #define CS_CP1255 "CP1255" | ||
| 144 | #define CS_CP1256 "CP1256" | ||
| 145 | #define CS_CP1257 "CP1257" | ||
| 146 | #define CS_CP1258 "CP1258" | ||
| 147 | #define CS_WINDOWS_932 "Windows-932" | ||
| 148 | #define CS_WINDOWS_1250 "Windows-1250" | ||
| 149 | #define CS_WINDOWS_1251 "Windows-1251" | ||
| 150 | #define CS_WINDOWS_1252 "Windows-1252" | ||
| 151 | #define CS_WINDOWS_1253 "Windows-1253" | ||
| 152 | #define CS_WINDOWS_1254 "Windows-1254" | ||
| 153 | #define CS_WINDOWS_1255 "Windows-1255" | ||
| 154 | #define CS_WINDOWS_1256 "Windows-1256" | ||
| 155 | #define CS_WINDOWS_1257 "Windows-1257" | ||
| 156 | #define CS_WINDOWS_1258 "Windows-1258" | ||
| 157 | #define CS_KOI8_R "KOI8-R" | ||
| 158 | #define CS_KOI8_T "KOI8-T" | ||
| 159 | #define CS_KOI8_U "KOI8-U" | ||
| 160 | #define CS_ISO_2022_JP "ISO-2022-JP" | ||
| 161 | #define CS_ISO_2022_JP_2 "ISO-2022-JP-2" | ||
| 162 | #define CS_ISO_2022_JP_3 "ISO-2022-JP-3" | ||
| 163 | #define CS_EUC_JP "EUC-JP" | ||
| 164 | #define CS_EUCJP "EUCJP" | ||
| 165 | #define CS_EUC_JP_MS "EUC-JP-MS" | ||
| 166 | #define CS_SHIFT_JIS "Shift_JIS" | ||
| 167 | #define CS_SHIFT__JIS "SHIFT-JIS" | ||
| 168 | #define CS_SJIS "SJIS" | ||
| 169 | #define CS_X_SJIS "X-SJIS" | ||
| 170 | #define CS_ISO_2022_KR "ISO-2022-KR" | ||
| 171 | #define CS_EUC_KR "EUC-KR" | ||
| 172 | #define CS_KS_C_5601_1987 "ks_c_5601-1987" | ||
| 173 | #define CS_ISO_2022_CN "ISO-2022-CN" | ||
| 174 | #define CS_EUC_CN "EUC-CN" | ||
| 175 | #define CS_GB2312 "GB2312" | ||
| 176 | #define CS_GBK "GBK" | ||
| 177 | #define CS_X_GBK "X-GBK" | ||
| 178 | #define CS_EUC_TW "EUC-TW" | ||
| 179 | #define CS_BIG5 "Big5" | ||
| 180 | #define CS_BIG5_HKSCS "BIG5-HKSCS" | ||
| 181 | #define CS_TIS_620 "TIS-620" | ||
| 182 | #define CS_WINDOWS_874 "Windows-874" | ||
| 183 | #define CS_GEORGIAN_PS "GEORGIAN-PS" | ||
| 184 | #define CS_TCVN5712_1 "TCVN5712-1" | ||
| 185 | #define CS_ISO_8859_16 "ISO-8859-16" | ||
| 186 | #define CS_UTF_16 "UTF-16" | ||
| 187 | #define CS_UTF_16BE "UTF-16BE" | ||
| 188 | #define CS_UTF_16LE "UTF-16LE" | ||
| 189 | |||
| 190 | #define C_INTERNAL C_UTF_8 | ||
| 191 | #define CS_INTERNAL CS_UTF_8 | ||
| 192 | |||
| 193 | //void conv_mb_alnum(gchar *str); | ||
| 194 | |||
| 195 | CharSet conv_guess_ja_encoding (const gchar *str); | ||
| 196 | |||
| 197 | gchar *conv_utf8todisp (const gchar *inbuf, | ||
| 198 | gint *error); | ||
| 199 | gchar *conv_localetodisp (const gchar *inbuf, | ||
| 200 | gint *error); | ||
| 201 | |||
| 202 | CodeConverter *conv_code_converter_new (const gchar *src_encoding, | ||
| 203 | const gchar *dest_encoding); | ||
| 204 | void conv_code_converter_destroy (CodeConverter *conv); | ||
| 205 | gchar *conv_convert (CodeConverter *conv, | ||
| 206 | const gchar *inbuf); | ||
| 207 | |||
| 208 | #define conv_codeset_strdup(inbuf, src_code, dest_code) \ | ||
| 209 | (conv_codeset_strdup_full(inbuf, src_code, dest_code, NULL)) | ||
| 210 | |||
| 211 | gchar *conv_codeset_strdup_full (const gchar *inbuf, | ||
| 212 | const gchar *src_encoding, | ||
| 213 | const gchar *dest_encoding, | ||
| 214 | gint *error); | ||
| 215 | |||
| 216 | CodeConvFunc conv_get_code_conv_func (const gchar *src_encoding, | ||
| 217 | const gchar *dest_encoding); | ||
| 218 | |||
| 219 | gchar *conv_iconv_strdup (const gchar *inbuf, | ||
| 220 | const gchar *src_encoding, | ||
| 221 | const gchar *dest_encoding, | ||
| 222 | gint *error); | ||
| 223 | gchar *conv_iconv_strdup_with_cd (const gchar *inbuf, | ||
| 224 | iconv_t cd, | ||
| 225 | gint *error); | ||
| 226 | |||
| 227 | const gchar *conv_get_charset_str (CharSet charset); | ||
| 228 | CharSet conv_get_charset_from_str (const gchar *charset); | ||
| 229 | CharSet conv_get_locale_charset (void); | ||
| 230 | const gchar *conv_get_locale_charset_str (void); | ||
| 231 | CharSet conv_get_internal_charset (void); | ||
| 232 | const gchar *conv_get_internal_charset_str (void); | ||
| 233 | CharSet conv_get_outgoing_charset (void); | ||
| 234 | const gchar *conv_get_outgoing_charset_str (void); | ||
| 235 | gboolean conv_is_multibyte_encoding (CharSet encoding); | ||
| 236 | |||
| 237 | const gchar *conv_get_current_locale (void); | ||
| 238 | gboolean conv_is_ja_locale (void); | ||
| 239 | |||
| 240 | void conv_set_autodetect_type (ConvADType type); | ||
| 241 | ConvADType conv_get_autodetect_type (void); | ||
| 242 | |||
| 243 | gchar *conv_unmime_header (const gchar *str, | ||
| 244 | const gchar *default_encoding); | ||
| 245 | void conv_encode_header (gchar *dest, | ||
| 246 | gint len, | ||
| 247 | const gchar *src, | ||
| 248 | gint header_len, | ||
| 249 | gboolean addr_field, | ||
| 250 | const gchar *out_encoding); | ||
| 251 | gchar *conv_encode_filename (const gchar *src, | ||
| 252 | const gchar *param_name, | ||
| 253 | const gchar *out_encoding); | ||
| 254 | |||
| 255 | gint conv_copy_file (const gchar *src, | ||
| 256 | const gchar *dest, | ||
| 257 | const gchar *src_encoding); | ||
| 258 | gint conv_copy_dir (const gchar *src, | ||
| 259 | const gchar *dest, | ||
| 260 | const gchar *src_encoding); | ||
| 261 | |||
| 262 | CharSet conv_check_file_encoding (const gchar *file); | ||
| 263 | |||
| 264 | gchar *conv_filename_from_utf8 (const gchar *utf8_file); | ||
| 265 | gchar *conv_filename_to_utf8 (const gchar *fs_file); | ||
| 266 | |||
| 267 | #endif /* __CODECONV_H__ */ | ||
diff --git a/libsylph/customheader.c b/libsylph/customheader.c new file mode 100644 index 0000000..0f4d4a0 --- /dev/null +++ b/libsylph/customheader.c | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <stdio.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <stdlib.h> | ||
| 30 | #include <errno.h> | ||
| 31 | |||
| 32 | #include "customheader.h" | ||
| 33 | #include "prefs.h" | ||
| 34 | #include "prefs_account.h" | ||
| 35 | #include "utils.h" | ||
| 36 | |||
| 37 | |||
| 38 | void custom_header_read_config(PrefsAccount *ac) | ||
| 39 | { | ||
| 40 | gchar *rcpath; | ||
| 41 | FILE *fp; | ||
| 42 | gchar buf[PREFSBUFSIZE]; | ||
| 43 | CustomHeader *ch; | ||
| 44 | |||
| 45 | debug_print("Reading custom header configuration...\n"); | ||
| 46 | |||
| 47 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 48 | CUSTOM_HEADER_RC, NULL); | ||
| 49 | if ((fp = g_fopen(rcpath, "rb")) == NULL) { | ||
| 50 | if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); | ||
| 51 | g_free(rcpath); | ||
| 52 | ac->customhdr_list = NULL; | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | g_free(rcpath); | ||
| 56 | |||
| 57 | /* remove all previous headers list */ | ||
| 58 | while (ac->customhdr_list != NULL) { | ||
| 59 | ch = (CustomHeader *)ac->customhdr_list->data; | ||
| 60 | custom_header_free(ch); | ||
| 61 | ac->customhdr_list = g_slist_remove(ac->customhdr_list, ch); | ||
| 62 | } | ||
| 63 | |||
| 64 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 65 | ch = custom_header_read_str(buf); | ||
| 66 | if (ch) { | ||
| 67 | if (ch->account_id == ac->account_id) { | ||
| 68 | ac->customhdr_list = | ||
| 69 | g_slist_append(ac->customhdr_list, ch); | ||
| 70 | } else | ||
| 71 | custom_header_free(ch); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | fclose(fp); | ||
| 76 | } | ||
| 77 | |||
| 78 | void custom_header_write_config(PrefsAccount *ac) | ||
| 79 | { | ||
| 80 | gchar *rcpath; | ||
| 81 | PrefFile *pfile; | ||
| 82 | GSList *cur; | ||
| 83 | gchar buf[PREFSBUFSIZE]; | ||
| 84 | FILE * fp; | ||
| 85 | CustomHeader *ch; | ||
| 86 | |||
| 87 | GSList *all_hdrs = NULL; | ||
| 88 | |||
| 89 | debug_print("Writing custom header configuration...\n"); | ||
| 90 | |||
| 91 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 92 | CUSTOM_HEADER_RC, NULL); | ||
| 93 | |||
| 94 | if ((fp = g_fopen(rcpath, "rb")) == NULL) { | ||
| 95 | if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); | ||
| 96 | } else { | ||
| 97 | all_hdrs = NULL; | ||
| 98 | |||
| 99 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 100 | ch = custom_header_read_str(buf); | ||
| 101 | if (ch) { | ||
| 102 | if (ch->account_id != ac->account_id) | ||
| 103 | all_hdrs = | ||
| 104 | g_slist_append(all_hdrs, ch); | ||
| 105 | else | ||
| 106 | custom_header_free(ch); | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | fclose(fp); | ||
| 111 | } | ||
| 112 | |||
| 113 | if ((pfile = prefs_file_open(rcpath)) == NULL) { | ||
| 114 | g_warning("failed to write configuration to file\n"); | ||
| 115 | g_free(rcpath); | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | |||
| 119 | for (cur = all_hdrs; cur != NULL; cur = cur->next) { | ||
| 120 | CustomHeader *hdr = (CustomHeader *)cur->data; | ||
| 121 | gchar *chstr; | ||
| 122 | |||
| 123 | chstr = custom_header_get_str(hdr); | ||
| 124 | if (fputs(chstr, pfile->fp) == EOF || | ||
| 125 | fputc('\n', pfile->fp) == EOF) { | ||
| 126 | FILE_OP_ERROR(rcpath, "fputs || fputc"); | ||
| 127 | prefs_file_close_revert(pfile); | ||
| 128 | g_free(rcpath); | ||
| 129 | g_free(chstr); | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | g_free(chstr); | ||
| 133 | } | ||
| 134 | |||
| 135 | for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) { | ||
| 136 | CustomHeader *hdr = (CustomHeader *)cur->data; | ||
| 137 | gchar *chstr; | ||
| 138 | |||
| 139 | chstr = custom_header_get_str(hdr); | ||
| 140 | if (fputs(chstr, pfile->fp) == EOF || | ||
| 141 | fputc('\n', pfile->fp) == EOF) { | ||
| 142 | FILE_OP_ERROR(rcpath, "fputs || fputc"); | ||
| 143 | prefs_file_close_revert(pfile); | ||
| 144 | g_free(rcpath); | ||
| 145 | g_free(chstr); | ||
| 146 | return; | ||
| 147 | } | ||
| 148 | g_free(chstr); | ||
| 149 | } | ||
| 150 | |||
| 151 | g_free(rcpath); | ||
| 152 | |||
| 153 | while (all_hdrs != NULL) { | ||
| 154 | ch = (CustomHeader *)all_hdrs->data; | ||
| 155 | custom_header_free(ch); | ||
| 156 | all_hdrs = g_slist_remove(all_hdrs, ch); | ||
| 157 | } | ||
| 158 | |||
| 159 | if (prefs_file_close(pfile) < 0) { | ||
| 160 | g_warning("failed to write configuration to file\n"); | ||
| 161 | return; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | gchar *custom_header_get_str(CustomHeader *ch) | ||
| 166 | { | ||
| 167 | return g_strdup_printf("%i:%s: %s", | ||
| 168 | ch->account_id, ch->name, | ||
| 169 | ch->value ? ch->value : ""); | ||
| 170 | } | ||
| 171 | |||
| 172 | CustomHeader *custom_header_read_str(const gchar *buf) | ||
| 173 | { | ||
| 174 | CustomHeader *ch; | ||
| 175 | gchar *account_id_str; | ||
| 176 | gint id; | ||
| 177 | gchar *name; | ||
| 178 | gchar *value; | ||
| 179 | gchar *tmp; | ||
| 180 | |||
| 181 | Xstrdup_a(tmp, buf, return NULL); | ||
| 182 | |||
| 183 | account_id_str = tmp; | ||
| 184 | |||
| 185 | name = strchr(account_id_str, ':'); | ||
| 186 | if (!name) | ||
| 187 | return NULL; | ||
| 188 | else { | ||
| 189 | gchar *endp; | ||
| 190 | |||
| 191 | *name++ = '\0'; | ||
| 192 | id = strtol(account_id_str, &endp, 10); | ||
| 193 | if (*endp != '\0') return NULL; | ||
| 194 | } | ||
| 195 | |||
| 196 | value = strchr(name, ':'); | ||
| 197 | if (!value) return NULL; | ||
| 198 | |||
| 199 | *value++ = '\0'; | ||
| 200 | |||
| 201 | g_strstrip(name); | ||
| 202 | g_strstrip(value); | ||
| 203 | |||
| 204 | ch = g_new0(CustomHeader, 1); | ||
| 205 | ch->account_id = id; | ||
| 206 | ch->name = *name ? g_strdup(name) : NULL; | ||
| 207 | ch->value = *value ? g_strdup(value) : NULL; | ||
| 208 | |||
| 209 | return ch; | ||
| 210 | } | ||
| 211 | |||
| 212 | CustomHeader *custom_header_find(GSList *header_list, const gchar *header) | ||
| 213 | { | ||
| 214 | GSList *cur; | ||
| 215 | CustomHeader *chdr; | ||
| 216 | |||
| 217 | for (cur = header_list; cur != NULL; cur = cur->next) { | ||
| 218 | chdr = (CustomHeader *)cur->data; | ||
| 219 | if (!g_ascii_strcasecmp(chdr->name, header)) | ||
| 220 | return chdr; | ||
| 221 | } | ||
| 222 | |||
| 223 | return NULL; | ||
| 224 | } | ||
| 225 | |||
| 226 | void custom_header_free(CustomHeader *ch) | ||
| 227 | { | ||
| 228 | if (!ch) return; | ||
| 229 | |||
| 230 | g_free(ch->name); | ||
| 231 | g_free(ch->value); | ||
| 232 | g_free(ch); | ||
| 233 | } | ||
diff --git a/libsylph/customheader.h b/libsylph/customheader.h new file mode 100644 index 0000000..66e6cc4 --- /dev/null +++ b/libsylph/customheader.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __CUSTOMHEADER_H__ | ||
| 21 | #define __CUSTOMHEADER_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | typedef struct _CustomHeader CustomHeader; | ||
| 26 | |||
| 27 | #include "prefs_account.h" | ||
| 28 | |||
| 29 | struct _CustomHeader | ||
| 30 | { | ||
| 31 | gint account_id; | ||
| 32 | gchar *name; | ||
| 33 | gchar *value; | ||
| 34 | }; | ||
| 35 | |||
| 36 | void custom_header_read_config (PrefsAccount *ac); | ||
| 37 | void custom_header_write_config (PrefsAccount *ac); | ||
| 38 | |||
| 39 | gchar *custom_header_get_str (CustomHeader *ch); | ||
| 40 | CustomHeader *custom_header_read_str (const gchar *buf); | ||
| 41 | CustomHeader *custom_header_find (GSList *header_list, | ||
| 42 | const gchar *header); | ||
| 43 | void custom_header_free (CustomHeader *ch); | ||
| 44 | |||
| 45 | #endif /* __CUSTOMHEADER_H__ */ | ||
diff --git a/libsylph/defs.h b/libsylph/defs.h new file mode 100644 index 0000000..9e3f82b --- /dev/null +++ b/libsylph/defs.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __DEFS_H__ | ||
| 21 | #define __DEFS_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | |||
| 29 | #if HAVE_PATHS_H | ||
| 30 | # include <paths.h> | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #if HAVE_SYS_PARAM_H | ||
| 34 | # include <sys/param.h> | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #define INBOX_DIR "inbox" | ||
| 38 | #define OUTBOX_DIR "sent" | ||
| 39 | #define QUEUE_DIR "queue" | ||
| 40 | #define DRAFT_DIR "draft" | ||
| 41 | #define TRASH_DIR "trash" | ||
| 42 | #define JUNK_DIR "junk" | ||
| 43 | #if defined(G_OS_WIN32) || defined(__APPLE__) | ||
| 44 | # define RC_DIR "Sylpheed" | ||
| 45 | #else | ||
| 46 | # define RC_DIR ".sylpheed-2.0" | ||
| 47 | #endif | ||
| 48 | #define OLD_RC_DIR ".sylpheed" | ||
| 49 | #define NEWS_CACHE_DIR "newscache" | ||
| 50 | #define IMAP_CACHE_DIR "imapcache" | ||
| 51 | #define MIME_TMP_DIR "mimetmp" | ||
| 52 | #define COMMON_RC "sylpheedrc" | ||
| 53 | #define ACCOUNT_RC "accountrc" | ||
| 54 | #define FILTER_RC "filterrc" | ||
| 55 | #define FILTER_LIST "filter.xml" | ||
| 56 | #define FILTER_HEADER_RC "filterheaderrc" | ||
| 57 | #define CUSTOM_HEADER_RC "customheaderrc" | ||
| 58 | #define DISPLAY_HEADER_RC "dispheaderrc" | ||
| 59 | #define MENU_RC "menurc" | ||
| 60 | #define ACTIONS_RC "actionsrc" | ||
| 61 | #define COMMAND_HISTORY "command_history" | ||
| 62 | #define TEMPLATE_DIR "templates" | ||
| 63 | #define TMP_DIR "tmp" | ||
| 64 | #define UIDL_DIR "uidl" | ||
| 65 | #define PLUGIN_DIR "plugins" | ||
| 66 | #define NEWSGROUP_LIST ".newsgroup_list" | ||
| 67 | #define ADDRESS_BOOK "addressbook.xml" | ||
| 68 | #define MANUAL_HTML_INDEX "sylpheed.html" | ||
| 69 | #define FAQ_HTML_INDEX "sylpheed-faq.html" | ||
| 70 | #define HOMEPAGE_URI "http://sylpheed.sraoss.jp/" | ||
| 71 | #define DOWNLOAD_URI "http://sylpheed.sraoss.jp/download.php" | ||
| 72 | #define VERSION_URI "http://sylpheed.sraoss.jp/version.txt" | ||
| 73 | #define PLUGIN_HOMEPAGE_URI "http://sylpheed.sraoss.jp/en/plugin.html" | ||
| 74 | #define PLUGIN_VERSION_URI "http://sylpheed.sraoss.jp/plugin_version.txt" | ||
| 75 | #define FOLDER_LIST "folderlist.xml" | ||
| 76 | #define CACHE_FILE ".sylpheed_cache" | ||
| 77 | #define MARK_FILE ".sylpheed_mark" | ||
| 78 | #define SEARCH_CACHE "search_cache" | ||
| 79 | #define CACHE_VERSION 0x21 | ||
| 80 | #define MARK_VERSION 2 | ||
| 81 | #define SEARCH_CACHE_VERSION 1 | ||
| 82 | |||
| 83 | #ifdef G_OS_WIN32 | ||
| 84 | # define REMOTE_CMD_PORT 50215 | ||
| 85 | #endif | ||
| 86 | |||
| 87 | #ifdef G_OS_WIN32 | ||
| 88 | # define DEFAULT_SIGNATURE "signature.txt" | ||
| 89 | #else | ||
| 90 | # define DEFAULT_SIGNATURE ".signature" | ||
| 91 | #endif | ||
| 92 | #define DEFAULT_INC_PATH "/usr/bin/mh/inc" | ||
| 93 | #define DEFAULT_INC_PROGRAM "inc" | ||
| 94 | /* #define DEFAULT_INC_PATH "/usr/bin/imget" */ | ||
| 95 | /* #define DEFAULT_INC_PROGRAM "imget" */ | ||
| 96 | #define DEFAULT_SENDMAIL_CMD "/usr/sbin/sendmail -t -i" | ||
| 97 | #ifdef __APPLE__ | ||
| 98 | # define DEFAULT_BROWSER_CMD "open '%s'" | ||
| 99 | #else | ||
| 100 | # define DEFAULT_BROWSER_CMD "xdg-open '%s'" | ||
| 101 | #endif | ||
| 102 | |||
| 103 | #ifdef _PATH_MAILDIR | ||
| 104 | # define DEFAULT_SPOOL_PATH _PATH_MAILDIR | ||
| 105 | #else | ||
| 106 | # define DEFAULT_SPOOL_PATH "/var/spool/mail" | ||
| 107 | #endif | ||
| 108 | |||
| 109 | #define BUFFSIZE 8192 | ||
| 110 | |||
| 111 | #ifndef MAXPATHLEN | ||
| 112 | # define MAXPATHLEN 4095 | ||
| 113 | #endif | ||
| 114 | |||
| 115 | #define DEFAULT_HEIGHT 460 | ||
| 116 | #define DEFAULT_FOLDERVIEW_WIDTH 179 | ||
| 117 | #define DEFAULT_MAINVIEW_WIDTH 600 | ||
| 118 | #define DEFAULT_SUMMARY_HEIGHT 140 | ||
| 119 | #define DEFAULT_HEADERVIEW_HEIGHT 40 | ||
| 120 | #define DEFAULT_COMPOSE_HEIGHT 560 | ||
| 121 | #define BORDER_WIDTH 2 | ||
| 122 | #define CTREE_INDENT 18 | ||
| 123 | #define FOLDER_SPACING 4 | ||
| 124 | #define MAX_ENTRY_LENGTH 8191 | ||
| 125 | #define COLOR_DIM 35000 | ||
| 126 | #define UI_REFRESH_INTERVAL 50000 /* usec */ | ||
| 127 | #define FOLDER_UPDATE_INTERVAL 1500 /* msec */ | ||
| 128 | #define PROGRESS_UPDATE_INTERVAL 200 /* msec */ | ||
| 129 | #define SESSION_TIMEOUT_INTERVAL 60 /* sec */ | ||
| 130 | #define MAX_HISTORY_SIZE 16 | ||
| 131 | |||
| 132 | #define DEFAULT_MESSAGE_FONT "Monospace 12" | ||
| 133 | |||
| 134 | #endif /* __DEFS_H__ */ | ||
diff --git a/libsylph/displayheader.c b/libsylph/displayheader.c new file mode 100644 index 0000000..edafc5a --- /dev/null +++ b/libsylph/displayheader.c | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | |||
| 26 | #include "displayheader.h" | ||
| 27 | |||
| 28 | gchar *display_header_prop_get_str(DisplayHeaderProp *dp) | ||
| 29 | { | ||
| 30 | return g_strconcat(dp->hidden ? "-" : "", dp->name, NULL); | ||
| 31 | } | ||
| 32 | |||
| 33 | DisplayHeaderProp *display_header_prop_read_str(gchar *buf) | ||
| 34 | { | ||
| 35 | DisplayHeaderProp *dp; | ||
| 36 | |||
| 37 | dp = g_new0(DisplayHeaderProp, 1); | ||
| 38 | |||
| 39 | dp->hidden = FALSE; | ||
| 40 | if (*buf == '-') { | ||
| 41 | dp->hidden = TRUE; | ||
| 42 | buf++; | ||
| 43 | } | ||
| 44 | if (*buf == '\0') { | ||
| 45 | g_free(dp); | ||
| 46 | return NULL; | ||
| 47 | } | ||
| 48 | dp->name = g_strdup(buf); | ||
| 49 | |||
| 50 | return dp; | ||
| 51 | } | ||
| 52 | |||
| 53 | void display_header_prop_free(DisplayHeaderProp *dp) | ||
| 54 | { | ||
| 55 | if (!dp) return; | ||
| 56 | |||
| 57 | g_free(dp->name); | ||
| 58 | g_free(dp); | ||
| 59 | } | ||
diff --git a/libsylph/displayheader.h b/libsylph/displayheader.h new file mode 100644 index 0000000..4ca8e18 --- /dev/null +++ b/libsylph/displayheader.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __DISPLAYHEADER_H__ | ||
| 21 | #define __DISPLAYHEADER_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | typedef struct _DisplayHeaderProp DisplayHeaderProp; | ||
| 26 | |||
| 27 | struct _DisplayHeaderProp | ||
| 28 | { | ||
| 29 | gchar *name; | ||
| 30 | gboolean hidden; | ||
| 31 | }; | ||
| 32 | |||
| 33 | gchar *display_header_prop_get_str (DisplayHeaderProp *dp); | ||
| 34 | DisplayHeaderProp *display_header_prop_read_str (gchar *buf); | ||
| 35 | void display_header_prop_free (DisplayHeaderProp *dp); | ||
| 36 | |||
| 37 | #endif /* __DISPLAYHEADER_H__ */ | ||
diff --git a/libsylph/enums.h b/libsylph/enums.h new file mode 100644 index 0000000..29bae62 --- /dev/null +++ b/libsylph/enums.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __ENUMS_H__ | ||
| 21 | #define __ENUMS_H__ | ||
| 22 | |||
| 23 | typedef enum | ||
| 24 | { | ||
| 25 | TOOLBAR_NONE, | ||
| 26 | TOOLBAR_ICON, | ||
| 27 | TOOLBAR_TEXT, | ||
| 28 | TOOLBAR_BOTH, | ||
| 29 | TOOLBAR_BOTH_HORIZ | ||
| 30 | } ToolbarStyle; | ||
| 31 | |||
| 32 | typedef enum | ||
| 33 | { | ||
| 34 | LAYOUT_NORMAL, | ||
| 35 | LAYOUT_VERTICAL, | ||
| 36 | LAYOUT_VERTICAL_DOUBLE, | ||
| 37 | LAYOUT_WIDE_MESSAGE, | ||
| 38 | LAYOUT_WIDE_SUMMARY | ||
| 39 | } LayoutType; | ||
| 40 | |||
| 41 | typedef enum | ||
| 42 | { | ||
| 43 | S_COL_MARK, | ||
| 44 | S_COL_UNREAD, | ||
| 45 | S_COL_MIME, | ||
| 46 | S_COL_SUBJECT, | ||
| 47 | S_COL_FROM, | ||
| 48 | S_COL_DATE, | ||
| 49 | S_COL_SIZE, | ||
| 50 | S_COL_NUMBER, | ||
| 51 | S_COL_TO, | ||
| 52 | |||
| 53 | S_COL_MSG_INFO, | ||
| 54 | |||
| 55 | S_COL_LABEL, | ||
| 56 | S_COL_TDATE, | ||
| 57 | |||
| 58 | S_COL_FOREGROUND, | ||
| 59 | S_COL_BOLD, | ||
| 60 | |||
| 61 | N_SUMMARY_COLS | ||
| 62 | } SummaryColumnType; | ||
| 63 | |||
| 64 | #define N_SUMMARY_VISIBLE_COLS S_COL_MSG_INFO | ||
| 65 | |||
| 66 | #endif /* __ENUMS_H__ */ | ||
diff --git a/libsylph/filter.c b/libsylph/filter.c new file mode 100644 index 0000000..c58f8fc --- /dev/null +++ b/libsylph/filter.c | |||
| @@ -0,0 +1,1951 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2015 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <strings.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <sys/types.h> | ||
| 32 | #if USE_ONIGURUMA | ||
| 33 | # include <oniguruma.h> | ||
| 34 | #elif HAVE_REGEX_H | ||
| 35 | # include <regex.h> | ||
| 36 | #endif | ||
| 37 | #include <time.h> | ||
| 38 | |||
| 39 | #include "filter.h" | ||
| 40 | #include "procmsg.h" | ||
| 41 | #include "procheader.h" | ||
| 42 | #include "folder.h" | ||
| 43 | #include "utils.h" | ||
| 44 | #include "xml.h" | ||
| 45 | #include "prefs.h" | ||
| 46 | #include "prefs_common.h" | ||
| 47 | #include "prefs_account.h" | ||
| 48 | #include "account.h" | ||
| 49 | |||
| 50 | typedef enum | ||
| 51 | { | ||
| 52 | FLT_O_CONTAIN = 1 << 0, | ||
| 53 | FLT_O_CASE_SENS = 1 << 1, | ||
| 54 | FLT_O_REGEX = 1 << 2 | ||
| 55 | } FilterOldFlag; | ||
| 56 | |||
| 57 | static FilterInAddressBookFunc default_addrbook_func = NULL; | ||
| 58 | |||
| 59 | static gboolean filter_match_cond (FilterCond *cond, | ||
| 60 | MsgInfo *msginfo, | ||
| 61 | GSList *hlist, | ||
| 62 | FilterInfo *fltinfo); | ||
| 63 | static gboolean filter_match_header_cond(FilterCond *cond, | ||
| 64 | GSList *hlist); | ||
| 65 | static gboolean filter_match_in_addressbook | ||
| 66 | (FilterCond *cond, | ||
| 67 | GSList *hlist, | ||
| 68 | FilterInfo *fltinfo); | ||
| 69 | |||
| 70 | static void filter_cond_free (FilterCond *cond); | ||
| 71 | static void filter_action_free (FilterAction *action); | ||
| 72 | |||
| 73 | |||
| 74 | gint filter_apply(GSList *fltlist, const gchar *file, FilterInfo *fltinfo) | ||
| 75 | { | ||
| 76 | MsgInfo *msginfo; | ||
| 77 | gint ret = 0; | ||
| 78 | |||
| 79 | g_return_val_if_fail(file != NULL, -1); | ||
| 80 | g_return_val_if_fail(fltinfo != NULL, -1); | ||
| 81 | |||
| 82 | if (!fltlist) return 0; | ||
| 83 | |||
| 84 | msginfo = procheader_parse_file(file, fltinfo->flags, FALSE); | ||
| 85 | if (!msginfo) return 0; | ||
| 86 | msginfo->file_path = g_strdup(file); | ||
| 87 | |||
| 88 | /* inherit MIME flag */ | ||
| 89 | fltinfo->flags.tmp_flags = | ||
| 90 | (fltinfo->flags.tmp_flags & ~MSG_CACHED_FLAG_MASK) | | ||
| 91 | (msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK); | ||
| 92 | |||
| 93 | ret = filter_apply_msginfo(fltlist, msginfo, fltinfo); | ||
| 94 | |||
| 95 | procmsg_msginfo_free(msginfo); | ||
| 96 | |||
| 97 | return ret; | ||
| 98 | } | ||
| 99 | |||
| 100 | gint filter_apply_msginfo(GSList *fltlist, MsgInfo *msginfo, | ||
| 101 | FilterInfo *fltinfo) | ||
| 102 | { | ||
| 103 | gchar *file; | ||
| 104 | GSList *hlist, *cur; | ||
| 105 | FilterRule *rule; | ||
| 106 | gint ret = 0; | ||
| 107 | |||
| 108 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 109 | g_return_val_if_fail(fltinfo != NULL, -1); | ||
| 110 | |||
| 111 | fltinfo->error = FLT_ERROR_OK; | ||
| 112 | |||
| 113 | if (!fltlist) return 0; | ||
| 114 | |||
| 115 | file = procmsg_get_message_file(msginfo); | ||
| 116 | if (!file) | ||
| 117 | return -1; | ||
| 118 | hlist = procheader_get_header_list_from_file(file); | ||
| 119 | if (!hlist) { | ||
| 120 | g_free(file); | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | procmsg_set_auto_decrypt_message(FALSE); | ||
| 125 | |||
| 126 | for (cur = fltlist; cur != NULL; cur = cur->next) { | ||
| 127 | gboolean matched; | ||
| 128 | |||
| 129 | rule = (FilterRule *)cur->data; | ||
| 130 | if (!rule->enabled) continue; | ||
| 131 | matched = filter_match_rule(rule, msginfo, hlist, fltinfo); | ||
| 132 | if (fltinfo->error != FLT_ERROR_OK) { | ||
| 133 | g_warning("filter_match_rule() returned error (code: %d)\n", fltinfo->error); | ||
| 134 | } | ||
| 135 | if (matched) { | ||
| 136 | debug_print("filter-log: %s: rule [%s] matched\n", | ||
| 137 | G_STRFUNC, rule->name ? rule->name : "(No name)"); | ||
| 138 | ret = filter_action_exec(rule, msginfo, file, fltinfo); | ||
| 139 | if (ret < 0) { | ||
| 140 | g_warning("filter_action_exec() returned error (code: %d)\n", fltinfo->error); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | if (fltinfo->drop_done == TRUE || | ||
| 144 | fltinfo->actions[FLT_ACTION_STOP_EVAL] == TRUE) | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | procmsg_set_auto_decrypt_message(TRUE); | ||
| 150 | |||
| 151 | procheader_header_list_destroy(hlist); | ||
| 152 | g_free(file); | ||
| 153 | |||
| 154 | return ret; | ||
| 155 | } | ||
| 156 | |||
| 157 | gint filter_action_exec(FilterRule *rule, MsgInfo *msginfo, const gchar *file, | ||
| 158 | FilterInfo *fltinfo) | ||
| 159 | { | ||
| 160 | FolderItem *dest_folder = NULL; | ||
| 161 | FilterAction *action; | ||
| 162 | GSList *cur; | ||
| 163 | gchar *cmdline; | ||
| 164 | gboolean copy_to_self = FALSE; | ||
| 165 | gint ret; | ||
| 166 | |||
| 167 | g_return_val_if_fail(rule != NULL, -1); | ||
| 168 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 169 | g_return_val_if_fail(file != NULL, -1); | ||
| 170 | g_return_val_if_fail(fltinfo != NULL, -1); | ||
| 171 | |||
| 172 | for (cur = rule->action_list; cur != NULL; cur = cur->next) { | ||
| 173 | action = (FilterAction *)cur->data; | ||
| 174 | |||
| 175 | switch (action->type) { | ||
| 176 | case FLT_ACTION_MARK: | ||
| 177 | debug_print("filter_action_exec(): mark\n"); | ||
| 178 | MSG_SET_PERM_FLAGS(fltinfo->flags, MSG_MARKED); | ||
| 179 | fltinfo->actions[action->type] = TRUE; | ||
| 180 | break; | ||
| 181 | case FLT_ACTION_COLOR_LABEL: | ||
| 182 | debug_print("filter_action_exec(): color label: %d\n", | ||
| 183 | action->int_value); | ||
| 184 | MSG_UNSET_PERM_FLAGS(fltinfo->flags, | ||
| 185 | MSG_CLABEL_FLAG_MASK); | ||
| 186 | MSG_SET_COLORLABEL_VALUE(fltinfo->flags, | ||
| 187 | action->int_value); | ||
| 188 | fltinfo->actions[action->type] = TRUE; | ||
| 189 | break; | ||
| 190 | case FLT_ACTION_MARK_READ: | ||
| 191 | debug_print("filter_action_exec(): mark as read\n"); | ||
| 192 | if (msginfo->folder) { | ||
| 193 | if (MSG_IS_NEW(fltinfo->flags)) | ||
| 194 | msginfo->folder->new--; | ||
| 195 | if (MSG_IS_UNREAD(fltinfo->flags)) | ||
| 196 | msginfo->folder->unread--; | ||
| 197 | } | ||
| 198 | MSG_UNSET_PERM_FLAGS(fltinfo->flags, MSG_NEW|MSG_UNREAD); | ||
| 199 | fltinfo->actions[action->type] = TRUE; | ||
| 200 | break; | ||
| 201 | case FLT_ACTION_EXEC: | ||
| 202 | cmdline = g_strconcat(action->str_value, " \"", file, | ||
| 203 | "\"", NULL); | ||
| 204 | ret = execute_command_line(cmdline, FALSE); | ||
| 205 | fltinfo->last_exec_exit_status = ret; | ||
| 206 | if (ret == -1) { | ||
| 207 | fltinfo->error = FLT_ERROR_EXEC_FAILED; | ||
| 208 | g_warning("filter_action_exec: cannot execute command: %s", cmdline); | ||
| 209 | g_free(cmdline); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | g_free(cmdline); | ||
| 213 | fltinfo->actions[action->type] = TRUE; | ||
| 214 | break; | ||
| 215 | case FLT_ACTION_EXEC_ASYNC: | ||
| 216 | cmdline = g_strconcat(action->str_value, " \"", file, | ||
| 217 | "\"", NULL); | ||
| 218 | ret = execute_command_line(cmdline, TRUE); | ||
| 219 | fltinfo->last_exec_exit_status = ret; | ||
| 220 | if (ret == -1) { | ||
| 221 | fltinfo->error = FLT_ERROR_EXEC_FAILED; | ||
| 222 | g_warning("filter_action_exec: cannot execute command: %s", cmdline); | ||
| 223 | g_free(cmdline); | ||
| 224 | return -1; | ||
| 225 | } | ||
| 226 | g_free(cmdline); | ||
| 227 | fltinfo->actions[action->type] = TRUE; | ||
| 228 | break; | ||
| 229 | default: | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | for (cur = rule->action_list; cur != NULL; cur = cur->next) { | ||
| 235 | action = (FilterAction *)cur->data; | ||
| 236 | |||
| 237 | switch (action->type) { | ||
| 238 | case FLT_ACTION_MOVE: | ||
| 239 | case FLT_ACTION_COPY: | ||
| 240 | dest_folder = folder_find_item_from_identifier | ||
| 241 | (action->str_value); | ||
| 242 | if (!dest_folder) { | ||
| 243 | g_warning("dest folder '%s' not found\n", | ||
| 244 | action->str_value); | ||
| 245 | fltinfo->error = FLT_ERROR_ERROR; | ||
| 246 | return -1; | ||
| 247 | } | ||
| 248 | debug_print("filter_action_exec(): %s: dest_folder = %s\n", | ||
| 249 | action->type == FLT_ACTION_COPY ? | ||
| 250 | "copy" : "move", action->str_value); | ||
| 251 | |||
| 252 | if (msginfo->folder) { | ||
| 253 | gint val; | ||
| 254 | |||
| 255 | /* local filtering */ | ||
| 256 | if (msginfo->folder == dest_folder) | ||
| 257 | copy_to_self = TRUE; | ||
| 258 | else { | ||
| 259 | if (action->type == FLT_ACTION_COPY) { | ||
| 260 | MsgFlags save_flags; | ||
| 261 | |||
| 262 | save_flags = msginfo->flags; | ||
| 263 | msginfo->flags = fltinfo->flags; | ||
| 264 | val = folder_item_copy_msg | ||
| 265 | (dest_folder, msginfo); | ||
| 266 | msginfo->flags = save_flags; | ||
| 267 | if (val == -1) { | ||
| 268 | fltinfo->error = FLT_ERROR_ERROR; | ||
| 269 | return -1; | ||
| 270 | } | ||
| 271 | } | ||
| 272 | fltinfo->actions[action->type] = TRUE; | ||
| 273 | } | ||
| 274 | } else { | ||
| 275 | MsgFlags save_flags; | ||
| 276 | |||
| 277 | save_flags = msginfo->flags; | ||
| 278 | msginfo->flags = fltinfo->flags; | ||
| 279 | if (folder_item_add_msg_msginfo | ||
| 280 | (dest_folder, msginfo, FALSE) < 0) { | ||
| 281 | msginfo->flags = save_flags; | ||
| 282 | fltinfo->error = FLT_ERROR_ERROR; | ||
| 283 | return -1; | ||
| 284 | } | ||
| 285 | msginfo->flags = save_flags; | ||
| 286 | fltinfo->actions[action->type] = TRUE; | ||
| 287 | } | ||
| 288 | |||
| 289 | fltinfo->dest_list = g_slist_append(fltinfo->dest_list, | ||
| 290 | dest_folder); | ||
| 291 | if (action->type == FLT_ACTION_MOVE) { | ||
| 292 | fltinfo->move_dest = dest_folder; | ||
| 293 | fltinfo->drop_done = TRUE; | ||
| 294 | } | ||
| 295 | break; | ||
| 296 | default: | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | if (fltinfo->drop_done == TRUE) | ||
| 302 | return 0; | ||
| 303 | |||
| 304 | for (cur = rule->action_list; cur != NULL; cur = cur->next) { | ||
| 305 | action = (FilterAction *)cur->data; | ||
| 306 | |||
| 307 | switch (action->type) { | ||
| 308 | case FLT_ACTION_NOT_RECEIVE: | ||
| 309 | debug_print("filter_action_exec(): don't receive\n"); | ||
| 310 | fltinfo->drop_done = TRUE; | ||
| 311 | fltinfo->actions[action->type] = TRUE; | ||
| 312 | return 0; | ||
| 313 | case FLT_ACTION_DELETE: | ||
| 314 | debug_print("filter_action_exec(): delete\n"); | ||
| 315 | if (msginfo->folder) { | ||
| 316 | /* local filtering */ | ||
| 317 | if (copy_to_self == FALSE) | ||
| 318 | fltinfo->actions[action->type] = TRUE; | ||
| 319 | } else | ||
| 320 | fltinfo->actions[action->type] = TRUE; | ||
| 321 | |||
| 322 | fltinfo->drop_done = TRUE; | ||
| 323 | return 0; | ||
| 324 | case FLT_ACTION_STOP_EVAL: | ||
| 325 | debug_print("filter_action_exec(): stop evaluation\n"); | ||
| 326 | fltinfo->actions[action->type] = TRUE; | ||
| 327 | return 0; | ||
| 328 | default: | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | return 0; | ||
| 334 | } | ||
| 335 | |||
| 336 | static gboolean strmatch_regex(const gchar *haystack, const gchar *needle) | ||
| 337 | { | ||
| 338 | #ifdef USE_ONIGURUMA | ||
| 339 | gint ret = 0; | ||
| 340 | OnigRegex reg; | ||
| 341 | OnigErrorInfo err_info; | ||
| 342 | const UChar *ptn = (const UChar *)needle; | ||
| 343 | const UChar *str = (const UChar *)haystack; | ||
| 344 | size_t haystack_len; | ||
| 345 | |||
| 346 | ret = onig_new(®, ptn, ptn + strlen(needle), | ||
| 347 | /* ONIG_OPTION_EXTEND requires spaces to be escaped */ | ||
| 348 | /* ONIG_OPTION_IGNORECASE|ONIG_OPTION_EXTEND, */ | ||
| 349 | ONIG_OPTION_IGNORECASE, | ||
| 350 | ONIG_ENCODING_UTF8, ONIG_SYNTAX_POSIX_EXTENDED, | ||
| 351 | &err_info); | ||
| 352 | if (ret != ONIG_NORMAL) { | ||
| 353 | g_warning("strmatch_regex: onig_new() failed: %d", ret); | ||
| 354 | return FALSE; | ||
| 355 | } | ||
| 356 | |||
| 357 | haystack_len = strlen(haystack); | ||
| 358 | ret = onig_search(reg, str, str + haystack_len, | ||
| 359 | str, str + haystack_len, NULL, 0); | ||
| 360 | onig_free(reg); | ||
| 361 | |||
| 362 | if (ret >= 0) | ||
| 363 | return TRUE; | ||
| 364 | else | ||
| 365 | return FALSE; | ||
| 366 | #elif defined(HAVE_REGCOMP) | ||
| 367 | gint ret = 0; | ||
| 368 | regex_t preg; | ||
| 369 | |||
| 370 | ret = regcomp(&preg, needle, REG_ICASE|REG_EXTENDED); | ||
| 371 | if (ret != 0) | ||
| 372 | return FALSE; | ||
| 373 | |||
| 374 | ret = regexec(&preg, haystack, 0, NULL, 0); | ||
| 375 | regfree(&preg); | ||
| 376 | |||
| 377 | if (ret == 0) | ||
| 378 | return TRUE; | ||
| 379 | else | ||
| 380 | return FALSE; | ||
| 381 | #else | ||
| 382 | return FALSE; | ||
| 383 | #endif | ||
| 384 | } | ||
| 385 | |||
| 386 | gboolean filter_match_rule(FilterRule *rule, MsgInfo *msginfo, GSList *hlist, | ||
| 387 | FilterInfo *fltinfo) | ||
| 388 | { | ||
| 389 | FilterCond *cond; | ||
| 390 | GSList *cur; | ||
| 391 | gboolean matched; | ||
| 392 | |||
| 393 | g_return_val_if_fail(rule->cond_list != NULL, FALSE); | ||
| 394 | |||
| 395 | switch (rule->timing) { | ||
| 396 | case FLT_TIMING_ANY: | ||
| 397 | break; | ||
| 398 | case FLT_TIMING_ON_RECEIVE: | ||
| 399 | if (msginfo->folder != NULL) | ||
| 400 | return FALSE; | ||
| 401 | break; | ||
| 402 | case FLT_TIMING_MANUAL: | ||
| 403 | if (msginfo->folder == NULL) | ||
| 404 | return FALSE; | ||
| 405 | break; | ||
| 406 | default: | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | |||
| 410 | if (rule->bool_op == FLT_AND) { | ||
| 411 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 412 | cond = (FilterCond *)cur->data; | ||
| 413 | if (cond->type >= FLT_COND_SIZE_GREATER) { | ||
| 414 | matched = filter_match_cond | ||
| 415 | (cond, msginfo, hlist, fltinfo); | ||
| 416 | if (matched == FALSE) | ||
| 417 | return FALSE; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | |||
| 421 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 422 | cond = (FilterCond *)cur->data; | ||
| 423 | if (cond->type <= FLT_COND_TO_OR_CC) { | ||
| 424 | matched = filter_match_cond | ||
| 425 | (cond, msginfo, hlist, fltinfo); | ||
| 426 | if (matched == FALSE) | ||
| 427 | return FALSE; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 432 | cond = (FilterCond *)cur->data; | ||
| 433 | if (cond->type == FLT_COND_BODY || | ||
| 434 | cond->type == FLT_COND_CMD_TEST) { | ||
| 435 | matched = filter_match_cond | ||
| 436 | (cond, msginfo, hlist, fltinfo); | ||
| 437 | if (matched == FALSE) | ||
| 438 | return FALSE; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | return TRUE; | ||
| 443 | } else if (rule->bool_op == FLT_OR) { | ||
| 444 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 445 | cond = (FilterCond *)cur->data; | ||
| 446 | if (cond->type >= FLT_COND_SIZE_GREATER) { | ||
| 447 | matched = filter_match_cond | ||
| 448 | (cond, msginfo, hlist, fltinfo); | ||
| 449 | if (matched == TRUE) | ||
| 450 | return TRUE; | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 455 | cond = (FilterCond *)cur->data; | ||
| 456 | if (cond->type <= FLT_COND_TO_OR_CC) { | ||
| 457 | matched = filter_match_cond | ||
| 458 | (cond, msginfo, hlist, fltinfo); | ||
| 459 | if (matched == TRUE) | ||
| 460 | return TRUE; | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 465 | cond = (FilterCond *)cur->data; | ||
| 466 | if (cond->type == FLT_COND_BODY || | ||
| 467 | cond->type == FLT_COND_CMD_TEST) { | ||
| 468 | matched = filter_match_cond | ||
| 469 | (cond, msginfo, hlist, fltinfo); | ||
| 470 | if (matched == TRUE) | ||
| 471 | return TRUE; | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | return FALSE; | ||
| 476 | } | ||
| 477 | |||
| 478 | return FALSE; | ||
| 479 | } | ||
| 480 | |||
| 481 | static gboolean filter_match_cond(FilterCond *cond, MsgInfo *msginfo, | ||
| 482 | GSList *hlist, FilterInfo *fltinfo) | ||
| 483 | { | ||
| 484 | gint ret; | ||
| 485 | gboolean matched = FALSE; | ||
| 486 | gboolean not_match = FALSE; | ||
| 487 | gint64 timediff = 0; | ||
| 488 | gchar *file; | ||
| 489 | gchar *cmdline; | ||
| 490 | PrefsAccount *cond_ac; | ||
| 491 | |||
| 492 | switch (cond->type) { | ||
| 493 | case FLT_COND_HEADER: | ||
| 494 | if (cond->match_type == FLT_IN_ADDRESSBOOK) | ||
| 495 | return filter_match_in_addressbook(cond, hlist, fltinfo); | ||
| 496 | else | ||
| 497 | return filter_match_header_cond(cond, hlist); | ||
| 498 | case FLT_COND_ANY_HEADER: | ||
| 499 | return filter_match_header_cond(cond, hlist); | ||
| 500 | case FLT_COND_TO_OR_CC: | ||
| 501 | if (cond->match_type == FLT_IN_ADDRESSBOOK) | ||
| 502 | return filter_match_in_addressbook(cond, hlist, fltinfo); | ||
| 503 | else | ||
| 504 | return filter_match_header_cond(cond, hlist); | ||
| 505 | case FLT_COND_BODY: | ||
| 506 | matched = procmime_find_string(msginfo, cond->str_value, | ||
| 507 | cond->match_func); | ||
| 508 | break; | ||
| 509 | case FLT_COND_CMD_TEST: | ||
| 510 | file = procmsg_get_message_file(msginfo); | ||
| 511 | if (!file) | ||
| 512 | return FALSE; | ||
| 513 | cmdline = g_strconcat(cond->str_value, " \"", file, "\"", NULL); | ||
| 514 | ret = execute_command_line_async_wait(cmdline); | ||
| 515 | fltinfo->last_exec_exit_status = ret; | ||
| 516 | matched = (ret == 0); | ||
| 517 | if (ret == -1) | ||
| 518 | fltinfo->error = FLT_ERROR_EXEC_FAILED; | ||
| 519 | g_free(cmdline); | ||
| 520 | g_free(file); | ||
| 521 | break; | ||
| 522 | case FLT_COND_SIZE_GREATER: | ||
| 523 | matched = (msginfo->size > cond->int_value * 1024); | ||
| 524 | break; | ||
| 525 | case FLT_COND_AGE_GREATER: | ||
| 526 | timediff = time(NULL) - msginfo->date_t; | ||
| 527 | matched = (timediff > cond->int_value * 24 * 60 * 60); | ||
| 528 | break; | ||
| 529 | case FLT_COND_UNREAD: | ||
| 530 | matched = MSG_IS_UNREAD(msginfo->flags); | ||
| 531 | break; | ||
| 532 | case FLT_COND_MARK: | ||
| 533 | matched = MSG_IS_MARKED(msginfo->flags); | ||
| 534 | break; | ||
| 535 | case FLT_COND_COLOR_LABEL: | ||
| 536 | matched = (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != 0); | ||
| 537 | break; | ||
| 538 | case FLT_COND_MIME: | ||
| 539 | matched = MSG_IS_MIME(msginfo->flags); | ||
| 540 | break; | ||
| 541 | case FLT_COND_ACCOUNT: | ||
| 542 | cond_ac = account_find_from_id(cond->int_value); | ||
| 543 | matched = (cond_ac != NULL && cond_ac == fltinfo->account); | ||
| 544 | break; | ||
| 545 | default: | ||
| 546 | g_warning("filter_match_cond(): unknown condition: %d\n", | ||
| 547 | cond->type); | ||
| 548 | fltinfo->error = FLT_ERROR_ERROR; | ||
| 549 | return FALSE; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (FLT_IS_NOT_MATCH(cond->match_flag)) { | ||
| 553 | not_match = TRUE; | ||
| 554 | matched = !matched; | ||
| 555 | } | ||
| 556 | |||
| 557 | if (matched && get_debug_mode()) { | ||
| 558 | gchar *sv = cond->str_value ? cond->str_value : ""; | ||
| 559 | gchar *nm = not_match ? " (reverse match)" : ""; | ||
| 560 | |||
| 561 | switch (cond->type) { | ||
| 562 | case FLT_COND_BODY: | ||
| 563 | debug_print("filter-log: %s: BODY, str_value: [%s]%s\n", G_STRFUNC, sv, nm); | ||
| 564 | break; | ||
| 565 | case FLT_COND_CMD_TEST: | ||
| 566 | debug_print("filter-log: %s: CMD_TEST, str_value: [%s]%s\n", G_STRFUNC, sv, nm); | ||
| 567 | break; | ||
| 568 | case FLT_COND_SIZE_GREATER: | ||
| 569 | debug_print("filter-log: %s: SIZE_GREATER: %u %s %d (KB)%s\n", G_STRFUNC, msginfo->size, not_match ? "<=" : ">", cond->int_value, nm); | ||
| 570 | break; | ||
| 571 | case FLT_COND_AGE_GREATER: | ||
| 572 | debug_print("filter-log: %s: AGE_GREATER: %lld (sec) %s %d (day)%s\n", G_STRFUNC, timediff, not_match ? "<=" : ">", cond->int_value, nm); | ||
| 573 | break; | ||
| 574 | case FLT_COND_UNREAD: | ||
| 575 | debug_print("filter-log: %s: UNREAD%s\n", G_STRFUNC, nm); | ||
| 576 | break; | ||
| 577 | case FLT_COND_MARK: | ||
| 578 | debug_print("filter-log: %s: MARK%s\n", G_STRFUNC, nm); | ||
| 579 | break; | ||
| 580 | case FLT_COND_COLOR_LABEL: | ||
| 581 | debug_print("filter-log: %s: COLOR_LABEL%s\n", G_STRFUNC, nm); | ||
| 582 | break; | ||
| 583 | case FLT_COND_MIME: | ||
| 584 | debug_print("filter-log: %s: MIME%s\n", G_STRFUNC, nm); | ||
| 585 | break; | ||
| 586 | case FLT_COND_ACCOUNT: | ||
| 587 | debug_print("filter-log: %s: ACCOUNT [%d]%s\n", G_STRFUNC, cond->int_value, nm); | ||
| 588 | break; | ||
| 589 | default: | ||
| 590 | break; | ||
| 591 | } | ||
| 592 | } | ||
| 593 | |||
| 594 | return matched; | ||
| 595 | } | ||
| 596 | |||
| 597 | static gboolean filter_match_header_cond(FilterCond *cond, GSList *hlist) | ||
| 598 | { | ||
| 599 | gboolean matched = FALSE; | ||
| 600 | gboolean not_match = FALSE; | ||
| 601 | GSList *cur; | ||
| 602 | Header *header; | ||
| 603 | |||
| 604 | for (cur = hlist; cur != NULL; cur = cur->next) { | ||
| 605 | header = (Header *)cur->data; | ||
| 606 | |||
| 607 | switch (cond->type) { | ||
| 608 | case FLT_COND_HEADER: | ||
| 609 | if (!g_ascii_strcasecmp | ||
| 610 | (header->name, cond->header_name)) { | ||
| 611 | if (!cond->str_value || | ||
| 612 | cond->match_func(header->body, | ||
| 613 | cond->str_value)) | ||
| 614 | matched = TRUE; | ||
| 615 | } | ||
| 616 | break; | ||
| 617 | case FLT_COND_ANY_HEADER: | ||
| 618 | if (!cond->str_value || | ||
| 619 | cond->match_func(header->body, cond->str_value)) | ||
| 620 | matched = TRUE; | ||
| 621 | break; | ||
| 622 | case FLT_COND_TO_OR_CC: | ||
| 623 | if (!g_ascii_strcasecmp(header->name, "To") || | ||
| 624 | !g_ascii_strcasecmp(header->name, "Cc")) { | ||
| 625 | if (!cond->str_value || | ||
| 626 | cond->match_func(header->body, | ||
| 627 | cond->str_value)) | ||
| 628 | matched = TRUE; | ||
| 629 | } | ||
| 630 | break; | ||
| 631 | default: | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | |||
| 635 | if (matched == TRUE) | ||
| 636 | break; | ||
| 637 | } | ||
| 638 | |||
| 639 | if (FLT_IS_NOT_MATCH(cond->match_flag)) { | ||
| 640 | not_match = TRUE; | ||
| 641 | matched = !matched; | ||
| 642 | } | ||
| 643 | |||
| 644 | if (matched && get_debug_mode()) { | ||
| 645 | gchar *sv = cond->str_value ? cond->str_value : ""; | ||
| 646 | gchar *nm = not_match ? " (reverse match)" : ""; | ||
| 647 | |||
| 648 | switch (cond->type) { | ||
| 649 | case FLT_COND_HEADER: | ||
| 650 | debug_print("filter-log: %s: HEADER [%s], str_value: [%s]%s\n", G_STRFUNC, cond->header_name, sv, nm); | ||
| 651 | break; | ||
| 652 | case FLT_COND_ANY_HEADER: | ||
| 653 | debug_print("filter-log: %s: ANY_HEADER, str_value: [%s]%s\n", G_STRFUNC, sv, nm); | ||
| 654 | break; | ||
| 655 | case FLT_COND_TO_OR_CC: | ||
| 656 | debug_print("filter-log: %s: TO_OR_CC, str_value: [%s]%s\n", G_STRFUNC, sv, nm); | ||
| 657 | break; | ||
| 658 | default: | ||
| 659 | break; | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | return matched; | ||
| 664 | } | ||
| 665 | |||
| 666 | static gboolean filter_match_in_addressbook(FilterCond *cond, GSList *hlist, | ||
| 667 | FilterInfo *fltinfo) | ||
| 668 | { | ||
| 669 | gboolean matched = FALSE; | ||
| 670 | gboolean not_match = FALSE; | ||
| 671 | GSList *cur; | ||
| 672 | Header *header; | ||
| 673 | |||
| 674 | if (!default_addrbook_func) | ||
| 675 | return FALSE; | ||
| 676 | if (cond->type != FLT_COND_HEADER && cond->type != FLT_COND_TO_OR_CC) | ||
| 677 | return FALSE; | ||
| 678 | |||
| 679 | for (cur = hlist; cur != NULL; cur = cur->next) { | ||
| 680 | header = (Header *)cur->data; | ||
| 681 | |||
| 682 | if (cond->type == FLT_COND_HEADER) { | ||
| 683 | if (!g_ascii_strcasecmp | ||
| 684 | (header->name, cond->header_name)) { | ||
| 685 | if (default_addrbook_func(header->body)) | ||
| 686 | matched = TRUE; | ||
| 687 | } | ||
| 688 | } else if (cond->type == FLT_COND_TO_OR_CC) { | ||
| 689 | if (!g_ascii_strcasecmp(header->name, "To") || | ||
| 690 | !g_ascii_strcasecmp(header->name, "Cc")) { | ||
| 691 | if (default_addrbook_func(header->body)) | ||
| 692 | matched = TRUE; | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | if (matched == TRUE) | ||
| 697 | break; | ||
| 698 | } | ||
| 699 | |||
| 700 | if (FLT_IS_NOT_MATCH(cond->match_flag)) { | ||
| 701 | not_match = TRUE; | ||
| 702 | matched = !matched; | ||
| 703 | } | ||
| 704 | |||
| 705 | if (matched && get_debug_mode()) { | ||
| 706 | gchar *nm = not_match ? " (reverse match)" : ""; | ||
| 707 | |||
| 708 | switch (cond->type) { | ||
| 709 | case FLT_COND_HEADER: | ||
| 710 | debug_print("filter-log: %s: HEADER [%s], IN_ADDRESSBOOK%s\n", G_STRFUNC, cond->header_name, nm); | ||
| 711 | break; | ||
| 712 | case FLT_COND_TO_OR_CC: | ||
| 713 | debug_print("filter-log: %s: TO_OR_CC, IN_ADDRESSBOOK%s\n", G_STRFUNC, nm); | ||
| 714 | break; | ||
| 715 | default: | ||
| 716 | break; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | return matched; | ||
| 721 | } | ||
| 722 | |||
| 723 | gboolean filter_rule_requires_full_headers(FilterRule *rule) | ||
| 724 | { | ||
| 725 | GSList *cur; | ||
| 726 | |||
| 727 | for (cur = rule->cond_list; cur != NULL; cur = cur->next) { | ||
| 728 | FilterCond *cond = (FilterCond *)cur->data; | ||
| 729 | const gchar *name = cond->header_name; | ||
| 730 | |||
| 731 | if (cond->type == FLT_COND_HEADER && name) { | ||
| 732 | if (g_ascii_strcasecmp(name, "Date") != 0 && | ||
| 733 | g_ascii_strcasecmp(name, "From") != 0 && | ||
| 734 | g_ascii_strcasecmp(name, "To") != 0 && | ||
| 735 | g_ascii_strcasecmp(name, "Newsgroups") != 0 && | ||
| 736 | g_ascii_strcasecmp(name, "Subject") != 0) | ||
| 737 | return TRUE; | ||
| 738 | } else if (cond->type == FLT_COND_ANY_HEADER || | ||
| 739 | cond->type == FLT_COND_TO_OR_CC) | ||
| 740 | return TRUE; | ||
| 741 | } | ||
| 742 | |||
| 743 | return FALSE; | ||
| 744 | } | ||
| 745 | |||
| 746 | #define RETURN_IF_TAG_NOT_MATCH(tag_name) \ | ||
| 747 | if (strcmp2(xmlnode->tag->tag, tag_name) != 0) { \ | ||
| 748 | g_warning("tag name != \"" tag_name "\"\n"); \ | ||
| 749 | filter_cond_list_free(cond_list); \ | ||
| 750 | filter_action_list_free(cond_list); \ | ||
| 751 | return FALSE; \ | ||
| 752 | } \ | ||
| 753 | |||
| 754 | #define STR_SWITCH(sw) { const gchar *sw_str = sw; | ||
| 755 | #define STR_CASE_BEGIN(str) if (!strcmp(sw_str, str)) { | ||
| 756 | #define STR_CASE(str) } else if (!strcmp(sw_str, str)) { | ||
| 757 | #define STR_CASE_END } } | ||
| 758 | |||
| 759 | static gboolean filter_xml_node_func(GNode *node, gpointer data) | ||
| 760 | { | ||
| 761 | XMLNode *xmlnode; | ||
| 762 | GList *list; | ||
| 763 | const gchar *name = NULL; | ||
| 764 | FilterTiming timing = FLT_TIMING_ANY; | ||
| 765 | gboolean enabled = TRUE; | ||
| 766 | FilterRule *rule; | ||
| 767 | FilterBoolOp bool_op = FLT_OR; | ||
| 768 | const gchar *target_folder = NULL; | ||
| 769 | gboolean recursive = FALSE; | ||
| 770 | GSList *cond_list = NULL; | ||
| 771 | GSList *action_list = NULL; | ||
| 772 | GNode *child, *cond_child, *action_child; | ||
| 773 | GSList **fltlist = (GSList **)data; | ||
| 774 | |||
| 775 | if (g_node_depth(node) != 2) return FALSE; | ||
| 776 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 777 | |||
| 778 | xmlnode = node->data; | ||
| 779 | RETURN_IF_TAG_NOT_MATCH("rule"); | ||
| 780 | |||
| 781 | for (list = xmlnode->tag->attr; list != NULL; list = list->next) { | ||
| 782 | XMLAttr *attr = (XMLAttr *)list->data; | ||
| 783 | |||
| 784 | if (!attr || !attr->name || !attr->value) continue; | ||
| 785 | if (!strcmp(attr->name, "name")) | ||
| 786 | name = attr->value; | ||
| 787 | else if (!strcmp(attr->name, "timing")) { | ||
| 788 | if (!strcmp(attr->value, "any")) | ||
| 789 | timing = FLT_TIMING_ANY; | ||
| 790 | else if (!strcmp(attr->value, "receive")) | ||
| 791 | timing = FLT_TIMING_ON_RECEIVE; | ||
| 792 | else if (!strcmp(attr->value, "manual")) | ||
| 793 | timing = FLT_TIMING_MANUAL; | ||
| 794 | } else if (!strcmp(attr->name, "enabled")) { | ||
| 795 | if (!strcmp(attr->value, "true")) | ||
| 796 | enabled = TRUE; | ||
| 797 | else | ||
| 798 | enabled = FALSE; | ||
| 799 | } | ||
| 800 | } | ||
| 801 | |||
| 802 | /* condition list */ | ||
| 803 | child = node->children; | ||
| 804 | if (!child) { | ||
| 805 | g_warning("<rule> must have children\n"); | ||
| 806 | return FALSE; | ||
| 807 | } | ||
| 808 | xmlnode = child->data; | ||
| 809 | RETURN_IF_TAG_NOT_MATCH("condition-list"); | ||
| 810 | for (list = xmlnode->tag->attr; list != NULL; list = list->next) { | ||
| 811 | XMLAttr *attr = (XMLAttr *)list->data; | ||
| 812 | |||
| 813 | if (attr && attr->name && attr->value && | ||
| 814 | !strcmp(attr->name, "bool")) { | ||
| 815 | if (!strcmp(attr->value, "or")) | ||
| 816 | bool_op = FLT_OR; | ||
| 817 | else | ||
| 818 | bool_op = FLT_AND; | ||
| 819 | } | ||
| 820 | } | ||
| 821 | |||
| 822 | for (cond_child = child->children; cond_child != NULL; | ||
| 823 | cond_child = cond_child->next) { | ||
| 824 | const gchar *type = NULL; | ||
| 825 | const gchar *name = NULL; | ||
| 826 | const gchar *value = NULL; | ||
| 827 | gboolean case_sens = FALSE; | ||
| 828 | FilterCond *cond; | ||
| 829 | FilterCondType cond_type = FLT_COND_HEADER; | ||
| 830 | FilterMatchType match_type = FLT_CONTAIN; | ||
| 831 | FilterMatchFlag match_flag = 0; | ||
| 832 | |||
| 833 | xmlnode = cond_child->data; | ||
| 834 | if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue; | ||
| 835 | |||
| 836 | for (list = xmlnode->tag->attr; list != NULL; list = list->next) { | ||
| 837 | XMLAttr *attr = (XMLAttr *)list->data; | ||
| 838 | |||
| 839 | if (!attr || !attr->name || !attr->value) continue; | ||
| 840 | |||
| 841 | STR_SWITCH(attr->name) | ||
| 842 | STR_CASE_BEGIN("type") | ||
| 843 | type = attr->value; | ||
| 844 | STR_CASE("name") | ||
| 845 | name = attr->value; | ||
| 846 | STR_CASE("case") | ||
| 847 | case_sens = TRUE; | ||
| 848 | STR_CASE("recursive") | ||
| 849 | if (!strcmp(attr->value, "true")) | ||
| 850 | recursive = TRUE; | ||
| 851 | else | ||
| 852 | recursive = FALSE; | ||
| 853 | STR_CASE_END | ||
| 854 | } | ||
| 855 | |||
| 856 | if (type) { | ||
| 857 | filter_rule_match_type_str_to_enum | ||
| 858 | (type, &match_type, &match_flag); | ||
| 859 | } | ||
| 860 | if (case_sens) | ||
| 861 | match_flag |= FLT_CASE_SENS; | ||
| 862 | value = xmlnode->element; | ||
| 863 | |||
| 864 | STR_SWITCH(xmlnode->tag->tag) | ||
| 865 | STR_CASE_BEGIN("match-header") | ||
| 866 | cond_type = FLT_COND_HEADER; | ||
| 867 | STR_CASE("match-any-header") | ||
| 868 | cond_type = FLT_COND_ANY_HEADER; | ||
| 869 | STR_CASE("match-to-or-cc") | ||
| 870 | cond_type = FLT_COND_TO_OR_CC; | ||
| 871 | STR_CASE("match-body-text") | ||
| 872 | cond_type = FLT_COND_BODY; | ||
| 873 | STR_CASE("command-test") | ||
| 874 | cond_type = FLT_COND_CMD_TEST; | ||
| 875 | STR_CASE("size") | ||
| 876 | cond_type = FLT_COND_SIZE_GREATER; | ||
| 877 | STR_CASE("age") | ||
| 878 | cond_type = FLT_COND_AGE_GREATER; | ||
| 879 | STR_CASE("unread") | ||
| 880 | cond_type = FLT_COND_UNREAD; | ||
| 881 | STR_CASE("mark") | ||
| 882 | cond_type = FLT_COND_MARK; | ||
| 883 | STR_CASE("color-label") | ||
| 884 | cond_type = FLT_COND_COLOR_LABEL; | ||
| 885 | STR_CASE("mime") | ||
| 886 | cond_type = FLT_COND_MIME; | ||
| 887 | STR_CASE("account-id") | ||
| 888 | cond_type = FLT_COND_ACCOUNT; | ||
| 889 | STR_CASE("target-folder") | ||
| 890 | target_folder = value; | ||
| 891 | continue; | ||
| 892 | STR_CASE_END | ||
| 893 | |||
| 894 | cond = filter_cond_new(cond_type, match_type, match_flag, | ||
| 895 | name, value); | ||
| 896 | cond_list = g_slist_append(cond_list, cond); | ||
| 897 | } | ||
| 898 | |||
| 899 | /* action list */ | ||
| 900 | child = child->next; | ||
| 901 | if (!child) { | ||
| 902 | g_warning("<rule> must have multiple children\n"); | ||
| 903 | filter_cond_list_free(cond_list); | ||
| 904 | return FALSE; | ||
| 905 | } | ||
| 906 | xmlnode = child->data; | ||
| 907 | RETURN_IF_TAG_NOT_MATCH("action-list"); | ||
| 908 | |||
| 909 | for (action_child = child->children; action_child != NULL; | ||
| 910 | action_child = action_child->next) { | ||
| 911 | FilterAction *action; | ||
| 912 | FilterActionType action_type = FLT_ACTION_NONE; | ||
| 913 | |||
| 914 | xmlnode = action_child->data; | ||
| 915 | if (!xmlnode || !xmlnode->tag || !xmlnode->tag->tag) continue; | ||
| 916 | |||
| 917 | STR_SWITCH(xmlnode->tag->tag) | ||
| 918 | STR_CASE_BEGIN("move") | ||
| 919 | action_type = FLT_ACTION_MOVE; | ||
| 920 | STR_CASE("copy") | ||
| 921 | action_type = FLT_ACTION_COPY; | ||
| 922 | STR_CASE("not-receive") | ||
| 923 | action_type = FLT_ACTION_NOT_RECEIVE; | ||
| 924 | STR_CASE("delete") | ||
| 925 | action_type = FLT_ACTION_DELETE; | ||
| 926 | STR_CASE("exec") | ||
| 927 | action_type = FLT_ACTION_EXEC; | ||
| 928 | STR_CASE("exec-async") | ||
| 929 | action_type = FLT_ACTION_EXEC_ASYNC; | ||
| 930 | STR_CASE("mark") | ||
| 931 | action_type = FLT_ACTION_MARK; | ||
| 932 | STR_CASE("color-label") | ||
| 933 | action_type = FLT_ACTION_COLOR_LABEL; | ||
| 934 | STR_CASE("mark-as-read") | ||
| 935 | action_type = FLT_ACTION_MARK_READ; | ||
| 936 | STR_CASE("forward") | ||
| 937 | action_type = FLT_ACTION_FORWARD; | ||
| 938 | STR_CASE("forward-as-attachment") | ||
| 939 | action_type = FLT_ACTION_FORWARD_AS_ATTACHMENT; | ||
| 940 | STR_CASE("redirect") | ||
| 941 | action_type = FLT_ACTION_REDIRECT; | ||
| 942 | STR_CASE("stop-eval") | ||
| 943 | action_type = FLT_ACTION_STOP_EVAL; | ||
| 944 | STR_CASE_END | ||
| 945 | |||
| 946 | action = filter_action_new(action_type, xmlnode->element); | ||
| 947 | action_list = g_slist_append(action_list, action); | ||
| 948 | } | ||
| 949 | |||
| 950 | if (name && cond_list) { | ||
| 951 | rule = filter_rule_new(name, bool_op, cond_list, action_list); | ||
| 952 | rule->timing = timing; | ||
| 953 | rule->enabled = enabled; | ||
| 954 | if (target_folder) { | ||
| 955 | rule->target_folder = g_strdup(target_folder); | ||
| 956 | rule->recursive = recursive; | ||
| 957 | } | ||
| 958 | *fltlist = g_slist_prepend(*fltlist, rule); | ||
| 959 | } | ||
| 960 | |||
| 961 | return FALSE; | ||
| 962 | } | ||
| 963 | |||
| 964 | #undef RETURN_IF_TAG_NOT_MATCH | ||
| 965 | #undef STR_SWITCH | ||
| 966 | #undef STR_CASE_BEGIN | ||
| 967 | #undef STR_CASE | ||
| 968 | #undef STR_CASE_END | ||
| 969 | |||
| 970 | GSList *filter_xml_node_to_filter_list(GNode *node) | ||
| 971 | { | ||
| 972 | GSList *fltlist = NULL; | ||
| 973 | |||
| 974 | g_return_val_if_fail(node != NULL, NULL); | ||
| 975 | |||
| 976 | g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2, | ||
| 977 | filter_xml_node_func, &fltlist); | ||
| 978 | fltlist = g_slist_reverse(fltlist); | ||
| 979 | |||
| 980 | return fltlist; | ||
| 981 | } | ||
| 982 | |||
| 983 | GSList *filter_read_file(const gchar *file) | ||
| 984 | { | ||
| 985 | GNode *node; | ||
| 986 | GSList *list; | ||
| 987 | |||
| 988 | g_return_val_if_fail(file != NULL, NULL); | ||
| 989 | |||
| 990 | debug_print("Reading %s\n", file); | ||
| 991 | |||
| 992 | if (!is_file_exist(file)) | ||
| 993 | return NULL; | ||
| 994 | |||
| 995 | node = xml_parse_file(file); | ||
| 996 | if (!node) { | ||
| 997 | g_warning("Can't parse %s\n", file); | ||
| 998 | return NULL; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | list = filter_xml_node_to_filter_list(node); | ||
| 1002 | |||
| 1003 | xml_free_tree(node); | ||
| 1004 | |||
| 1005 | return list; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | void filter_read_config(void) | ||
| 1009 | { | ||
| 1010 | gchar *rcpath; | ||
| 1011 | |||
| 1012 | debug_print("Reading filter configuration...\n"); | ||
| 1013 | |||
| 1014 | /* remove all previous filter list */ | ||
| 1015 | while (prefs_common.fltlist != NULL) { | ||
| 1016 | FilterRule *rule = (FilterRule *)prefs_common.fltlist->data; | ||
| 1017 | |||
| 1018 | filter_rule_free(rule); | ||
| 1019 | prefs_common.fltlist = g_slist_remove(prefs_common.fltlist, | ||
| 1020 | rule); | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST, | ||
| 1024 | NULL); | ||
| 1025 | prefs_common.fltlist = filter_read_file(rcpath); | ||
| 1026 | g_free(rcpath); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | #define NODE_NEW(tag, text) \ | ||
| 1030 | node = xml_node_new(xml_tag_new(tag), text) | ||
| 1031 | #define ADD_ATTR(name, value) \ | ||
| 1032 | xml_tag_add_attr(node->tag, xml_attr_new(name, value)) | ||
| 1033 | |||
| 1034 | void filter_write_file(GSList *list, const gchar *file) | ||
| 1035 | { | ||
| 1036 | PrefFile *pfile; | ||
| 1037 | GSList *cur; | ||
| 1038 | |||
| 1039 | g_return_if_fail(file != NULL); | ||
| 1040 | |||
| 1041 | if ((pfile = prefs_file_open(file)) == NULL) { | ||
| 1042 | g_warning("failed to write filter configuration to file: %s\n", | ||
| 1043 | file); | ||
| 1044 | return; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | xml_file_put_xml_decl(pfile->fp); | ||
| 1048 | fputs("\n<filter>\n", pfile->fp); | ||
| 1049 | |||
| 1050 | for (cur = list; cur != NULL; cur = cur->next) { | ||
| 1051 | FilterRule *rule = (FilterRule *)cur->data; | ||
| 1052 | GSList *cur_cond; | ||
| 1053 | GSList *cur_action; | ||
| 1054 | gchar match_type[64]; | ||
| 1055 | gchar nstr[16]; | ||
| 1056 | |||
| 1057 | fputs(" <rule name=\"", pfile->fp); | ||
| 1058 | xml_file_put_escape_str(pfile->fp, rule->name); | ||
| 1059 | fprintf(pfile->fp, "\" timing=\"%s\"", | ||
| 1060 | rule->timing == FLT_TIMING_ON_RECEIVE ? "receive" : | ||
| 1061 | rule->timing == FLT_TIMING_MANUAL ? "manual" : "any"); | ||
| 1062 | fprintf(pfile->fp, " enabled=\"%s\">\n", | ||
| 1063 | rule->enabled ? "true" : "false"); | ||
| 1064 | |||
| 1065 | fprintf(pfile->fp, " <condition-list bool=\"%s\">\n", | ||
| 1066 | rule->bool_op == FLT_OR ? "or" : "and"); | ||
| 1067 | |||
| 1068 | for (cur_cond = rule->cond_list; cur_cond != NULL; | ||
| 1069 | cur_cond = cur_cond->next) { | ||
| 1070 | FilterCond *cond = (FilterCond *)cur_cond->data; | ||
| 1071 | XMLNode *node = NULL; | ||
| 1072 | |||
| 1073 | switch (cond->match_type) { | ||
| 1074 | case FLT_CONTAIN: | ||
| 1075 | strncpy2(match_type, | ||
| 1076 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1077 | ? "not-contain" : "contains", | ||
| 1078 | sizeof(match_type)); | ||
| 1079 | break; | ||
| 1080 | case FLT_EQUAL: | ||
| 1081 | strncpy2(match_type, | ||
| 1082 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1083 | ? "is-not" : "is", sizeof(match_type)); | ||
| 1084 | break; | ||
| 1085 | case FLT_REGEX: | ||
| 1086 | strncpy2(match_type, | ||
| 1087 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1088 | ? "not-regex" : "regex", | ||
| 1089 | sizeof(match_type)); | ||
| 1090 | break; | ||
| 1091 | case FLT_IN_ADDRESSBOOK: | ||
| 1092 | strncpy2(match_type, | ||
| 1093 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1094 | ? "not-in-addressbook" : "in-addressbook", | ||
| 1095 | sizeof(match_type)); | ||
| 1096 | break; | ||
| 1097 | default: | ||
| 1098 | match_type[0] = '\0'; | ||
| 1099 | break; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | switch (cond->type) { | ||
| 1103 | case FLT_COND_HEADER: | ||
| 1104 | NODE_NEW("match-header", cond->str_value); | ||
| 1105 | ADD_ATTR("type", match_type); | ||
| 1106 | ADD_ATTR("name", cond->header_name); | ||
| 1107 | if (FLT_IS_CASE_SENS(cond->match_flag)) | ||
| 1108 | ADD_ATTR("case", "true"); | ||
| 1109 | break; | ||
| 1110 | case FLT_COND_ANY_HEADER: | ||
| 1111 | NODE_NEW("match-any-header", cond->str_value); | ||
| 1112 | ADD_ATTR("type", match_type); | ||
| 1113 | if (FLT_IS_CASE_SENS(cond->match_flag)) | ||
| 1114 | ADD_ATTR("case", "true"); | ||
| 1115 | break; | ||
| 1116 | case FLT_COND_TO_OR_CC: | ||
| 1117 | NODE_NEW("match-to-or-cc", cond->str_value); | ||
| 1118 | ADD_ATTR("type", match_type); | ||
| 1119 | if (FLT_IS_CASE_SENS(cond->match_flag)) | ||
| 1120 | ADD_ATTR("case", "true"); | ||
| 1121 | break; | ||
| 1122 | case FLT_COND_BODY: | ||
| 1123 | NODE_NEW("match-body-text", cond->str_value); | ||
| 1124 | ADD_ATTR("type", match_type); | ||
| 1125 | if (FLT_IS_CASE_SENS(cond->match_flag)) | ||
| 1126 | ADD_ATTR("case", "true"); | ||
| 1127 | break; | ||
| 1128 | case FLT_COND_CMD_TEST: | ||
| 1129 | NODE_NEW("command-test", cond->str_value); | ||
| 1130 | break; | ||
| 1131 | case FLT_COND_SIZE_GREATER: | ||
| 1132 | NODE_NEW("size", itos_buf(nstr, cond->int_value)); | ||
| 1133 | ADD_ATTR("type", | ||
| 1134 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1135 | ? "lt" : "gt"); | ||
| 1136 | break; | ||
| 1137 | case FLT_COND_AGE_GREATER: | ||
| 1138 | NODE_NEW("age", itos_buf(nstr, cond->int_value)); | ||
| 1139 | ADD_ATTR("type", | ||
| 1140 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1141 | ? "lt" : "gt"); | ||
| 1142 | break; | ||
| 1143 | case FLT_COND_UNREAD: | ||
| 1144 | NODE_NEW("unread", NULL); | ||
| 1145 | ADD_ATTR("type", | ||
| 1146 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1147 | ? "is-not" : "is"); | ||
| 1148 | break; | ||
| 1149 | case FLT_COND_MARK: | ||
| 1150 | NODE_NEW("mark", NULL); | ||
| 1151 | ADD_ATTR("type", | ||
| 1152 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1153 | ? "is-not" : "is"); | ||
| 1154 | break; | ||
| 1155 | case FLT_COND_COLOR_LABEL: | ||
| 1156 | NODE_NEW("color-label", NULL); | ||
| 1157 | ADD_ATTR("type", | ||
| 1158 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1159 | ? "is-not" : "is"); | ||
| 1160 | break; | ||
| 1161 | case FLT_COND_MIME: | ||
| 1162 | NODE_NEW("mime", NULL); | ||
| 1163 | ADD_ATTR("type", | ||
| 1164 | FLT_IS_NOT_MATCH(cond->match_flag) | ||
| 1165 | ? "is-not" : "is"); | ||
| 1166 | break; | ||
| 1167 | case FLT_COND_ACCOUNT: | ||
| 1168 | NODE_NEW("account-id", itos_buf(nstr, cond->int_value)); | ||
| 1169 | break; | ||
| 1170 | default: | ||
| 1171 | break; | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | if (node) { | ||
| 1175 | fputs(" ", pfile->fp); | ||
| 1176 | xml_file_put_node(pfile->fp, node); | ||
| 1177 | xml_free_node(node); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | if (rule->target_folder) { | ||
| 1182 | XMLNode *node; | ||
| 1183 | |||
| 1184 | NODE_NEW("target-folder", rule->target_folder); | ||
| 1185 | ADD_ATTR("recursive", rule->recursive | ||
| 1186 | ? "true" : "false"); | ||
| 1187 | fputs(" ", pfile->fp); | ||
| 1188 | xml_file_put_node(pfile->fp, node); | ||
| 1189 | xml_free_node(node); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | fputs(" </condition-list>\n", pfile->fp); | ||
| 1193 | |||
| 1194 | fputs(" <action-list>\n", pfile->fp); | ||
| 1195 | |||
| 1196 | for (cur_action = rule->action_list; cur_action != NULL; | ||
| 1197 | cur_action = cur_action->next) { | ||
| 1198 | FilterAction *action = (FilterAction *)cur_action->data; | ||
| 1199 | XMLNode *node = NULL; | ||
| 1200 | |||
| 1201 | switch (action->type) { | ||
| 1202 | case FLT_ACTION_MOVE: | ||
| 1203 | NODE_NEW("move", action->str_value); | ||
| 1204 | break; | ||
| 1205 | case FLT_ACTION_COPY: | ||
| 1206 | NODE_NEW("copy", action->str_value); | ||
| 1207 | break; | ||
| 1208 | case FLT_ACTION_NOT_RECEIVE: | ||
| 1209 | NODE_NEW("not-receive", NULL); | ||
| 1210 | break; | ||
| 1211 | case FLT_ACTION_DELETE: | ||
| 1212 | NODE_NEW("delete", NULL); | ||
| 1213 | break; | ||
| 1214 | case FLT_ACTION_EXEC: | ||
| 1215 | NODE_NEW("exec", action->str_value); | ||
| 1216 | break; | ||
| 1217 | case FLT_ACTION_EXEC_ASYNC: | ||
| 1218 | NODE_NEW("exec-async", action->str_value); | ||
| 1219 | break; | ||
| 1220 | case FLT_ACTION_MARK: | ||
| 1221 | NODE_NEW("mark", NULL); | ||
| 1222 | break; | ||
| 1223 | case FLT_ACTION_COLOR_LABEL: | ||
| 1224 | NODE_NEW("color-label", action->str_value); | ||
| 1225 | break; | ||
| 1226 | case FLT_ACTION_MARK_READ: | ||
| 1227 | NODE_NEW("mark-as-read", NULL); | ||
| 1228 | break; | ||
| 1229 | case FLT_ACTION_FORWARD: | ||
| 1230 | NODE_NEW("forward", action->str_value); | ||
| 1231 | break; | ||
| 1232 | case FLT_ACTION_FORWARD_AS_ATTACHMENT: | ||
| 1233 | NODE_NEW("forward-as-attachment", | ||
| 1234 | action->str_value); | ||
| 1235 | break; | ||
| 1236 | case FLT_ACTION_REDIRECT: | ||
| 1237 | NODE_NEW("redirect", action->str_value); | ||
| 1238 | break; | ||
| 1239 | case FLT_ACTION_STOP_EVAL: | ||
| 1240 | NODE_NEW("stop-eval", NULL); | ||
| 1241 | break; | ||
| 1242 | default: | ||
| 1243 | break; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | if (node) { | ||
| 1247 | fputs(" ", pfile->fp); | ||
| 1248 | xml_file_put_node(pfile->fp, node); | ||
| 1249 | xml_free_node(node); | ||
| 1250 | } | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | fputs(" </action-list>\n", pfile->fp); | ||
| 1254 | |||
| 1255 | fputs(" </rule>\n", pfile->fp); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | fputs("</filter>\n", pfile->fp); | ||
| 1259 | |||
| 1260 | if (prefs_file_close(pfile) < 0) { | ||
| 1261 | g_warning("failed to write filter configuration to file: %s\n", | ||
| 1262 | file); | ||
| 1263 | return; | ||
| 1264 | } | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | void filter_write_config(void) | ||
| 1268 | { | ||
| 1269 | gchar *rcpath; | ||
| 1270 | |||
| 1271 | debug_print("Writing filter configuration...\n"); | ||
| 1272 | |||
| 1273 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, FILTER_LIST, | ||
| 1274 | NULL); | ||
| 1275 | filter_write_file(prefs_common.fltlist, rcpath); | ||
| 1276 | g_free(rcpath); | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | #undef NODE_NEW | ||
| 1280 | #undef ADD_ATTR | ||
| 1281 | |||
| 1282 | gchar *filter_get_str(FilterRule *rule) | ||
| 1283 | { | ||
| 1284 | gchar *str; | ||
| 1285 | FilterCond *cond1, *cond2; | ||
| 1286 | FilterAction *action = NULL; | ||
| 1287 | GSList *cur; | ||
| 1288 | gint flag1 = 0, flag2 = 0; | ||
| 1289 | |||
| 1290 | cond1 = (FilterCond *)rule->cond_list->data; | ||
| 1291 | if (rule->cond_list->next) | ||
| 1292 | cond2 = (FilterCond *)rule->cond_list->next->data; | ||
| 1293 | else | ||
| 1294 | cond2 = NULL; | ||
| 1295 | |||
| 1296 | /* new -> old flag conversion */ | ||
| 1297 | switch (cond1->match_type) { | ||
| 1298 | case FLT_CONTAIN: | ||
| 1299 | case FLT_EQUAL: | ||
| 1300 | flag1 = FLT_IS_NOT_MATCH(cond1->match_flag) ? 0 : FLT_O_CONTAIN; | ||
| 1301 | if (FLT_IS_CASE_SENS(cond1->match_flag)) | ||
| 1302 | flag1 |= FLT_O_CASE_SENS; | ||
| 1303 | break; | ||
| 1304 | case FLT_REGEX: | ||
| 1305 | flag1 = FLT_O_REGEX; break; | ||
| 1306 | default: | ||
| 1307 | break; | ||
| 1308 | } | ||
| 1309 | if (cond2) { | ||
| 1310 | switch (cond2->match_type) { | ||
| 1311 | case FLT_CONTAIN: | ||
| 1312 | case FLT_EQUAL: | ||
| 1313 | flag2 = FLT_IS_NOT_MATCH(cond2->match_flag) ? 0 : FLT_O_CONTAIN; | ||
| 1314 | if (FLT_IS_CASE_SENS(cond2->match_flag)) | ||
| 1315 | flag2 |= FLT_O_CASE_SENS; | ||
| 1316 | break; | ||
| 1317 | case FLT_REGEX: | ||
| 1318 | flag2 = FLT_O_REGEX; break; | ||
| 1319 | default: | ||
| 1320 | break; | ||
| 1321 | } | ||
| 1322 | } else | ||
| 1323 | flag2 = FLT_O_CONTAIN; | ||
| 1324 | |||
| 1325 | for (cur = rule->action_list; cur != NULL; cur = cur->next) { | ||
| 1326 | action = (FilterAction *)cur->data; | ||
| 1327 | if (action->type == FLT_ACTION_MOVE || | ||
| 1328 | action->type == FLT_ACTION_NOT_RECEIVE || | ||
| 1329 | action->type == FLT_ACTION_DELETE) | ||
| 1330 | break; | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | str = g_strdup_printf | ||
| 1334 | ("%s:%s:%c:%s:%s:%s:%d:%d:%c", | ||
| 1335 | cond1->header_name, cond1->str_value ? cond1->str_value : "", | ||
| 1336 | (cond2 && cond2->header_name) ? | ||
| 1337 | (rule->bool_op == FLT_AND ? '&' : '|') : ' ', | ||
| 1338 | (cond2 && cond2->header_name) ? cond2->header_name : "", | ||
| 1339 | (cond2 && cond2->str_value) ? cond2->str_value : "", | ||
| 1340 | (action && action->str_value) ? action->str_value : "", | ||
| 1341 | flag1, flag2, | ||
| 1342 | (action && action->type == FLT_ACTION_MOVE) ? 'm' : | ||
| 1343 | (action && action->type == FLT_ACTION_NOT_RECEIVE) ? 'n' : | ||
| 1344 | (action && action->type == FLT_ACTION_DELETE) ? 'd' : ' '); | ||
| 1345 | |||
| 1346 | return str; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | #define PARSE_ONE_PARAM(p, srcp) \ | ||
| 1350 | { \ | ||
| 1351 | p = strchr(srcp, '\t'); \ | ||
| 1352 | if (!p) return NULL; \ | ||
| 1353 | else \ | ||
| 1354 | *p++ = '\0'; \ | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | FilterRule *filter_read_str(const gchar *str) | ||
| 1358 | { | ||
| 1359 | FilterRule *rule; | ||
| 1360 | FilterBoolOp bool_op; | ||
| 1361 | gint flag; | ||
| 1362 | FilterCond *cond; | ||
| 1363 | FilterMatchType match_type; | ||
| 1364 | FilterMatchFlag match_flag; | ||
| 1365 | FilterAction *action; | ||
| 1366 | GSList *cond_list = NULL; | ||
| 1367 | GSList *action_list = NULL; | ||
| 1368 | gchar *tmp; | ||
| 1369 | gchar *rule_name; | ||
| 1370 | gchar *name1, *body1, *op, *name2, *body2, *dest; | ||
| 1371 | gchar *flag1 = NULL, *flag2 = NULL, *action1 = NULL; | ||
| 1372 | |||
| 1373 | Xstrdup_a(tmp, str, return NULL); | ||
| 1374 | |||
| 1375 | name1 = tmp; | ||
| 1376 | PARSE_ONE_PARAM(body1, name1); | ||
| 1377 | PARSE_ONE_PARAM(op, body1); | ||
| 1378 | PARSE_ONE_PARAM(name2, op); | ||
| 1379 | PARSE_ONE_PARAM(body2, name2); | ||
| 1380 | PARSE_ONE_PARAM(dest, body2); | ||
| 1381 | if (strchr(dest, '\t')) { | ||
| 1382 | gchar *p; | ||
| 1383 | |||
| 1384 | PARSE_ONE_PARAM(flag1, dest); | ||
| 1385 | PARSE_ONE_PARAM(flag2, flag1); | ||
| 1386 | PARSE_ONE_PARAM(action1, flag2); | ||
| 1387 | if ((p = strchr(action1, '\t'))) *p = '\0'; | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | bool_op = (*op == '&') ? FLT_AND : FLT_OR; | ||
| 1391 | |||
| 1392 | if (*name1) { | ||
| 1393 | if (flag1) | ||
| 1394 | flag = (FilterOldFlag)strtoul(flag1, NULL, 10); | ||
| 1395 | else | ||
| 1396 | flag = FLT_O_CONTAIN; | ||
| 1397 | match_type = FLT_CONTAIN; | ||
| 1398 | match_flag = 0; | ||
| 1399 | if (flag & FLT_O_REGEX) | ||
| 1400 | match_type = FLT_REGEX; | ||
| 1401 | else if (!(flag & FLT_O_CONTAIN)) | ||
| 1402 | match_flag = FLT_NOT_MATCH; | ||
| 1403 | if (flag & FLT_O_CASE_SENS) | ||
| 1404 | match_flag |= FLT_CASE_SENS; | ||
| 1405 | cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag, | ||
| 1406 | name1, body1); | ||
| 1407 | cond_list = g_slist_append(cond_list, cond); | ||
| 1408 | } | ||
| 1409 | if (*name2) { | ||
| 1410 | if (flag2) | ||
| 1411 | flag = (FilterOldFlag)strtoul(flag2, NULL, 10); | ||
| 1412 | else | ||
| 1413 | flag = FLT_O_CONTAIN; | ||
| 1414 | match_type = FLT_CONTAIN; | ||
| 1415 | match_flag = 0; | ||
| 1416 | if (flag & FLT_O_REGEX) | ||
| 1417 | match_type = FLT_REGEX; | ||
| 1418 | else if (!(flag & FLT_O_CONTAIN)) | ||
| 1419 | match_flag = FLT_NOT_MATCH; | ||
| 1420 | if (flag & FLT_O_CASE_SENS) | ||
| 1421 | match_flag |= FLT_CASE_SENS; | ||
| 1422 | cond = filter_cond_new(FLT_COND_HEADER, match_type, match_flag, | ||
| 1423 | name2, body2); | ||
| 1424 | cond_list = g_slist_append(cond_list, cond); | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | action = filter_action_new(FLT_ACTION_MOVE, | ||
| 1428 | *dest ? g_strdup(dest) : NULL); | ||
| 1429 | if (action1) { | ||
| 1430 | switch (*action1) { | ||
| 1431 | case 'm': action->type = FLT_ACTION_MOVE; break; | ||
| 1432 | case 'n': action->type = FLT_ACTION_NOT_RECEIVE; break; | ||
| 1433 | case 'd': action->type = FLT_ACTION_DELETE; break; | ||
| 1434 | default: g_warning("Invalid action: `%c'\n", *action1); | ||
| 1435 | } | ||
| 1436 | } | ||
| 1437 | action_list = g_slist_append(action_list, action); | ||
| 1438 | |||
| 1439 | Xstrdup_a(rule_name, str, return NULL); | ||
| 1440 | subst_char(rule_name, '\t', ':'); | ||
| 1441 | |||
| 1442 | rule = filter_rule_new(rule_name, bool_op, cond_list, action_list); | ||
| 1443 | |||
| 1444 | return rule; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | void filter_set_addressbook_func(FilterInAddressBookFunc func) | ||
| 1448 | { | ||
| 1449 | default_addrbook_func = func; | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | FilterInAddressBookFunc filter_get_addressbook_func(void) | ||
| 1453 | { | ||
| 1454 | return default_addrbook_func; | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | FilterRule *filter_rule_new(const gchar *name, FilterBoolOp bool_op, | ||
| 1458 | GSList *cond_list, GSList *action_list) | ||
| 1459 | { | ||
| 1460 | FilterRule *rule; | ||
| 1461 | |||
| 1462 | rule = g_new0(FilterRule, 1); | ||
| 1463 | rule->name = g_strdup(name); | ||
| 1464 | rule->bool_op = bool_op; | ||
| 1465 | rule->cond_list = cond_list; | ||
| 1466 | rule->action_list = action_list; | ||
| 1467 | rule->timing = FLT_TIMING_ANY; | ||
| 1468 | rule->enabled = TRUE; | ||
| 1469 | |||
| 1470 | return rule; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | FilterCond *filter_cond_new(FilterCondType type, | ||
| 1474 | FilterMatchType match_type, | ||
| 1475 | FilterMatchFlag match_flag, | ||
| 1476 | const gchar *header, const gchar *value) | ||
| 1477 | { | ||
| 1478 | FilterCond *cond; | ||
| 1479 | |||
| 1480 | cond = g_new0(FilterCond, 1); | ||
| 1481 | cond->type = type; | ||
| 1482 | cond->match_type = match_type; | ||
| 1483 | cond->match_flag = match_flag; | ||
| 1484 | |||
| 1485 | if (type == FLT_COND_HEADER) | ||
| 1486 | cond->header_name = | ||
| 1487 | (header && *header) ? g_strdup(header) : NULL; | ||
| 1488 | else | ||
| 1489 | cond->header_name = NULL; | ||
| 1490 | |||
| 1491 | cond->str_value = (value && *value) ? g_strdup(value) : NULL; | ||
| 1492 | if (type == FLT_COND_SIZE_GREATER || type == FLT_COND_AGE_GREATER || | ||
| 1493 | type == FLT_COND_ACCOUNT) | ||
| 1494 | cond->int_value = atoi(value); | ||
| 1495 | else | ||
| 1496 | cond->int_value = 0; | ||
| 1497 | |||
| 1498 | if (match_type == FLT_REGEX) | ||
| 1499 | cond->match_func = strmatch_regex; | ||
| 1500 | else if (match_type == FLT_EQUAL) { | ||
| 1501 | if (FLT_IS_CASE_SENS(match_flag)) | ||
| 1502 | cond->match_func = str_find_equal; | ||
| 1503 | else | ||
| 1504 | cond->match_func = str_case_find_equal; | ||
| 1505 | } else if (match_type == FLT_IN_ADDRESSBOOK) { | ||
| 1506 | cond->match_func = str_case_find_equal; | ||
| 1507 | } else { | ||
| 1508 | if (FLT_IS_CASE_SENS(match_flag)) | ||
| 1509 | cond->match_func = str_find; | ||
| 1510 | else | ||
| 1511 | cond->match_func = str_case_find; | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | return cond; | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | FilterAction *filter_action_new(FilterActionType type, const gchar *str) | ||
| 1518 | { | ||
| 1519 | FilterAction *action; | ||
| 1520 | |||
| 1521 | action = g_new0(FilterAction, 1); | ||
| 1522 | action->type = type; | ||
| 1523 | |||
| 1524 | action->str_value = (str && *str) ? g_strdup(str) : NULL; | ||
| 1525 | if (type == FLT_ACTION_COLOR_LABEL && str) | ||
| 1526 | action->int_value = atoi(str); | ||
| 1527 | else | ||
| 1528 | action->int_value = 0; | ||
| 1529 | |||
| 1530 | return action; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | FilterInfo *filter_info_new(void) | ||
| 1534 | { | ||
| 1535 | FilterInfo *fltinfo; | ||
| 1536 | |||
| 1537 | fltinfo = g_new0(FilterInfo, 1); | ||
| 1538 | fltinfo->dest_list = NULL; | ||
| 1539 | fltinfo->move_dest = NULL; | ||
| 1540 | fltinfo->drop_done = FALSE; | ||
| 1541 | fltinfo->error = FLT_ERROR_OK; | ||
| 1542 | fltinfo->last_exec_exit_status = 0; | ||
| 1543 | |||
| 1544 | return fltinfo; | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | FilterRule *filter_junk_rule_create(PrefsAccount *account, | ||
| 1548 | FolderItem *default_junk, | ||
| 1549 | gboolean is_manual) | ||
| 1550 | { | ||
| 1551 | FilterRule *rule; | ||
| 1552 | FilterCond *cond; | ||
| 1553 | FilterAction *action; | ||
| 1554 | GSList *cond_list = NULL, *action_list = NULL; | ||
| 1555 | gchar *junk_id = NULL; | ||
| 1556 | FolderItem *item = NULL; | ||
| 1557 | |||
| 1558 | if (!prefs_common.junk_classify_cmd) | ||
| 1559 | return NULL; | ||
| 1560 | |||
| 1561 | if (prefs_common.junk_folder) | ||
| 1562 | item = folder_find_item_from_identifier(prefs_common.junk_folder); | ||
| 1563 | |||
| 1564 | if (!item && account) { | ||
| 1565 | Folder *folder = NULL; | ||
| 1566 | GList *list; | ||
| 1567 | |||
| 1568 | /* find most suitable Junk folder for account */ | ||
| 1569 | |||
| 1570 | if (account->inbox && *account->inbox == '#') { | ||
| 1571 | FolderItem *inbox; | ||
| 1572 | inbox = folder_find_item_from_identifier(account->inbox); | ||
| 1573 | if (inbox) { | ||
| 1574 | folder = inbox->folder; | ||
| 1575 | if (folder) | ||
| 1576 | item = folder_get_junk(folder); | ||
| 1577 | } | ||
| 1578 | } | ||
| 1579 | if (!item) { | ||
| 1580 | folder = FOLDER(account->folder); | ||
| 1581 | if (folder) | ||
| 1582 | item = folder_get_junk(folder); | ||
| 1583 | } | ||
| 1584 | if (!item) { | ||
| 1585 | for (list = folder_get_list(); list != NULL; list = list->next) { | ||
| 1586 | folder = FOLDER(list->data); | ||
| 1587 | if (FOLDER_IS_LOCAL(folder)) { | ||
| 1588 | if (folder->account == account) | ||
| 1589 | item = folder_get_junk(folder); | ||
| 1590 | if (!item && folder->node) { | ||
| 1591 | item = FOLDER_ITEM(folder->node->data); | ||
| 1592 | if (item && item->account == account && item->folder) | ||
| 1593 | item = folder_get_junk(item->folder); | ||
| 1594 | else | ||
| 1595 | item = NULL; | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | if (item) | ||
| 1599 | break; | ||
| 1600 | } | ||
| 1601 | } | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | if (!item) | ||
| 1605 | item = default_junk; | ||
| 1606 | if (!item) | ||
| 1607 | item = folder_get_default_junk(); | ||
| 1608 | if (!item) | ||
| 1609 | return NULL; | ||
| 1610 | junk_id = folder_item_get_identifier(item); | ||
| 1611 | if (!junk_id) | ||
| 1612 | return NULL; | ||
| 1613 | |||
| 1614 | debug_print("filter_junk_rule_create: junk folder: %s\n", | ||
| 1615 | junk_id); | ||
| 1616 | |||
| 1617 | if (prefs_common.nofilter_junk_sender_in_book) { | ||
| 1618 | cond = filter_cond_new(FLT_COND_HEADER, FLT_IN_ADDRESSBOOK, | ||
| 1619 | FLT_NOT_MATCH, "From", NULL); | ||
| 1620 | cond_list = g_slist_append(cond_list, cond); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | cond = filter_cond_new(FLT_COND_CMD_TEST, 0, 0, NULL, | ||
| 1624 | prefs_common.junk_classify_cmd); | ||
| 1625 | cond_list = g_slist_append(cond_list, cond); | ||
| 1626 | |||
| 1627 | if (prefs_common.delete_junk_on_recv && !is_manual) { | ||
| 1628 | action = filter_action_new(FLT_ACTION_COPY, junk_id); | ||
| 1629 | action_list = g_slist_append(NULL, action); | ||
| 1630 | action = filter_action_new(FLT_ACTION_DELETE, NULL); | ||
| 1631 | action_list = g_slist_append(action_list, action); | ||
| 1632 | } else { | ||
| 1633 | action = filter_action_new(FLT_ACTION_MOVE, junk_id); | ||
| 1634 | action_list = g_slist_append(NULL, action); | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | if (prefs_common.mark_junk_as_read) { | ||
| 1638 | action = filter_action_new(FLT_ACTION_MARK_READ, NULL); | ||
| 1639 | action_list = g_slist_append(action_list, action); | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | if (is_manual) | ||
| 1643 | rule = filter_rule_new(_("Junk mail filter (manual)"), FLT_AND, | ||
| 1644 | cond_list, action_list); | ||
| 1645 | else | ||
| 1646 | rule = filter_rule_new(_("Junk mail filter"), FLT_AND, | ||
| 1647 | cond_list, action_list); | ||
| 1648 | |||
| 1649 | g_free(junk_id); | ||
| 1650 | |||
| 1651 | return rule; | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | void filter_rule_rename_dest_path(FilterRule *rule, const gchar *old_path, | ||
| 1655 | const gchar *new_path) | ||
| 1656 | { | ||
| 1657 | FilterAction *action; | ||
| 1658 | GSList *cur; | ||
| 1659 | gchar *base; | ||
| 1660 | gchar *dest_path; | ||
| 1661 | gint oldpathlen; | ||
| 1662 | |||
| 1663 | oldpathlen = strlen(old_path); | ||
| 1664 | |||
| 1665 | for (cur = rule->action_list; cur != NULL; cur = cur->next) { | ||
| 1666 | action = (FilterAction *)cur->data; | ||
| 1667 | |||
| 1668 | if (action->type != FLT_ACTION_MOVE && | ||
| 1669 | action->type != FLT_ACTION_COPY) | ||
| 1670 | continue; | ||
| 1671 | |||
| 1672 | if (action->str_value && | ||
| 1673 | !strncmp(old_path, action->str_value, oldpathlen)) { | ||
| 1674 | base = action->str_value + oldpathlen; | ||
| 1675 | if (*base != '/' && *base != '\0') | ||
| 1676 | continue; | ||
| 1677 | while (*base == '/') base++; | ||
| 1678 | if (*base == '\0') | ||
| 1679 | dest_path = g_strdup(new_path); | ||
| 1680 | else | ||
| 1681 | dest_path = g_strconcat(new_path, "/", base, | ||
| 1682 | NULL); | ||
| 1683 | debug_print("filter_rule_rename_dest_path(): " | ||
| 1684 | "renaming %s -> %s\n", | ||
| 1685 | action->str_value, dest_path); | ||
| 1686 | g_free(action->str_value); | ||
| 1687 | action->str_value = dest_path; | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | void filter_rule_delete_action_by_dest_path(FilterRule *rule, const gchar *path) | ||
| 1693 | { | ||
| 1694 | FilterAction *action; | ||
| 1695 | GSList *cur; | ||
| 1696 | GSList *next; | ||
| 1697 | gint pathlen; | ||
| 1698 | |||
| 1699 | pathlen = strlen(path); | ||
| 1700 | |||
| 1701 | for (cur = rule->action_list; cur != NULL; cur = next) { | ||
| 1702 | action = (FilterAction *)cur->data; | ||
| 1703 | next = cur->next; | ||
| 1704 | |||
| 1705 | if (action->type != FLT_ACTION_MOVE && | ||
| 1706 | action->type != FLT_ACTION_COPY) | ||
| 1707 | continue; | ||
| 1708 | |||
| 1709 | if (action->str_value && | ||
| 1710 | !strncmp(path, action->str_value, pathlen) && | ||
| 1711 | (action->str_value[pathlen] == '/' || | ||
| 1712 | action->str_value[pathlen] == '\0')) { | ||
| 1713 | debug_print("filter_rule_delete_action_by_dest_path(): " | ||
| 1714 | "deleting %s\n", action->str_value); | ||
| 1715 | rule->action_list = g_slist_remove | ||
| 1716 | (rule->action_list, action); | ||
| 1717 | filter_action_free(action); | ||
| 1718 | } | ||
| 1719 | } | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | void filter_list_rename_path(const gchar *old_path, const gchar *new_path) | ||
| 1723 | { | ||
| 1724 | GSList *cur; | ||
| 1725 | |||
| 1726 | g_return_if_fail(old_path != NULL); | ||
| 1727 | g_return_if_fail(new_path != NULL); | ||
| 1728 | |||
| 1729 | for (cur = prefs_common.fltlist; cur != NULL; cur = cur->next) { | ||
| 1730 | FilterRule *rule = (FilterRule *)cur->data; | ||
| 1731 | filter_rule_rename_dest_path(rule, old_path, new_path); | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | filter_write_config(); | ||
| 1735 | } | ||
| 1736 | |||
| 1737 | void filter_list_delete_path(const gchar *path) | ||
| 1738 | { | ||
| 1739 | GSList *cur; | ||
| 1740 | GSList *next; | ||
| 1741 | |||
| 1742 | g_return_if_fail(path != NULL); | ||
| 1743 | |||
| 1744 | for (cur = prefs_common.fltlist; cur != NULL; cur = next) { | ||
| 1745 | FilterRule *rule = (FilterRule *)cur->data; | ||
| 1746 | next = cur->next; | ||
| 1747 | |||
| 1748 | filter_rule_delete_action_by_dest_path(rule, path); | ||
| 1749 | if (!rule->action_list) { | ||
| 1750 | prefs_common.fltlist = | ||
| 1751 | g_slist_remove(prefs_common.fltlist, rule); | ||
| 1752 | filter_rule_free(rule); | ||
| 1753 | } | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | filter_write_config(); | ||
| 1757 | } | ||
| 1758 | |||
| 1759 | void filter_rule_match_type_str_to_enum(const gchar *match_type, | ||
| 1760 | FilterMatchType *type, | ||
| 1761 | FilterMatchFlag *flag) | ||
| 1762 | { | ||
| 1763 | g_return_if_fail(match_type != NULL); | ||
| 1764 | |||
| 1765 | *type = FLT_CONTAIN; | ||
| 1766 | *flag = 0; | ||
| 1767 | |||
| 1768 | if (!strcmp(match_type, "contains")) { | ||
| 1769 | *type = FLT_CONTAIN; | ||
| 1770 | } else if (!strcmp(match_type, "not-contain")) { | ||
| 1771 | *type = FLT_CONTAIN; | ||
| 1772 | *flag = FLT_NOT_MATCH; | ||
| 1773 | } else if (!strcmp(match_type, "is")) { | ||
| 1774 | *type = FLT_EQUAL; | ||
| 1775 | } else if (!strcmp(match_type, "is-not")) { | ||
| 1776 | *type = FLT_EQUAL; | ||
| 1777 | *flag = FLT_NOT_MATCH; | ||
| 1778 | } else if (!strcmp(match_type, "regex")) { | ||
| 1779 | *type = FLT_REGEX; | ||
| 1780 | } else if (!strcmp(match_type, "not-regex")) { | ||
| 1781 | *type = FLT_REGEX; | ||
| 1782 | *flag = FLT_NOT_MATCH; | ||
| 1783 | } else if (!strcmp(match_type, "in-addressbook")) { | ||
| 1784 | *type = FLT_IN_ADDRESSBOOK; | ||
| 1785 | } else if (!strcmp(match_type, "not-in-addressbook")) { | ||
| 1786 | *type = FLT_IN_ADDRESSBOOK; | ||
| 1787 | *flag = FLT_NOT_MATCH; | ||
| 1788 | } else if (!strcmp(match_type, "gt")) { | ||
| 1789 | } else if (!strcmp(match_type, "lt")) { | ||
| 1790 | *flag = FLT_NOT_MATCH; | ||
| 1791 | } | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | void filter_get_keyword_from_msg(MsgInfo *msginfo, gchar **header, gchar **key, | ||
| 1795 | FilterCreateType type) | ||
| 1796 | { | ||
| 1797 | static HeaderEntry hentry[] = {{"List-Id:", NULL, TRUE}, | ||
| 1798 | {"X-ML-Name:", NULL, TRUE}, | ||
| 1799 | {"X-List:", NULL, TRUE}, | ||
| 1800 | {"X-Mailing-list:", NULL, TRUE}, | ||
| 1801 | {"X-Sequence:", NULL, TRUE}, | ||
| 1802 | {NULL, NULL, FALSE}}; | ||
| 1803 | enum | ||
| 1804 | { | ||
| 1805 | H_LIST_ID = 0, | ||
| 1806 | H_X_ML_NAME = 1, | ||
| 1807 | H_X_LIST = 2, | ||
| 1808 | H_X_MAILING_LIST = 3, | ||
| 1809 | H_X_SEQUENCE = 4 | ||
| 1810 | }; | ||
| 1811 | |||
| 1812 | FILE *fp; | ||
| 1813 | |||
| 1814 | g_return_if_fail(msginfo != NULL); | ||
| 1815 | g_return_if_fail(header != NULL); | ||
| 1816 | g_return_if_fail(key != NULL); | ||
| 1817 | |||
| 1818 | *header = NULL; | ||
| 1819 | *key = NULL; | ||
| 1820 | |||
| 1821 | switch (type) { | ||
| 1822 | case FLT_BY_NONE: | ||
| 1823 | return; | ||
| 1824 | case FLT_BY_AUTO: | ||
| 1825 | if ((fp = procmsg_open_message(msginfo)) == NULL) | ||
| 1826 | return; | ||
| 1827 | procheader_get_header_fields(fp, hentry); | ||
| 1828 | fclose(fp); | ||
| 1829 | |||
| 1830 | #define SET_FILTER_KEY(hstr, idx) \ | ||
| 1831 | { \ | ||
| 1832 | *header = g_strdup(hstr); \ | ||
| 1833 | *key = hentry[idx].body; \ | ||
| 1834 | hentry[idx].body = NULL; \ | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | if (hentry[H_LIST_ID].body != NULL) { | ||
| 1838 | SET_FILTER_KEY("List-Id", H_LIST_ID); | ||
| 1839 | extract_list_id_str(*key); | ||
| 1840 | } else if (hentry[H_X_ML_NAME].body != NULL) { | ||
| 1841 | SET_FILTER_KEY("X-ML-Name", H_X_ML_NAME); | ||
| 1842 | } else if (hentry[H_X_LIST].body != NULL) { | ||
| 1843 | SET_FILTER_KEY("X-List", H_X_LIST); | ||
| 1844 | } else if (hentry[H_X_MAILING_LIST].body != NULL) { | ||
| 1845 | SET_FILTER_KEY("X-Mailing-list", H_X_MAILING_LIST); | ||
| 1846 | } else if (hentry[H_X_SEQUENCE].body != NULL) { | ||
| 1847 | gchar *p; | ||
| 1848 | |||
| 1849 | SET_FILTER_KEY("X-Sequence", H_X_SEQUENCE); | ||
| 1850 | p = *key; | ||
| 1851 | while (*p != '\0') { | ||
| 1852 | while (*p != '\0' && !g_ascii_isspace(*p)) p++; | ||
| 1853 | while (g_ascii_isspace(*p)) p++; | ||
| 1854 | if (g_ascii_isdigit(*p)) { | ||
| 1855 | *p = '\0'; | ||
| 1856 | break; | ||
| 1857 | } | ||
| 1858 | } | ||
| 1859 | g_strstrip(*key); | ||
| 1860 | } else if (msginfo->subject) { | ||
| 1861 | *header = g_strdup("Subject"); | ||
| 1862 | *key = g_strdup(msginfo->subject); | ||
| 1863 | } | ||
| 1864 | |||
| 1865 | #undef SET_FILTER_KEY | ||
| 1866 | |||
| 1867 | g_free(hentry[H_LIST_ID].body); | ||
| 1868 | hentry[H_LIST_ID].body = NULL; | ||
| 1869 | g_free(hentry[H_X_ML_NAME].body); | ||
| 1870 | hentry[H_X_ML_NAME].body = NULL; | ||
| 1871 | g_free(hentry[H_X_LIST].body); | ||
| 1872 | hentry[H_X_LIST].body = NULL; | ||
| 1873 | g_free(hentry[H_X_MAILING_LIST].body); | ||
| 1874 | hentry[H_X_MAILING_LIST].body = NULL; | ||
| 1875 | |||
| 1876 | break; | ||
| 1877 | case FLT_BY_FROM: | ||
| 1878 | *header = g_strdup("From"); | ||
| 1879 | *key = g_strdup(msginfo->from); | ||
| 1880 | break; | ||
| 1881 | case FLT_BY_TO: | ||
| 1882 | *header = g_strdup("To"); | ||
| 1883 | *key = g_strdup(msginfo->to); | ||
| 1884 | break; | ||
| 1885 | case FLT_BY_SUBJECT: | ||
| 1886 | *header = g_strdup("Subject"); | ||
| 1887 | *key = g_strdup(msginfo->subject); | ||
| 1888 | break; | ||
| 1889 | default: | ||
| 1890 | break; | ||
| 1891 | } | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | void filter_rule_list_free(GSList *fltlist) | ||
| 1895 | { | ||
| 1896 | GSList *cur; | ||
| 1897 | |||
| 1898 | for (cur = fltlist; cur != NULL; cur = cur->next) | ||
| 1899 | filter_rule_free((FilterRule *)cur->data); | ||
| 1900 | g_slist_free(fltlist); | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | void filter_rule_free(FilterRule *rule) | ||
| 1904 | { | ||
| 1905 | if (!rule) return; | ||
| 1906 | |||
| 1907 | g_free(rule->name); | ||
| 1908 | g_free(rule->target_folder); | ||
| 1909 | |||
| 1910 | filter_cond_list_free(rule->cond_list); | ||
| 1911 | filter_action_list_free(rule->action_list); | ||
| 1912 | |||
| 1913 | g_free(rule); | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | void filter_cond_list_free(GSList *cond_list) | ||
| 1917 | { | ||
| 1918 | GSList *cur; | ||
| 1919 | |||
| 1920 | for (cur = cond_list; cur != NULL; cur = cur->next) | ||
| 1921 | filter_cond_free((FilterCond *)cur->data); | ||
| 1922 | g_slist_free(cond_list); | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | void filter_action_list_free(GSList *action_list) | ||
| 1926 | { | ||
| 1927 | GSList *cur; | ||
| 1928 | |||
| 1929 | for (cur = action_list; cur != NULL; cur = cur->next) | ||
| 1930 | filter_action_free((FilterAction *)cur->data); | ||
| 1931 | g_slist_free(action_list); | ||
| 1932 | } | ||
| 1933 | |||
| 1934 | static void filter_cond_free(FilterCond *cond) | ||
| 1935 | { | ||
| 1936 | g_free(cond->header_name); | ||
| 1937 | g_free(cond->str_value); | ||
| 1938 | g_free(cond); | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | static void filter_action_free(FilterAction *action) | ||
| 1942 | { | ||
| 1943 | g_free(action->str_value); | ||
| 1944 | g_free(action); | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | void filter_info_free(FilterInfo *fltinfo) | ||
| 1948 | { | ||
| 1949 | g_slist_free(fltinfo->dest_list); | ||
| 1950 | g_free(fltinfo); | ||
| 1951 | } | ||
diff --git a/libsylph/filter.h b/libsylph/filter.h new file mode 100644 index 0000000..296c150 --- /dev/null +++ b/libsylph/filter.h | |||
| @@ -0,0 +1,247 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2010 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __FILTER_H__ | ||
| 21 | #define __FILTER_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "folder.h" | ||
| 26 | #include "procmsg.h" | ||
| 27 | #include "utils.h" | ||
| 28 | |||
| 29 | typedef struct _FilterCond FilterCond; | ||
| 30 | typedef struct _FilterAction FilterAction; | ||
| 31 | typedef struct _FilterRule FilterRule; | ||
| 32 | typedef struct _FilterInfo FilterInfo; | ||
| 33 | |||
| 34 | typedef enum | ||
| 35 | { | ||
| 36 | FLT_TIMING_ANY, | ||
| 37 | FLT_TIMING_ON_RECEIVE, | ||
| 38 | FLT_TIMING_MANUAL | ||
| 39 | } FilterTiming; | ||
| 40 | |||
| 41 | typedef enum | ||
| 42 | { | ||
| 43 | FLT_COND_HEADER, | ||
| 44 | FLT_COND_ANY_HEADER, | ||
| 45 | FLT_COND_TO_OR_CC, | ||
| 46 | FLT_COND_BODY, | ||
| 47 | FLT_COND_CMD_TEST, | ||
| 48 | FLT_COND_SIZE_GREATER, | ||
| 49 | FLT_COND_AGE_GREATER, | ||
| 50 | FLT_COND_UNREAD, | ||
| 51 | FLT_COND_MARK, | ||
| 52 | FLT_COND_COLOR_LABEL, | ||
| 53 | FLT_COND_MIME, | ||
| 54 | FLT_COND_ACCOUNT | ||
| 55 | } FilterCondType; | ||
| 56 | |||
| 57 | typedef enum | ||
| 58 | { | ||
| 59 | FLT_CONTAIN, | ||
| 60 | FLT_EQUAL, | ||
| 61 | FLT_REGEX, | ||
| 62 | FLT_IN_ADDRESSBOOK | ||
| 63 | } FilterMatchType; | ||
| 64 | |||
| 65 | typedef enum | ||
| 66 | { | ||
| 67 | FLT_NOT_MATCH = 1 << 0, | ||
| 68 | FLT_CASE_SENS = 1 << 1 | ||
| 69 | } FilterMatchFlag; | ||
| 70 | |||
| 71 | typedef enum | ||
| 72 | { | ||
| 73 | FLT_OR, | ||
| 74 | FLT_AND | ||
| 75 | } FilterBoolOp; | ||
| 76 | |||
| 77 | typedef enum | ||
| 78 | { | ||
| 79 | FLT_ACTION_MOVE, | ||
| 80 | FLT_ACTION_COPY, | ||
| 81 | FLT_ACTION_NOT_RECEIVE, | ||
| 82 | FLT_ACTION_DELETE, | ||
| 83 | FLT_ACTION_EXEC, | ||
| 84 | FLT_ACTION_EXEC_ASYNC, | ||
| 85 | FLT_ACTION_MARK, | ||
| 86 | FLT_ACTION_COLOR_LABEL, | ||
| 87 | FLT_ACTION_MARK_READ, | ||
| 88 | FLT_ACTION_FORWARD, | ||
| 89 | FLT_ACTION_FORWARD_AS_ATTACHMENT, | ||
| 90 | FLT_ACTION_REDIRECT, | ||
| 91 | FLT_ACTION_STOP_EVAL, | ||
| 92 | FLT_ACTION_NONE | ||
| 93 | } FilterActionType; | ||
| 94 | |||
| 95 | typedef enum | ||
| 96 | { | ||
| 97 | FLT_BY_NONE, | ||
| 98 | FLT_BY_AUTO, | ||
| 99 | FLT_BY_FROM, | ||
| 100 | FLT_BY_TO, | ||
| 101 | FLT_BY_SUBJECT | ||
| 102 | } FilterCreateType; | ||
| 103 | |||
| 104 | typedef enum | ||
| 105 | { | ||
| 106 | FLT_ERROR_OK, | ||
| 107 | FLT_ERROR_ERROR, | ||
| 108 | FLT_ERROR_EXEC_FAILED | ||
| 109 | } FilterErrorValue; | ||
| 110 | |||
| 111 | #define FLT_IS_NOT_MATCH(flag) ((flag & FLT_NOT_MATCH) != 0) | ||
| 112 | #define FLT_IS_CASE_SENS(flag) ((flag & FLT_CASE_SENS) != 0) | ||
| 113 | |||
| 114 | typedef gboolean (*FilterInAddressBookFunc) (const gchar *address); | ||
| 115 | |||
| 116 | struct _FilterCond | ||
| 117 | { | ||
| 118 | FilterCondType type; | ||
| 119 | |||
| 120 | gchar *header_name; | ||
| 121 | |||
| 122 | gchar *str_value; | ||
| 123 | gint int_value; | ||
| 124 | |||
| 125 | FilterMatchType match_type; | ||
| 126 | FilterMatchFlag match_flag; | ||
| 127 | |||
| 128 | StrFindFunc match_func; | ||
| 129 | }; | ||
| 130 | |||
| 131 | struct _FilterAction | ||
| 132 | { | ||
| 133 | FilterActionType type; | ||
| 134 | |||
| 135 | gchar *str_value; | ||
| 136 | gint int_value; | ||
| 137 | }; | ||
| 138 | |||
| 139 | struct _FilterRule | ||
| 140 | { | ||
| 141 | gchar *name; | ||
| 142 | |||
| 143 | FilterBoolOp bool_op; | ||
| 144 | |||
| 145 | GSList *cond_list; | ||
| 146 | GSList *action_list; | ||
| 147 | |||
| 148 | FilterTiming timing; | ||
| 149 | gboolean enabled; | ||
| 150 | |||
| 151 | gchar *target_folder; | ||
| 152 | gboolean recursive; | ||
| 153 | }; | ||
| 154 | |||
| 155 | struct _FilterInfo | ||
| 156 | { | ||
| 157 | PrefsAccount *account; | ||
| 158 | MsgFlags flags; | ||
| 159 | |||
| 160 | gboolean actions[FLT_ACTION_NONE]; | ||
| 161 | GSList *dest_list; | ||
| 162 | FolderItem *move_dest; | ||
| 163 | gboolean drop_done; | ||
| 164 | |||
| 165 | FilterErrorValue error; | ||
| 166 | gint last_exec_exit_status; | ||
| 167 | }; | ||
| 168 | |||
| 169 | gint filter_apply (GSList *fltlist, | ||
| 170 | const gchar *file, | ||
| 171 | FilterInfo *fltinfo); | ||
| 172 | gint filter_apply_msginfo (GSList *fltlist, | ||
| 173 | MsgInfo *msginfo, | ||
| 174 | FilterInfo *fltinfo); | ||
| 175 | |||
| 176 | gint filter_action_exec (FilterRule *rule, | ||
| 177 | MsgInfo *msginfo, | ||
| 178 | const gchar *file, | ||
| 179 | FilterInfo *fltinfo); | ||
| 180 | |||
| 181 | gboolean filter_match_rule (FilterRule *rule, | ||
| 182 | MsgInfo *msginfo, | ||
| 183 | GSList *hlist, | ||
| 184 | FilterInfo *fltinfo); | ||
| 185 | |||
| 186 | gboolean filter_rule_requires_full_headers (FilterRule *rule); | ||
| 187 | |||
| 188 | /* read / write config */ | ||
| 189 | GSList *filter_xml_node_to_filter_list (GNode *node); | ||
| 190 | GSList *filter_read_file (const gchar *file); | ||
| 191 | void filter_read_config (void); | ||
| 192 | void filter_write_file (GSList *list, | ||
| 193 | const gchar *file); | ||
| 194 | void filter_write_config (void); | ||
| 195 | |||
| 196 | /* for old filterrc */ | ||
| 197 | gchar *filter_get_str (FilterRule *rule); | ||
| 198 | FilterRule *filter_read_str (const gchar *str); | ||
| 199 | |||
| 200 | void filter_set_addressbook_func (FilterInAddressBookFunc func); | ||
| 201 | FilterInAddressBookFunc filter_get_addressbook_func | ||
| 202 | (void); | ||
| 203 | |||
| 204 | FilterRule *filter_rule_new (const gchar *name, | ||
| 205 | FilterBoolOp bool_op, | ||
| 206 | GSList *cond_list, | ||
| 207 | GSList *action_list); | ||
| 208 | FilterCond *filter_cond_new (FilterCondType type, | ||
| 209 | FilterMatchType match_type, | ||
| 210 | FilterMatchFlag match_flag, | ||
| 211 | const gchar *header, | ||
| 212 | const gchar *value); | ||
| 213 | FilterAction *filter_action_new (FilterActionType type, | ||
| 214 | const gchar *str); | ||
| 215 | FilterInfo *filter_info_new (void); | ||
| 216 | |||
| 217 | FilterRule *filter_junk_rule_create (PrefsAccount *account, | ||
| 218 | FolderItem *default_junk, | ||
| 219 | gboolean is_manual); | ||
| 220 | |||
| 221 | void filter_rule_rename_dest_path (FilterRule *rule, | ||
| 222 | const gchar *old_path, | ||
| 223 | const gchar *new_path); | ||
| 224 | void filter_rule_delete_action_by_dest_path | ||
| 225 | (FilterRule *rule, | ||
| 226 | const gchar *path); | ||
| 227 | |||
| 228 | void filter_list_rename_path (const gchar *old_path, | ||
| 229 | const gchar *new_path); | ||
| 230 | void filter_list_delete_path (const gchar *path); | ||
| 231 | |||
| 232 | void filter_rule_match_type_str_to_enum (const gchar *type_str, | ||
| 233 | FilterMatchType *type, | ||
| 234 | FilterMatchFlag *flag); | ||
| 235 | |||
| 236 | void filter_get_keyword_from_msg (MsgInfo *msginfo, | ||
| 237 | gchar **header, | ||
| 238 | gchar **key, | ||
| 239 | FilterCreateType type); | ||
| 240 | |||
| 241 | void filter_rule_list_free (GSList *fltlist); | ||
| 242 | void filter_rule_free (FilterRule *rule); | ||
| 243 | void filter_cond_list_free (GSList *cond_list); | ||
| 244 | void filter_action_list_free (GSList *action_list); | ||
| 245 | void filter_info_free (FilterInfo *info); | ||
| 246 | |||
| 247 | #endif /* __FILTER_H__ */ | ||
diff --git a/libsylph/folder.c b/libsylph/folder.c new file mode 100644 index 0000000..6fffe2d --- /dev/null +++ b/libsylph/folder.c | |||
| @@ -0,0 +1,1994 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | |||
| 32 | #include "folder.h" | ||
| 33 | #include "session.h" | ||
| 34 | #include "imap.h" | ||
| 35 | #include "news.h" | ||
| 36 | #include "mh.h" | ||
| 37 | #include "virtual.h" | ||
| 38 | #include "utils.h" | ||
| 39 | #include "xml.h" | ||
| 40 | #include "codeconv.h" | ||
| 41 | #include "prefs.h" | ||
| 42 | #include "account.h" | ||
| 43 | #include "prefs_account.h" | ||
| 44 | #include "sylmain.h" | ||
| 45 | |||
| 46 | typedef struct _FolderPrivData FolderPrivData; | ||
| 47 | |||
| 48 | struct _FolderPrivData { | ||
| 49 | Folder *folder; | ||
| 50 | FolderItem *junk; | ||
| 51 | |||
| 52 | FolderUIFunc2 ui_func2; | ||
| 53 | gpointer ui_func2_data; | ||
| 54 | |||
| 55 | gpointer data; | ||
| 56 | }; | ||
| 57 | |||
| 58 | static GList *folder_list = NULL; | ||
| 59 | static GList *folder_priv_list = NULL; | ||
| 60 | |||
| 61 | static void folder_init (Folder *folder, | ||
| 62 | const gchar *name); | ||
| 63 | |||
| 64 | static FolderPrivData *folder_get_priv (Folder *folder); | ||
| 65 | |||
| 66 | static gboolean folder_read_folder_func (GNode *node, | ||
| 67 | gpointer data); | ||
| 68 | static gchar *folder_get_list_path (void); | ||
| 69 | static void folder_write_list_recursive (GNode *node, | ||
| 70 | gpointer data); | ||
| 71 | |||
| 72 | |||
| 73 | Folder *folder_new(FolderType type, const gchar *name, const gchar *path) | ||
| 74 | { | ||
| 75 | Folder *folder = NULL; | ||
| 76 | |||
| 77 | name = name ? name : path; | ||
| 78 | switch (type) { | ||
| 79 | case F_MH: | ||
| 80 | folder = mh_get_class()->folder_new(name, path); | ||
| 81 | break; | ||
| 82 | case F_IMAP: | ||
| 83 | folder = imap_get_class()->folder_new(name, path); | ||
| 84 | break; | ||
| 85 | case F_NEWS: | ||
| 86 | folder = news_get_class()->folder_new(name, path); | ||
| 87 | break; | ||
| 88 | default: | ||
| 89 | return NULL; | ||
| 90 | } | ||
| 91 | |||
| 92 | return folder; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void folder_init(Folder *folder, const gchar *name) | ||
| 96 | { | ||
| 97 | FolderItem *item; | ||
| 98 | |||
| 99 | g_return_if_fail(folder != NULL); | ||
| 100 | |||
| 101 | folder_set_name(folder, name); | ||
| 102 | folder->account = NULL; | ||
| 103 | folder->inbox = NULL; | ||
| 104 | folder->outbox = NULL; | ||
| 105 | folder->draft = NULL; | ||
| 106 | folder->queue = NULL; | ||
| 107 | folder->trash = NULL; | ||
| 108 | folder->ui_func = NULL; | ||
| 109 | folder->ui_func_data = NULL; | ||
| 110 | item = folder_item_new(name, NULL); | ||
| 111 | item->folder = folder; | ||
| 112 | folder->node = item->node = g_node_new(item); | ||
| 113 | folder->data = NULL; | ||
| 114 | } | ||
| 115 | |||
| 116 | void folder_local_folder_init(Folder *folder, const gchar *name, | ||
| 117 | const gchar *path) | ||
| 118 | { | ||
| 119 | folder_init(folder, name); | ||
| 120 | LOCAL_FOLDER(folder)->rootpath = g_strdup(path); | ||
| 121 | } | ||
| 122 | |||
| 123 | void folder_remote_folder_init(Folder *folder, const gchar *name, | ||
| 124 | const gchar *path) | ||
| 125 | { | ||
| 126 | folder_init(folder, name); | ||
| 127 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 128 | REMOTE_FOLDER(folder)->remove_cache_on_destroy = TRUE; | ||
| 129 | } | ||
| 130 | |||
| 131 | void folder_destroy(Folder *folder) | ||
| 132 | { | ||
| 133 | FolderPrivData *priv; | ||
| 134 | |||
| 135 | g_return_if_fail(folder != NULL); | ||
| 136 | g_return_if_fail(folder->klass->destroy != NULL); | ||
| 137 | |||
| 138 | debug_print("folder_destroy: destroying Folder (%p)\n", folder); | ||
| 139 | |||
| 140 | folder->klass->destroy(folder); | ||
| 141 | |||
| 142 | folder_list = g_list_remove(folder_list, folder); | ||
| 143 | |||
| 144 | folder_tree_destroy(folder); | ||
| 145 | |||
| 146 | priv = folder_get_priv(folder); | ||
| 147 | folder_priv_list = g_list_remove(folder_priv_list, priv); | ||
| 148 | g_free(priv); | ||
| 149 | |||
| 150 | g_free(folder->name); | ||
| 151 | g_free(folder); | ||
| 152 | } | ||
| 153 | |||
| 154 | void folder_local_folder_destroy(LocalFolder *lfolder) | ||
| 155 | { | ||
| 156 | g_return_if_fail(lfolder != NULL); | ||
| 157 | |||
| 158 | g_free(lfolder->rootpath); | ||
| 159 | } | ||
| 160 | |||
| 161 | void folder_remote_folder_destroy(RemoteFolder *rfolder) | ||
| 162 | { | ||
| 163 | g_return_if_fail(rfolder != NULL); | ||
| 164 | |||
| 165 | if (rfolder->session) | ||
| 166 | session_destroy(rfolder->session); | ||
| 167 | } | ||
| 168 | |||
| 169 | gint folder_remote_folder_destroy_all_sessions(void) | ||
| 170 | { | ||
| 171 | GList *list; | ||
| 172 | Folder *folder; | ||
| 173 | RemoteFolder *rfolder; | ||
| 174 | |||
| 175 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 176 | folder = FOLDER(list->data); | ||
| 177 | if (FOLDER_IS_REMOTE(folder)) { | ||
| 178 | rfolder = REMOTE_FOLDER(folder); | ||
| 179 | if (rfolder->session && | ||
| 180 | !folder_remote_folder_is_session_active(rfolder)) { | ||
| 181 | session_destroy(rfolder->session); | ||
| 182 | rfolder->session = NULL; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | gboolean folder_remote_folder_is_session_active(RemoteFolder *rfolder) | ||
| 191 | { | ||
| 192 | g_return_val_if_fail(rfolder != NULL, FALSE); | ||
| 193 | |||
| 194 | if (FOLDER_TYPE(rfolder) == F_IMAP) | ||
| 195 | return imap_is_session_active(IMAP_FOLDER(rfolder)); | ||
| 196 | |||
| 197 | return FALSE; | ||
| 198 | } | ||
| 199 | |||
| 200 | gboolean folder_remote_folder_active_session_exist(void) | ||
| 201 | { | ||
| 202 | GList *list; | ||
| 203 | Folder *folder; | ||
| 204 | RemoteFolder *rfolder; | ||
| 205 | |||
| 206 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 207 | folder = FOLDER(list->data); | ||
| 208 | if (FOLDER_IS_REMOTE(folder)) { | ||
| 209 | rfolder = REMOTE_FOLDER(folder); | ||
| 210 | if (folder_remote_folder_is_session_active(rfolder)) | ||
| 211 | return TRUE; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | return FALSE; | ||
| 216 | } | ||
| 217 | |||
| 218 | gint folder_scan_tree(Folder *folder) | ||
| 219 | { | ||
| 220 | g_return_val_if_fail(folder != NULL, -1); | ||
| 221 | g_return_val_if_fail(folder->klass->scan_tree != NULL, -1); | ||
| 222 | |||
| 223 | return folder->klass->scan_tree(folder); | ||
| 224 | } | ||
| 225 | |||
| 226 | gint folder_create_tree(Folder *folder) | ||
| 227 | { | ||
| 228 | g_return_val_if_fail(folder != NULL, -1); | ||
| 229 | g_return_val_if_fail(folder->klass->create_tree != NULL, -1); | ||
| 230 | |||
| 231 | return folder->klass->create_tree(folder); | ||
| 232 | } | ||
| 233 | |||
| 234 | FolderItem *folder_item_new(const gchar *name, const gchar *path) | ||
| 235 | { | ||
| 236 | FolderItem *item; | ||
| 237 | |||
| 238 | item = g_new0(FolderItem, 1); | ||
| 239 | |||
| 240 | item->stype = F_NORMAL; | ||
| 241 | item->name = g_strdup(name); | ||
| 242 | item->path = g_strdup(path); | ||
| 243 | item->mtime = 0; | ||
| 244 | item->new = 0; | ||
| 245 | item->unread = 0; | ||
| 246 | item->total = 0; | ||
| 247 | item->unmarked_num = 0; | ||
| 248 | item->last_num = -1; | ||
| 249 | item->no_sub = FALSE; | ||
| 250 | item->no_select = FALSE; | ||
| 251 | item->collapsed = FALSE; | ||
| 252 | item->threaded = TRUE; | ||
| 253 | item->opened = FALSE; | ||
| 254 | item->updated = FALSE; | ||
| 255 | item->cache_dirty = FALSE; | ||
| 256 | item->mark_dirty = FALSE; | ||
| 257 | item->node = NULL; | ||
| 258 | item->parent = NULL; | ||
| 259 | item->folder = NULL; | ||
| 260 | item->account = NULL; | ||
| 261 | item->ac_apply_sub = FALSE; | ||
| 262 | item->auto_to = NULL; | ||
| 263 | item->use_auto_to_on_reply = FALSE; | ||
| 264 | item->auto_cc = NULL; | ||
| 265 | item->auto_bcc = NULL; | ||
| 266 | item->auto_replyto = NULL; | ||
| 267 | item->mark_queue = NULL; | ||
| 268 | item->last_selected = 0; | ||
| 269 | item->qsearch_cond_type = 0; | ||
| 270 | item->data = NULL; | ||
| 271 | |||
| 272 | return item; | ||
| 273 | } | ||
| 274 | |||
| 275 | void folder_item_append(FolderItem *parent, FolderItem *item) | ||
| 276 | { | ||
| 277 | g_return_if_fail(parent != NULL); | ||
| 278 | g_return_if_fail(parent->folder != NULL); | ||
| 279 | g_return_if_fail(parent->node != NULL); | ||
| 280 | g_return_if_fail(item != NULL); | ||
| 281 | |||
| 282 | item->parent = parent; | ||
| 283 | item->folder = parent->folder; | ||
| 284 | item->node = g_node_append_data(parent->node, item); | ||
| 285 | } | ||
| 286 | |||
| 287 | FolderItem *folder_item_copy(FolderItem *item) | ||
| 288 | { | ||
| 289 | FolderItem *new_item; | ||
| 290 | |||
| 291 | new_item = g_new0(FolderItem, 1); | ||
| 292 | |||
| 293 | new_item->stype = item->stype; | ||
| 294 | new_item->name = g_strdup(item->name); | ||
| 295 | new_item->path = g_strdup(item->path); | ||
| 296 | new_item->mtime = item->mtime; | ||
| 297 | new_item->new = item->new; | ||
| 298 | new_item->unread = item->unread; | ||
| 299 | new_item->total = item->total; | ||
| 300 | new_item->unmarked_num = item->unmarked_num; | ||
| 301 | new_item->last_num = item->last_num; | ||
| 302 | new_item->no_sub = item->no_sub; | ||
| 303 | new_item->no_select = item->no_select; | ||
| 304 | new_item->collapsed = item->collapsed; | ||
| 305 | new_item->threaded = item->threaded; | ||
| 306 | new_item->opened = item->opened; | ||
| 307 | new_item->updated = item->updated; | ||
| 308 | new_item->cache_dirty = item->cache_dirty; | ||
| 309 | new_item->mark_dirty = item->mark_dirty; | ||
| 310 | new_item->node = item->node; | ||
| 311 | new_item->parent = item->parent; | ||
| 312 | new_item->folder = item->folder; | ||
| 313 | new_item->account = item->account; | ||
| 314 | new_item->ac_apply_sub = item->ac_apply_sub; | ||
| 315 | new_item->auto_to = g_strdup(item->auto_to); | ||
| 316 | new_item->use_auto_to_on_reply = item->use_auto_to_on_reply; | ||
| 317 | new_item->auto_cc = g_strdup(item->auto_cc); | ||
| 318 | new_item->auto_bcc = g_strdup(item->auto_bcc); | ||
| 319 | new_item->auto_replyto = g_strdup(item->auto_replyto); | ||
| 320 | new_item->mark_queue = item->mark_queue; | ||
| 321 | new_item->last_selected = item->last_selected; | ||
| 322 | new_item->qsearch_cond_type = item->qsearch_cond_type; | ||
| 323 | new_item->data = item->data; | ||
| 324 | |||
| 325 | return new_item; | ||
| 326 | } | ||
| 327 | |||
| 328 | static gboolean folder_item_remove_func(GNode *node, gpointer data) | ||
| 329 | { | ||
| 330 | FolderItem *item = FOLDER_ITEM(node->data); | ||
| 331 | |||
| 332 | folder_item_destroy(item); | ||
| 333 | return FALSE; | ||
| 334 | } | ||
| 335 | |||
| 336 | void folder_item_remove(FolderItem *item) | ||
| 337 | { | ||
| 338 | GNode *node; | ||
| 339 | |||
| 340 | g_return_if_fail(item != NULL); | ||
| 341 | g_return_if_fail(item->folder != NULL); | ||
| 342 | g_return_if_fail(item->node != NULL); | ||
| 343 | |||
| 344 | node = item->node; | ||
| 345 | |||
| 346 | if (item->folder->node == node) | ||
| 347 | item->folder->node = NULL; | ||
| 348 | |||
| 349 | g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1, | ||
| 350 | folder_item_remove_func, NULL); | ||
| 351 | g_node_destroy(node); | ||
| 352 | } | ||
| 353 | |||
| 354 | void folder_item_remove_children(FolderItem *item) | ||
| 355 | { | ||
| 356 | GNode *node, *next; | ||
| 357 | |||
| 358 | g_return_if_fail(item != NULL); | ||
| 359 | g_return_if_fail(item->folder != NULL); | ||
| 360 | g_return_if_fail(item->node != NULL); | ||
| 361 | |||
| 362 | node = item->node->children; | ||
| 363 | while (node != NULL) { | ||
| 364 | next = node->next; | ||
| 365 | folder_item_remove(FOLDER_ITEM(node->data)); | ||
| 366 | node = next; | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | void folder_item_destroy(FolderItem *item) | ||
| 371 | { | ||
| 372 | Folder *folder; | ||
| 373 | |||
| 374 | g_return_if_fail(item != NULL); | ||
| 375 | |||
| 376 | folder = item->folder; | ||
| 377 | if (folder) { | ||
| 378 | if (folder->inbox == item) | ||
| 379 | folder->inbox = NULL; | ||
| 380 | else if (folder->outbox == item) | ||
| 381 | folder->outbox = NULL; | ||
| 382 | else if (folder->draft == item) | ||
| 383 | folder->draft = NULL; | ||
| 384 | else if (folder->queue == item) | ||
| 385 | folder->queue = NULL; | ||
| 386 | else if (folder->trash == item) | ||
| 387 | folder->trash = NULL; | ||
| 388 | else if (folder_get_junk(folder) == item) | ||
| 389 | folder_set_junk(folder, NULL); | ||
| 390 | } | ||
| 391 | |||
| 392 | g_free(item->name); | ||
| 393 | g_free(item->path); | ||
| 394 | g_free(item->auto_to); | ||
| 395 | g_free(item->auto_cc); | ||
| 396 | g_free(item->auto_bcc); | ||
| 397 | g_free(item->auto_replyto); | ||
| 398 | g_free(item); | ||
| 399 | } | ||
| 400 | |||
| 401 | gint folder_item_compare(FolderItem *item_a, FolderItem *item_b) | ||
| 402 | { | ||
| 403 | gint ret; | ||
| 404 | gchar *str_a, *str_b; | ||
| 405 | |||
| 406 | if (!item_a || !item_b) | ||
| 407 | return 0; | ||
| 408 | if (!item_a->parent || !item_b->parent) | ||
| 409 | return 0; | ||
| 410 | if (!item_a->name || !item_b->name) | ||
| 411 | return 0; | ||
| 412 | |||
| 413 | /* if both a and b are special folders, sort them according to | ||
| 414 | * their types (which is in-order). Note that this assumes that | ||
| 415 | * there are no multiple folders of a special type. As a special | ||
| 416 | * case, two virtual folders are compared like normal ones. */ | ||
| 417 | if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL && | ||
| 418 | (item_a->stype != F_VIRTUAL || item_b->stype != F_VIRTUAL)) | ||
| 419 | return item_a->stype - item_b->stype; | ||
| 420 | |||
| 421 | /* if b is normal folder, and a is not, b is smaller (ends up | ||
| 422 | * lower in the list) */ | ||
| 423 | if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL) | ||
| 424 | return item_b->stype - item_a->stype; | ||
| 425 | |||
| 426 | /* if b is special folder, and a is not, b is larger (ends up | ||
| 427 | * higher in the list) */ | ||
| 428 | if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL) | ||
| 429 | return item_b->stype - item_a->stype; | ||
| 430 | |||
| 431 | /* otherwise just compare the folder names */ | ||
| 432 | str_a = g_utf8_casefold(item_a->name, -1); | ||
| 433 | str_b = g_utf8_casefold(item_b->name, -1); | ||
| 434 | ret = g_utf8_collate(str_a, str_b); | ||
| 435 | g_free(str_b); | ||
| 436 | g_free(str_a); | ||
| 437 | |||
| 438 | return ret; | ||
| 439 | } | ||
| 440 | |||
| 441 | void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data) | ||
| 442 | { | ||
| 443 | g_return_if_fail(folder != NULL); | ||
| 444 | |||
| 445 | folder->ui_func = func; | ||
| 446 | folder->ui_func_data = data; | ||
| 447 | } | ||
| 448 | |||
| 449 | void folder_set_ui_func2(Folder *folder, FolderUIFunc2 func, gpointer data) | ||
| 450 | { | ||
| 451 | FolderPrivData *priv; | ||
| 452 | |||
| 453 | priv = folder_get_priv(folder); | ||
| 454 | if (priv) { | ||
| 455 | priv->ui_func2 = func; | ||
| 456 | priv->ui_func2_data = data; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | FolderUIFunc2 folder_get_ui_func2(Folder *folder) | ||
| 461 | { | ||
| 462 | FolderPrivData *priv; | ||
| 463 | |||
| 464 | priv = folder_get_priv(folder); | ||
| 465 | if (priv) | ||
| 466 | return priv->ui_func2; | ||
| 467 | |||
| 468 | return NULL; | ||
| 469 | } | ||
| 470 | |||
| 471 | gboolean folder_call_ui_func2(Folder *folder, FolderItem *item, guint count, | ||
| 472 | guint total) | ||
| 473 | { | ||
| 474 | FolderPrivData *priv; | ||
| 475 | |||
| 476 | priv = folder_get_priv(folder); | ||
| 477 | if (priv && priv->ui_func2) { | ||
| 478 | return priv->ui_func2(folder, item, count, total, | ||
| 479 | priv->ui_func2_data); | ||
| 480 | } | ||
| 481 | |||
| 482 | return TRUE; | ||
| 483 | } | ||
| 484 | |||
| 485 | void folder_set_name(Folder *folder, const gchar *name) | ||
| 486 | { | ||
| 487 | g_return_if_fail(folder != NULL); | ||
| 488 | |||
| 489 | g_free(folder->name); | ||
| 490 | folder->name = name ? g_strdup(name) : NULL; | ||
| 491 | if (folder->node && folder->node->data) { | ||
| 492 | FolderItem *item = (FolderItem *)folder->node->data; | ||
| 493 | |||
| 494 | g_free(item->name); | ||
| 495 | item->name = name ? g_strdup(name) : NULL; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | void folder_tree_destroy(Folder *folder) | ||
| 500 | { | ||
| 501 | g_return_if_fail(folder != NULL); | ||
| 502 | |||
| 503 | if (folder->node) | ||
| 504 | folder_item_remove(FOLDER_ITEM(folder->node->data)); | ||
| 505 | } | ||
| 506 | |||
| 507 | void folder_add(Folder *folder) | ||
| 508 | { | ||
| 509 | Folder *cur_folder; | ||
| 510 | GList *cur; | ||
| 511 | gint i; | ||
| 512 | FolderPrivData *priv; | ||
| 513 | |||
| 514 | debug_print("Adding Folder (%p) to folder list\n", folder); | ||
| 515 | |||
| 516 | g_return_if_fail(folder != NULL); | ||
| 517 | |||
| 518 | for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) { | ||
| 519 | cur_folder = FOLDER(cur->data); | ||
| 520 | if (FOLDER_TYPE(folder) == F_MH) { | ||
| 521 | if (FOLDER_TYPE(cur_folder) != F_MH) break; | ||
| 522 | } else if (FOLDER_TYPE(folder) == F_IMAP) { | ||
| 523 | if (FOLDER_TYPE(cur_folder) != F_MH && | ||
| 524 | FOLDER_TYPE(cur_folder) != F_IMAP) break; | ||
| 525 | } else if (FOLDER_TYPE(folder) == F_NEWS) { | ||
| 526 | if (FOLDER_TYPE(cur_folder) != F_MH && | ||
| 527 | FOLDER_TYPE(cur_folder) != F_IMAP && | ||
| 528 | FOLDER_TYPE(cur_folder) != F_NEWS) break; | ||
| 529 | } | ||
| 530 | } | ||
| 531 | |||
| 532 | folder_list = g_list_insert(folder_list, folder, i); | ||
| 533 | priv = g_new0(FolderPrivData, 1); | ||
| 534 | priv->folder = folder; | ||
| 535 | folder_priv_list = g_list_insert(folder_priv_list, priv, i); | ||
| 536 | } | ||
| 537 | |||
| 538 | GList *folder_get_list(void) | ||
| 539 | { | ||
| 540 | return folder_list; | ||
| 541 | } | ||
| 542 | |||
| 543 | gint folder_read_list(void) | ||
| 544 | { | ||
| 545 | GNode *node; | ||
| 546 | XMLNode *xmlnode; | ||
| 547 | gchar *path; | ||
| 548 | |||
| 549 | path = folder_get_list_path(); | ||
| 550 | if (!is_file_exist(path)) return -1; | ||
| 551 | node = xml_parse_file(path); | ||
| 552 | if (!node) return -1; | ||
| 553 | |||
| 554 | xmlnode = node->data; | ||
| 555 | if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) { | ||
| 556 | g_warning("wrong folder list\n"); | ||
| 557 | xml_free_tree(node); | ||
| 558 | return -1; | ||
| 559 | } | ||
| 560 | |||
| 561 | g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2, | ||
| 562 | folder_read_folder_func, NULL); | ||
| 563 | |||
| 564 | xml_free_tree(node); | ||
| 565 | if (folder_list) | ||
| 566 | return 0; | ||
| 567 | else | ||
| 568 | return -1; | ||
| 569 | } | ||
| 570 | |||
| 571 | void folder_write_list(void) | ||
| 572 | { | ||
| 573 | GList *list; | ||
| 574 | Folder *folder; | ||
| 575 | gchar *path; | ||
| 576 | PrefFile *pfile; | ||
| 577 | |||
| 578 | path = folder_get_list_path(); | ||
| 579 | if ((pfile = prefs_file_open(path)) == NULL) return; | ||
| 580 | |||
| 581 | fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", | ||
| 582 | CS_INTERNAL); | ||
| 583 | fputs("\n<folderlist>\n", pfile->fp); | ||
| 584 | |||
| 585 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 586 | folder = list->data; | ||
| 587 | folder_write_list_recursive(folder->node, pfile->fp); | ||
| 588 | } | ||
| 589 | |||
| 590 | fputs("</folderlist>\n", pfile->fp); | ||
| 591 | |||
| 592 | if (prefs_file_close(pfile) < 0) | ||
| 593 | g_warning("failed to write folder list.\n"); | ||
| 594 | |||
| 595 | if (syl_app_get()) | ||
| 596 | g_signal_emit_by_name(syl_app_get(), "folderlist-updated"); | ||
| 597 | } | ||
| 598 | |||
| 599 | struct TotalMsgStatus | ||
| 600 | { | ||
| 601 | guint new; | ||
| 602 | guint unread; | ||
| 603 | guint total; | ||
| 604 | GString *str; | ||
| 605 | }; | ||
| 606 | |||
| 607 | static gboolean folder_get_status_full_all_func(GNode *node, gpointer data) | ||
| 608 | { | ||
| 609 | FolderItem *item; | ||
| 610 | struct TotalMsgStatus *status = (struct TotalMsgStatus *)data; | ||
| 611 | gchar *id; | ||
| 612 | |||
| 613 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 614 | |||
| 615 | item = FOLDER_ITEM(node->data); | ||
| 616 | |||
| 617 | if (!item->path) return FALSE; | ||
| 618 | |||
| 619 | status->new += item->new; | ||
| 620 | status->unread += item->unread; | ||
| 621 | status->total += item->total; | ||
| 622 | |||
| 623 | if (status->str) { | ||
| 624 | id = folder_item_get_identifier(item); | ||
| 625 | g_string_sprintfa(status->str, "%5d %5d %5d %s\n", | ||
| 626 | item->new, item->unread, | ||
| 627 | item->total, id); | ||
| 628 | g_free(id); | ||
| 629 | } | ||
| 630 | |||
| 631 | return FALSE; | ||
| 632 | } | ||
| 633 | |||
| 634 | static void folder_get_status_full_all(GString *str, guint *new, guint *unread, | ||
| 635 | guint *total) | ||
| 636 | { | ||
| 637 | GList *list; | ||
| 638 | Folder *folder; | ||
| 639 | struct TotalMsgStatus status; | ||
| 640 | |||
| 641 | status.new = status.unread = status.total = 0; | ||
| 642 | status.str = str; | ||
| 643 | |||
| 644 | debug_print("Counting total number of messages...\n"); | ||
| 645 | |||
| 646 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 647 | folder = FOLDER(list->data); | ||
| 648 | if (folder->node) | ||
| 649 | g_node_traverse(folder->node, G_PRE_ORDER, | ||
| 650 | G_TRAVERSE_ALL, -1, | ||
| 651 | folder_get_status_full_all_func, | ||
| 652 | &status); | ||
| 653 | } | ||
| 654 | |||
| 655 | *new = status.new; | ||
| 656 | *unread = status.unread; | ||
| 657 | *total = status.total; | ||
| 658 | } | ||
| 659 | |||
| 660 | gchar *folder_get_status(GPtrArray *folders, gboolean full) | ||
| 661 | { | ||
| 662 | guint new, unread, total; | ||
| 663 | GString *str; | ||
| 664 | gint i; | ||
| 665 | gchar *ret; | ||
| 666 | |||
| 667 | new = unread = total = 0; | ||
| 668 | |||
| 669 | str = g_string_new(NULL); | ||
| 670 | |||
| 671 | if (folders) { | ||
| 672 | for (i = 0; i < folders->len; i++) { | ||
| 673 | FolderItem *item; | ||
| 674 | |||
| 675 | item = g_ptr_array_index(folders, i); | ||
| 676 | new += item->new; | ||
| 677 | unread += item->unread; | ||
| 678 | total += item->total; | ||
| 679 | |||
| 680 | if (full) { | ||
| 681 | gchar *id; | ||
| 682 | |||
| 683 | id = folder_item_get_identifier(item); | ||
| 684 | g_string_sprintfa(str, "%5d %5d %5d %s\n", | ||
| 685 | item->new, item->unread, | ||
| 686 | item->total, id); | ||
| 687 | g_free(id); | ||
| 688 | } | ||
| 689 | } | ||
| 690 | } else { | ||
| 691 | folder_get_status_full_all(full ? str : NULL, | ||
| 692 | &new, &unread, &total); | ||
| 693 | } | ||
| 694 | |||
| 695 | if (full) | ||
| 696 | g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total); | ||
| 697 | else | ||
| 698 | g_string_sprintfa(str, "%d %d %d\n", new, unread, total); | ||
| 699 | |||
| 700 | ret = str->str; | ||
| 701 | g_string_free(str, FALSE); | ||
| 702 | |||
| 703 | return ret; | ||
| 704 | } | ||
| 705 | |||
| 706 | Folder *folder_find_from_path(const gchar *path) | ||
| 707 | { | ||
| 708 | GList *list; | ||
| 709 | Folder *folder; | ||
| 710 | |||
| 711 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 712 | folder = list->data; | ||
| 713 | if (FOLDER_TYPE(folder) == F_MH && | ||
| 714 | !path_cmp(LOCAL_FOLDER(folder)->rootpath, path)) | ||
| 715 | return folder; | ||
| 716 | } | ||
| 717 | |||
| 718 | return NULL; | ||
| 719 | } | ||
| 720 | |||
| 721 | Folder *folder_find_from_name(const gchar *name, FolderType type) | ||
| 722 | { | ||
| 723 | GList *list; | ||
| 724 | Folder *folder; | ||
| 725 | |||
| 726 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 727 | folder = list->data; | ||
| 728 | if (FOLDER_TYPE(folder) == type && | ||
| 729 | strcmp2(name, folder->name) == 0) | ||
| 730 | return folder; | ||
| 731 | } | ||
| 732 | |||
| 733 | return NULL; | ||
| 734 | } | ||
| 735 | |||
| 736 | static gboolean folder_item_find_func(GNode *node, gpointer data) | ||
| 737 | { | ||
| 738 | FolderItem *item = node->data; | ||
| 739 | gpointer *d = data; | ||
| 740 | const gchar *path = d[0]; | ||
| 741 | |||
| 742 | if (path_cmp(path, item->path) != 0) | ||
| 743 | return FALSE; | ||
| 744 | |||
| 745 | d[1] = item; | ||
| 746 | |||
| 747 | return TRUE; | ||
| 748 | } | ||
| 749 | |||
| 750 | FolderItem *folder_find_item_from_path(const gchar *path) | ||
| 751 | { | ||
| 752 | Folder *folder; | ||
| 753 | gpointer d[2]; | ||
| 754 | |||
| 755 | folder = folder_get_default_folder(); | ||
| 756 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 757 | |||
| 758 | d[0] = (gpointer)path; | ||
| 759 | d[1] = NULL; | ||
| 760 | g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 761 | folder_item_find_func, d); | ||
| 762 | return d[1]; | ||
| 763 | } | ||
| 764 | |||
| 765 | FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name) | ||
| 766 | { | ||
| 767 | GNode *node; | ||
| 768 | FolderItem *child; | ||
| 769 | const gchar *base; | ||
| 770 | |||
| 771 | if (!name) | ||
| 772 | return NULL; | ||
| 773 | |||
| 774 | for (node = item->node->children; node != NULL; node = node->next) { | ||
| 775 | child = FOLDER_ITEM(node->data); | ||
| 776 | base = g_basename(child->path); | ||
| 777 | #ifdef G_OS_WIN32 | ||
| 778 | if (base && g_ascii_strcasecmp(base, name) == 0) | ||
| 779 | #else | ||
| 780 | if (strcmp2(base, name) == 0) | ||
| 781 | #endif | ||
| 782 | return child; | ||
| 783 | } | ||
| 784 | |||
| 785 | return NULL; | ||
| 786 | } | ||
| 787 | |||
| 788 | static const struct { | ||
| 789 | gchar *str; | ||
| 790 | FolderType type; | ||
| 791 | } type_str_table[] = { | ||
| 792 | {"#mh" , F_MH}, | ||
| 793 | {"#mbox" , F_MBOX}, | ||
| 794 | {"#maildir", F_MAILDIR}, | ||
| 795 | {"#imap" , F_IMAP}, | ||
| 796 | {"#news" , F_NEWS} | ||
| 797 | }; | ||
| 798 | |||
| 799 | static gchar *folder_get_type_string(FolderType type) | ||
| 800 | { | ||
| 801 | gint i; | ||
| 802 | |||
| 803 | for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]); | ||
| 804 | i++) { | ||
| 805 | if (type_str_table[i].type == type) | ||
| 806 | return type_str_table[i].str; | ||
| 807 | } | ||
| 808 | |||
| 809 | return NULL; | ||
| 810 | } | ||
| 811 | |||
| 812 | static FolderType folder_get_type_from_string(const gchar *str) | ||
| 813 | { | ||
| 814 | gint i; | ||
| 815 | |||
| 816 | for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]); | ||
| 817 | i++) { | ||
| 818 | if (g_ascii_strcasecmp(type_str_table[i].str, str) == 0) | ||
| 819 | return type_str_table[i].type; | ||
| 820 | } | ||
| 821 | |||
| 822 | return F_UNKNOWN; | ||
| 823 | } | ||
| 824 | |||
| 825 | gchar *folder_get_identifier(Folder *folder) | ||
| 826 | { | ||
| 827 | gchar *type_str; | ||
| 828 | |||
| 829 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 830 | |||
| 831 | type_str = folder_get_type_string(FOLDER_TYPE(folder)); | ||
| 832 | return g_strconcat(type_str, "/", folder->name, NULL); | ||
| 833 | } | ||
| 834 | |||
| 835 | gchar *folder_item_get_identifier(FolderItem *item) | ||
| 836 | { | ||
| 837 | gchar *id; | ||
| 838 | gchar *folder_id; | ||
| 839 | |||
| 840 | g_return_val_if_fail(item != NULL, NULL); | ||
| 841 | |||
| 842 | if (!item->path) { | ||
| 843 | if (!item->parent) | ||
| 844 | return folder_get_identifier(item->folder); | ||
| 845 | else | ||
| 846 | return NULL; | ||
| 847 | } | ||
| 848 | |||
| 849 | folder_id = folder_get_identifier(item->folder); | ||
| 850 | id = g_strconcat(folder_id, "/", item->path, NULL); | ||
| 851 | g_free(folder_id); | ||
| 852 | |||
| 853 | return id; | ||
| 854 | } | ||
| 855 | |||
| 856 | FolderItem *folder_find_item_from_identifier(const gchar *identifier) | ||
| 857 | { | ||
| 858 | Folder *folder; | ||
| 859 | gpointer d[2]; | ||
| 860 | gchar *str; | ||
| 861 | gchar *p; | ||
| 862 | gchar *name; | ||
| 863 | gchar *path; | ||
| 864 | FolderType type; | ||
| 865 | |||
| 866 | g_return_val_if_fail(identifier != NULL, NULL); | ||
| 867 | |||
| 868 | if (*identifier != '#') | ||
| 869 | return folder_find_item_from_path(identifier); | ||
| 870 | |||
| 871 | Xstrdup_a(str, identifier, return NULL); | ||
| 872 | |||
| 873 | p = strchr(str, '/'); | ||
| 874 | if (!p) | ||
| 875 | return folder_find_item_from_path(identifier); | ||
| 876 | *p = '\0'; | ||
| 877 | p++; | ||
| 878 | type = folder_get_type_from_string(str); | ||
| 879 | if (type == F_UNKNOWN) | ||
| 880 | return folder_find_item_from_path(identifier); | ||
| 881 | |||
| 882 | name = p; | ||
| 883 | p = strchr(p, '/'); | ||
| 884 | if (p) { | ||
| 885 | *p = '\0'; | ||
| 886 | p++; | ||
| 887 | } | ||
| 888 | |||
| 889 | folder = folder_find_from_name(name, type); | ||
| 890 | if (!folder) | ||
| 891 | return folder_find_item_from_path(identifier); | ||
| 892 | |||
| 893 | if (!p) | ||
| 894 | return FOLDER_ITEM(folder->node->data); | ||
| 895 | |||
| 896 | path = p; | ||
| 897 | |||
| 898 | d[0] = (gpointer)path; | ||
| 899 | d[1] = NULL; | ||
| 900 | g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 901 | folder_item_find_func, d); | ||
| 902 | return d[1]; | ||
| 903 | } | ||
| 904 | |||
| 905 | FolderItem *folder_find_item_and_num_from_id(const gchar *identifier, gint *num) | ||
| 906 | { | ||
| 907 | gchar *id; | ||
| 908 | gchar *msg; | ||
| 909 | FolderItem *item; | ||
| 910 | |||
| 911 | g_return_val_if_fail(identifier != NULL, NULL); | ||
| 912 | |||
| 913 | id = g_path_get_dirname(identifier); | ||
| 914 | msg = g_path_get_basename(identifier); | ||
| 915 | item = folder_find_item_from_identifier(id); | ||
| 916 | *num = to_number(msg); | ||
| 917 | g_free(msg); | ||
| 918 | g_free(id); | ||
| 919 | |||
| 920 | return item; | ||
| 921 | } | ||
| 922 | |||
| 923 | Folder *folder_get_default_folder(void) | ||
| 924 | { | ||
| 925 | return folder_list ? FOLDER(folder_list->data) : NULL; | ||
| 926 | } | ||
| 927 | |||
| 928 | FolderItem *folder_get_default_inbox(void) | ||
| 929 | { | ||
| 930 | Folder *folder; | ||
| 931 | |||
| 932 | if (!folder_list) return NULL; | ||
| 933 | folder = FOLDER(folder_list->data); | ||
| 934 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 935 | return folder->inbox; | ||
| 936 | } | ||
| 937 | |||
| 938 | FolderItem *folder_get_default_outbox(void) | ||
| 939 | { | ||
| 940 | Folder *folder; | ||
| 941 | |||
| 942 | if (!folder_list) return NULL; | ||
| 943 | folder = FOLDER(folder_list->data); | ||
| 944 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 945 | return folder->outbox; | ||
| 946 | } | ||
| 947 | |||
| 948 | FolderItem *folder_get_default_draft(void) | ||
| 949 | { | ||
| 950 | Folder *folder; | ||
| 951 | |||
| 952 | if (!folder_list) return NULL; | ||
| 953 | folder = FOLDER(folder_list->data); | ||
| 954 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 955 | return folder->draft; | ||
| 956 | } | ||
| 957 | |||
| 958 | FolderItem *folder_get_default_queue(void) | ||
| 959 | { | ||
| 960 | Folder *folder; | ||
| 961 | |||
| 962 | if (!folder_list) return NULL; | ||
| 963 | folder = FOLDER(folder_list->data); | ||
| 964 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 965 | return folder->queue; | ||
| 966 | } | ||
| 967 | |||
| 968 | FolderItem *folder_get_default_trash(void) | ||
| 969 | { | ||
| 970 | Folder *folder; | ||
| 971 | |||
| 972 | if (!folder_list) return NULL; | ||
| 973 | folder = FOLDER(folder_list->data); | ||
| 974 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 975 | return folder->trash; | ||
| 976 | } | ||
| 977 | |||
| 978 | FolderItem *folder_get_default_junk(void) | ||
| 979 | { | ||
| 980 | FolderPrivData *priv; | ||
| 981 | |||
| 982 | if (!folder_list) return NULL; | ||
| 983 | if (!folder_priv_list) return NULL; | ||
| 984 | priv = (FolderPrivData *)folder_priv_list->data; | ||
| 985 | g_return_val_if_fail(priv != NULL, NULL); | ||
| 986 | g_return_val_if_fail(priv->folder != NULL, NULL); | ||
| 987 | return priv->junk; | ||
| 988 | } | ||
| 989 | |||
| 990 | static FolderPrivData *folder_get_priv(Folder *folder) | ||
| 991 | { | ||
| 992 | FolderPrivData *priv; | ||
| 993 | GList *cur; | ||
| 994 | |||
| 995 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 996 | |||
| 997 | for (cur = folder_priv_list; cur != NULL; cur = cur->next) { | ||
| 998 | priv = (FolderPrivData *)cur->data; | ||
| 999 | if (priv->folder == folder) | ||
| 1000 | return priv; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | g_warning("folder_get_priv: private data for Folder (%p) not found.", | ||
| 1004 | folder); | ||
| 1005 | |||
| 1006 | return NULL; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | FolderItem *folder_get_junk(Folder *folder) | ||
| 1010 | { | ||
| 1011 | FolderPrivData *priv; | ||
| 1012 | |||
| 1013 | priv = folder_get_priv(folder); | ||
| 1014 | if (priv) | ||
| 1015 | return priv->junk; | ||
| 1016 | |||
| 1017 | return NULL; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | void folder_set_junk(Folder *folder, FolderItem *item) | ||
| 1021 | { | ||
| 1022 | FolderPrivData *priv; | ||
| 1023 | |||
| 1024 | priv = folder_get_priv(folder); | ||
| 1025 | if (priv) | ||
| 1026 | priv->junk = item; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | gboolean folder_item_is_trash(FolderItem *item) | ||
| 1030 | { | ||
| 1031 | PrefsAccount *ac; | ||
| 1032 | FolderItem *trash; | ||
| 1033 | |||
| 1034 | g_return_val_if_fail(item != NULL, FALSE); | ||
| 1035 | |||
| 1036 | if (item->stype == F_TRASH) | ||
| 1037 | return TRUE; | ||
| 1038 | |||
| 1039 | ac = account_find_from_item_property(item); | ||
| 1040 | if (ac && ac->set_trash_folder && ac->trash_folder) { | ||
| 1041 | trash = folder_find_item_from_identifier(ac->trash_folder); | ||
| 1042 | if (trash == item) | ||
| 1043 | return TRUE; | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | return FALSE; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \ | ||
| 1050 | { \ | ||
| 1051 | if (!folder->member) { \ | ||
| 1052 | item = folder_item_new(dir, dir); \ | ||
| 1053 | item->stype = type; \ | ||
| 1054 | folder_item_append(rootitem, item); \ | ||
| 1055 | folder->member = item; \ | ||
| 1056 | } \ | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | void folder_set_missing_folders(void) | ||
| 1060 | { | ||
| 1061 | Folder *folder; | ||
| 1062 | FolderItem *rootitem; | ||
| 1063 | FolderItem *item; | ||
| 1064 | GList *list; | ||
| 1065 | |||
| 1066 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 1067 | folder = list->data; | ||
| 1068 | if (FOLDER_TYPE(folder) != F_MH) continue; | ||
| 1069 | rootitem = FOLDER_ITEM(folder->node->data); | ||
| 1070 | g_return_if_fail(rootitem != NULL); | ||
| 1071 | |||
| 1072 | if (folder->inbox && folder->outbox && folder->draft && | ||
| 1073 | folder->queue && folder->trash && folder_get_junk(folder)) | ||
| 1074 | continue; | ||
| 1075 | |||
| 1076 | if (folder_create_tree(folder) < 0) { | ||
| 1077 | g_warning("%s: can't create the folder tree.\n", | ||
| 1078 | LOCAL_FOLDER(folder)->rootpath); | ||
| 1079 | continue; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX); | ||
| 1083 | CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX); | ||
| 1084 | CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT); | ||
| 1085 | CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE); | ||
| 1086 | CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH); | ||
| 1087 | |||
| 1088 | if (!folder_get_junk(folder)) { | ||
| 1089 | item = folder_item_new(JUNK_DIR, JUNK_DIR); | ||
| 1090 | item->stype = F_JUNK; | ||
| 1091 | folder_item_append(rootitem, item); | ||
| 1092 | folder_set_junk(folder, item); | ||
| 1093 | } | ||
| 1094 | } | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | static gboolean folder_unref_account_func(GNode *node, gpointer data) | ||
| 1098 | { | ||
| 1099 | FolderItem *item = node->data; | ||
| 1100 | PrefsAccount *account = data; | ||
| 1101 | |||
| 1102 | if (item->account == account) | ||
| 1103 | item->account = NULL; | ||
| 1104 | |||
| 1105 | return FALSE; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | void folder_unref_account_all(PrefsAccount *account) | ||
| 1109 | { | ||
| 1110 | Folder *folder; | ||
| 1111 | GList *list; | ||
| 1112 | |||
| 1113 | if (!account) return; | ||
| 1114 | |||
| 1115 | for (list = folder_list; list != NULL; list = list->next) { | ||
| 1116 | folder = list->data; | ||
| 1117 | if (folder->account == account) | ||
| 1118 | folder->account = NULL; | ||
| 1119 | g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1120 | folder_unref_account_func, account); | ||
| 1121 | } | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | #undef CREATE_FOLDER_IF_NOT_EXIST | ||
| 1125 | |||
| 1126 | gchar *folder_get_path(Folder *folder) | ||
| 1127 | { | ||
| 1128 | gchar *path; | ||
| 1129 | |||
| 1130 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 1131 | |||
| 1132 | if (FOLDER_TYPE(folder) == F_MH) { | ||
| 1133 | path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath, | ||
| 1134 | -1, NULL, NULL, NULL); | ||
| 1135 | if (!path) { | ||
| 1136 | g_warning("folder_get_path: failed to convert character set\n"); | ||
| 1137 | path = g_strdup(LOCAL_FOLDER(folder)->rootpath); | ||
| 1138 | } | ||
| 1139 | if (!g_path_is_absolute(path)) { | ||
| 1140 | gchar *path_; | ||
| 1141 | |||
| 1142 | path_ = g_strconcat(get_mail_base_dir(), | ||
| 1143 | G_DIR_SEPARATOR_S, path, NULL); | ||
| 1144 | g_free(path); | ||
| 1145 | path = path_; | ||
| 1146 | } | ||
| 1147 | } else if (FOLDER_TYPE(folder) == F_IMAP) { | ||
| 1148 | gchar *server; | ||
| 1149 | gchar *uid; | ||
| 1150 | |||
| 1151 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 1152 | server = uriencode_for_filename(folder->account->recv_server); | ||
| 1153 | uid = uriencode_for_filename(folder->account->userid); | ||
| 1154 | path = g_strconcat(get_imap_cache_dir(), | ||
| 1155 | G_DIR_SEPARATOR_S, server, | ||
| 1156 | G_DIR_SEPARATOR_S, uid, NULL); | ||
| 1157 | g_free(uid); | ||
| 1158 | g_free(server); | ||
| 1159 | } else if (FOLDER_TYPE(folder) == F_NEWS) { | ||
| 1160 | gchar *server; | ||
| 1161 | |||
| 1162 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 1163 | server = uriencode_for_filename(folder->account->nntp_server); | ||
| 1164 | path = g_strconcat(get_news_cache_dir(), | ||
| 1165 | G_DIR_SEPARATOR_S, server, NULL); | ||
| 1166 | g_free(server); | ||
| 1167 | } else | ||
| 1168 | path = NULL; | ||
| 1169 | |||
| 1170 | return path; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | gchar *folder_item_get_path(FolderItem *item) | ||
| 1174 | { | ||
| 1175 | gchar *folder_path; | ||
| 1176 | gchar *item_path = NULL, *path; | ||
| 1177 | |||
| 1178 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1179 | |||
| 1180 | folder_path = folder_get_path(item->folder); | ||
| 1181 | g_return_val_if_fail(folder_path != NULL, NULL); | ||
| 1182 | |||
| 1183 | if (item->path) { | ||
| 1184 | item_path = g_filename_from_utf8(item->path, -1, | ||
| 1185 | NULL, NULL, NULL); | ||
| 1186 | if (!item_path) { | ||
| 1187 | g_warning("folder_item_get_path: failed to convert character set\n"); | ||
| 1188 | item_path = g_strdup(item->path); | ||
| 1189 | } | ||
| 1190 | #ifdef G_OS_WIN32 | ||
| 1191 | subst_char(item_path, '/', G_DIR_SEPARATOR); | ||
| 1192 | #endif | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | if (item_path) | ||
| 1196 | path = g_strconcat(folder_path, G_DIR_SEPARATOR_S, item_path, | ||
| 1197 | NULL); | ||
| 1198 | else | ||
| 1199 | path = g_strdup(folder_path); | ||
| 1200 | |||
| 1201 | g_free(item_path); | ||
| 1202 | g_free(folder_path); | ||
| 1203 | return path; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | gint folder_item_scan(FolderItem *item) | ||
| 1207 | { | ||
| 1208 | Folder *folder; | ||
| 1209 | |||
| 1210 | g_return_val_if_fail(item != NULL, -1); | ||
| 1211 | |||
| 1212 | folder = item->folder; | ||
| 1213 | return folder->klass->scan(folder, item); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | static void folder_item_scan_foreach_func(gpointer key, gpointer val, | ||
| 1217 | gpointer data) | ||
| 1218 | { | ||
| 1219 | folder_item_scan(FOLDER_ITEM(key)); | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | void folder_item_scan_foreach(GHashTable *table) | ||
| 1223 | { | ||
| 1224 | g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache) | ||
| 1228 | { | ||
| 1229 | Folder *folder; | ||
| 1230 | |||
| 1231 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1232 | |||
| 1233 | folder = item->folder; | ||
| 1234 | |||
| 1235 | if (item->stype == F_VIRTUAL) | ||
| 1236 | return virtual_get_class()->get_msg_list(folder, item, | ||
| 1237 | use_cache); | ||
| 1238 | |||
| 1239 | return folder->klass->get_msg_list(folder, item, use_cache); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | GSList *folder_item_get_uncached_msg_list(FolderItem *item) | ||
| 1243 | { | ||
| 1244 | Folder *folder; | ||
| 1245 | |||
| 1246 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1247 | g_return_val_if_fail(item->folder->klass->get_uncached_msg_list != NULL, | ||
| 1248 | NULL); | ||
| 1249 | |||
| 1250 | folder = item->folder; | ||
| 1251 | |||
| 1252 | if (item->stype == F_VIRTUAL) | ||
| 1253 | return NULL; | ||
| 1254 | |||
| 1255 | return folder->klass->get_uncached_msg_list(folder, item); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | gchar *folder_item_fetch_msg(FolderItem *item, gint num) | ||
| 1259 | { | ||
| 1260 | Folder *folder; | ||
| 1261 | |||
| 1262 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1263 | |||
| 1264 | folder = item->folder; | ||
| 1265 | |||
| 1266 | return folder->klass->fetch_msg(folder, item, num); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | gint folder_item_fetch_all_msg(FolderItem *item) | ||
| 1270 | { | ||
| 1271 | Folder *folder; | ||
| 1272 | GSList *mlist; | ||
| 1273 | GSList *cur; | ||
| 1274 | gint num = 0; | ||
| 1275 | gint ret = 0; | ||
| 1276 | |||
| 1277 | g_return_val_if_fail(item != NULL, -1); | ||
| 1278 | |||
| 1279 | debug_print("fetching all messages in %s ...\n", item->path); | ||
| 1280 | |||
| 1281 | folder = item->folder; | ||
| 1282 | |||
| 1283 | if (folder->ui_func) | ||
| 1284 | folder->ui_func(folder, item, folder->ui_func_data ? | ||
| 1285 | folder->ui_func_data : GINT_TO_POINTER(num)); | ||
| 1286 | |||
| 1287 | mlist = folder_item_get_msg_list(item, TRUE); | ||
| 1288 | |||
| 1289 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1290 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 1291 | gchar *msg; | ||
| 1292 | |||
| 1293 | num++; | ||
| 1294 | if (folder->ui_func) | ||
| 1295 | folder->ui_func(folder, item, | ||
| 1296 | folder->ui_func_data ? | ||
| 1297 | folder->ui_func_data : | ||
| 1298 | GINT_TO_POINTER(num)); | ||
| 1299 | |||
| 1300 | msg = folder_item_fetch_msg(item, msginfo->msgnum); | ||
| 1301 | if (!msg) { | ||
| 1302 | g_warning("Can't fetch message %d. Aborting.\n", | ||
| 1303 | msginfo->msgnum); | ||
| 1304 | ret = -1; | ||
| 1305 | break; | ||
| 1306 | } | ||
| 1307 | g_free(msg); | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | procmsg_msg_list_free(mlist); | ||
| 1311 | |||
| 1312 | return ret; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num) | ||
| 1316 | { | ||
| 1317 | Folder *folder; | ||
| 1318 | |||
| 1319 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1320 | |||
| 1321 | folder = item->folder; | ||
| 1322 | |||
| 1323 | return folder->klass->get_msginfo(folder, item, num); | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags, | ||
| 1327 | gboolean remove_source) | ||
| 1328 | { | ||
| 1329 | Folder *folder; | ||
| 1330 | |||
| 1331 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1332 | g_return_val_if_fail(file != NULL, -1); | ||
| 1333 | g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1); | ||
| 1334 | |||
| 1335 | folder = dest->folder; | ||
| 1336 | |||
| 1337 | return folder->klass->add_msg(folder, dest, file, flags, remove_source); | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | gint folder_item_add_msgs(FolderItem *dest, GSList *file_list, | ||
| 1341 | gboolean remove_source, gint *first) | ||
| 1342 | { | ||
| 1343 | Folder *folder; | ||
| 1344 | |||
| 1345 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1346 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 1347 | g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1); | ||
| 1348 | |||
| 1349 | folder = dest->folder; | ||
| 1350 | |||
| 1351 | return folder->klass->add_msgs(folder, dest, file_list, remove_source, | ||
| 1352 | first); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | gint folder_item_add_msg_msginfo(FolderItem *dest, MsgInfo *msginfo, | ||
| 1356 | gboolean remove_source) | ||
| 1357 | { | ||
| 1358 | Folder *folder; | ||
| 1359 | |||
| 1360 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1361 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1362 | g_return_val_if_fail(msginfo->file_path != NULL, -1); | ||
| 1363 | g_return_val_if_fail(dest->folder->klass->add_msg_msginfo != NULL, -1); | ||
| 1364 | |||
| 1365 | folder = dest->folder; | ||
| 1366 | |||
| 1367 | return folder->klass->add_msg_msginfo(folder, dest, msginfo, | ||
| 1368 | remove_source); | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | gint folder_item_add_msgs_msginfo(FolderItem *dest, GSList *msglist, | ||
| 1372 | gboolean remove_source, gint *first) | ||
| 1373 | { | ||
| 1374 | Folder *folder; | ||
| 1375 | |||
| 1376 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1377 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1378 | g_return_val_if_fail(dest->folder->klass->add_msgs_msginfo != NULL, -1); | ||
| 1379 | |||
| 1380 | folder = dest->folder; | ||
| 1381 | |||
| 1382 | return folder->klass->add_msgs_msginfo(folder, dest, msglist, | ||
| 1383 | remove_source, first); | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | #define IS_FROM_QUEUE(m, d) \ | ||
| 1387 | (m->folder && m->folder->stype == F_QUEUE && \ | ||
| 1388 | MSG_IS_QUEUED(m->flags) && d->stype != F_QUEUE) | ||
| 1389 | |||
| 1390 | gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo) | ||
| 1391 | { | ||
| 1392 | Folder *folder; | ||
| 1393 | |||
| 1394 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1395 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1396 | g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1); | ||
| 1397 | |||
| 1398 | folder = dest->folder; | ||
| 1399 | |||
| 1400 | if (IS_FROM_QUEUE(msginfo, dest)) { | ||
| 1401 | GSList msglist; | ||
| 1402 | |||
| 1403 | msglist.data = msginfo; | ||
| 1404 | msglist.next = NULL; | ||
| 1405 | return procmsg_add_messages_from_queue(dest, &msglist, TRUE); | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | return folder->klass->move_msg(folder, dest, msginfo); | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | gint folder_item_move_msgs(FolderItem *dest, GSList *msglist) | ||
| 1412 | { | ||
| 1413 | Folder *folder; | ||
| 1414 | MsgInfo *msginfo; | ||
| 1415 | |||
| 1416 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1417 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1418 | g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1); | ||
| 1419 | |||
| 1420 | folder = dest->folder; | ||
| 1421 | |||
| 1422 | msginfo = (MsgInfo *)msglist->data; | ||
| 1423 | if (IS_FROM_QUEUE(msginfo, dest)) | ||
| 1424 | return procmsg_add_messages_from_queue(dest, msglist, TRUE); | ||
| 1425 | |||
| 1426 | return folder->klass->move_msgs(folder, dest, msglist); | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo) | ||
| 1430 | { | ||
| 1431 | Folder *folder; | ||
| 1432 | |||
| 1433 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1434 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1435 | g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1); | ||
| 1436 | |||
| 1437 | folder = dest->folder; | ||
| 1438 | |||
| 1439 | if (IS_FROM_QUEUE(msginfo, dest)) { | ||
| 1440 | GSList msglist; | ||
| 1441 | |||
| 1442 | msglist.data = msginfo; | ||
| 1443 | msglist.next = NULL; | ||
| 1444 | return procmsg_add_messages_from_queue(dest, &msglist, FALSE); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | return folder->klass->copy_msg(folder, dest, msginfo); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist) | ||
| 1451 | { | ||
| 1452 | Folder *folder; | ||
| 1453 | MsgInfo *msginfo; | ||
| 1454 | |||
| 1455 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1456 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1457 | g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1); | ||
| 1458 | |||
| 1459 | folder = dest->folder; | ||
| 1460 | |||
| 1461 | msginfo = (MsgInfo *)msglist->data; | ||
| 1462 | if (IS_FROM_QUEUE(msginfo, dest)) | ||
| 1463 | return procmsg_add_messages_from_queue(dest, msglist, FALSE); | ||
| 1464 | |||
| 1465 | return folder->klass->copy_msgs(folder, dest, msglist); | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | #undef IS_FROM_QUEUE | ||
| 1469 | |||
| 1470 | gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo) | ||
| 1471 | { | ||
| 1472 | Folder *folder; | ||
| 1473 | |||
| 1474 | g_return_val_if_fail(item != NULL, -1); | ||
| 1475 | g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1); | ||
| 1476 | |||
| 1477 | folder = item->folder; | ||
| 1478 | |||
| 1479 | return folder->klass->remove_msg(folder, item, msginfo); | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | gint folder_item_remove_msgs(FolderItem *item, GSList *msglist) | ||
| 1483 | { | ||
| 1484 | Folder *folder; | ||
| 1485 | gint ret = 0; | ||
| 1486 | |||
| 1487 | g_return_val_if_fail(item != NULL, -1); | ||
| 1488 | |||
| 1489 | folder = item->folder; | ||
| 1490 | if (folder->klass->remove_msgs) { | ||
| 1491 | return folder->klass->remove_msgs(folder, item, msglist); | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | while (msglist != NULL) { | ||
| 1495 | MsgInfo *msginfo = (MsgInfo *)msglist->data; | ||
| 1496 | |||
| 1497 | ret = folder_item_remove_msg(item, msginfo); | ||
| 1498 | if (ret != 0) break; | ||
| 1499 | msglist = msglist->next; | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | return ret; | ||
| 1503 | } | ||
| 1504 | |||
| 1505 | gint folder_item_remove_all_msg(FolderItem *item) | ||
| 1506 | { | ||
| 1507 | Folder *folder; | ||
| 1508 | |||
| 1509 | g_return_val_if_fail(item != NULL, -1); | ||
| 1510 | g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1); | ||
| 1511 | |||
| 1512 | folder = item->folder; | ||
| 1513 | |||
| 1514 | return folder->klass->remove_all_msg(folder, item); | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo) | ||
| 1518 | { | ||
| 1519 | Folder *folder; | ||
| 1520 | |||
| 1521 | g_return_val_if_fail(item != NULL, FALSE); | ||
| 1522 | g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL, | ||
| 1523 | FALSE); | ||
| 1524 | |||
| 1525 | folder = item->folder; | ||
| 1526 | return folder->klass->is_msg_changed(folder, item, msginfo); | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | gint folder_item_close(FolderItem *item) | ||
| 1530 | { | ||
| 1531 | Folder *folder; | ||
| 1532 | |||
| 1533 | g_return_val_if_fail(item != NULL, -1); | ||
| 1534 | |||
| 1535 | item->opened = FALSE; | ||
| 1536 | folder = item->folder; | ||
| 1537 | return folder->klass->close(folder, item); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | gchar *folder_item_get_cache_file(FolderItem *item) | ||
| 1541 | { | ||
| 1542 | gchar *path; | ||
| 1543 | gchar *file; | ||
| 1544 | |||
| 1545 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1546 | g_return_val_if_fail(item->path != NULL, NULL); | ||
| 1547 | |||
| 1548 | path = folder_item_get_path(item); | ||
| 1549 | g_return_val_if_fail(path != NULL, NULL); | ||
| 1550 | if (!is_dir_exist(path)) | ||
| 1551 | make_dir_hier(path); | ||
| 1552 | file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL); | ||
| 1553 | g_free(path); | ||
| 1554 | |||
| 1555 | return file; | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | gchar *folder_item_get_mark_file(FolderItem *item) | ||
| 1559 | { | ||
| 1560 | gchar *path; | ||
| 1561 | gchar *file; | ||
| 1562 | |||
| 1563 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1564 | g_return_val_if_fail(item->path != NULL, NULL); | ||
| 1565 | |||
| 1566 | path = folder_item_get_path(item); | ||
| 1567 | g_return_val_if_fail(path != NULL, NULL); | ||
| 1568 | if (!is_dir_exist(path)) | ||
| 1569 | make_dir_hier(path); | ||
| 1570 | file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL); | ||
| 1571 | g_free(path); | ||
| 1572 | |||
| 1573 | return file; | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | static gboolean folder_build_tree(GNode *node, gpointer data) | ||
| 1577 | { | ||
| 1578 | Folder *folder = FOLDER(data); | ||
| 1579 | FolderItem *item; | ||
| 1580 | XMLNode *xmlnode; | ||
| 1581 | GList *list; | ||
| 1582 | SpecialFolderItemType stype = F_NORMAL; | ||
| 1583 | const gchar *name = NULL; | ||
| 1584 | const gchar *path = NULL; | ||
| 1585 | PrefsAccount *account = NULL; | ||
| 1586 | gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE, | ||
| 1587 | threaded = TRUE, ac_apply_sub = FALSE; | ||
| 1588 | FolderSortKey sort_key = SORT_BY_NONE; | ||
| 1589 | FolderSortType sort_type = SORT_ASCENDING; | ||
| 1590 | gboolean qsearch_cond_type = 0; | ||
| 1591 | gint new = 0, unread = 0, total = 0; | ||
| 1592 | time_t mtime = 0; | ||
| 1593 | gboolean use_auto_to_on_reply = FALSE; | ||
| 1594 | gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL, | ||
| 1595 | *auto_replyto = NULL; | ||
| 1596 | gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE; | ||
| 1597 | |||
| 1598 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 1599 | if (!node->parent) return FALSE; | ||
| 1600 | |||
| 1601 | xmlnode = node->data; | ||
| 1602 | if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) { | ||
| 1603 | g_warning("tag name != \"folderitem\"\n"); | ||
| 1604 | return FALSE; | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | list = xmlnode->tag->attr; | ||
| 1608 | for (; list != NULL; list = list->next) { | ||
| 1609 | XMLAttr *attr = list->data; | ||
| 1610 | |||
| 1611 | if (!attr || !attr->name || !attr->value) continue; | ||
| 1612 | if (!strcmp(attr->name, "type")) { | ||
| 1613 | if (!g_ascii_strcasecmp(attr->value, "normal")) | ||
| 1614 | stype = F_NORMAL; | ||
| 1615 | else if (!g_ascii_strcasecmp(attr->value, "inbox")) | ||
| 1616 | stype = F_INBOX; | ||
| 1617 | else if (!g_ascii_strcasecmp(attr->value, "outbox")) | ||
| 1618 | stype = F_OUTBOX; | ||
| 1619 | else if (!g_ascii_strcasecmp(attr->value, "draft")) | ||
| 1620 | stype = F_DRAFT; | ||
| 1621 | else if (!g_ascii_strcasecmp(attr->value, "queue")) | ||
| 1622 | stype = F_QUEUE; | ||
| 1623 | else if (!g_ascii_strcasecmp(attr->value, "trash")) | ||
| 1624 | stype = F_TRASH; | ||
| 1625 | else if (!g_ascii_strcasecmp(attr->value, "junk")) | ||
| 1626 | stype = F_JUNK; | ||
| 1627 | else if (!g_ascii_strcasecmp(attr->value, "virtual")) | ||
| 1628 | stype = F_VIRTUAL; | ||
| 1629 | } else if (!strcmp(attr->name, "name")) | ||
| 1630 | name = attr->value; | ||
| 1631 | else if (!strcmp(attr->name, "path")) { | ||
| 1632 | #ifdef G_OS_WIN32 | ||
| 1633 | subst_char(attr->value, G_DIR_SEPARATOR, '/'); | ||
| 1634 | #endif | ||
| 1635 | path = attr->value; | ||
| 1636 | } else if (!strcmp(attr->name, "mtime")) | ||
| 1637 | mtime = strtoll(attr->value, NULL, 10); | ||
| 1638 | else if (!strcmp(attr->name, "new")) | ||
| 1639 | new = atoi(attr->value); | ||
| 1640 | else if (!strcmp(attr->name, "unread")) | ||
| 1641 | unread = atoi(attr->value); | ||
| 1642 | else if (!strcmp(attr->name, "total")) | ||
| 1643 | total = atoi(attr->value); | ||
| 1644 | else if (!strcmp(attr->name, "no_sub")) | ||
| 1645 | no_sub = *attr->value == '1' ? TRUE : FALSE; | ||
| 1646 | else if (!strcmp(attr->name, "no_select")) | ||
| 1647 | no_select = *attr->value == '1' ? TRUE : FALSE; | ||
| 1648 | else if (!strcmp(attr->name, "collapsed")) | ||
| 1649 | collapsed = *attr->value == '1' ? TRUE : FALSE; | ||
| 1650 | else if (!strcmp(attr->name, "threaded")) | ||
| 1651 | threaded = *attr->value == '1' ? TRUE : FALSE; | ||
| 1652 | else if (!strcmp(attr->name, "sort_key")) { | ||
| 1653 | if (!strcmp(attr->value, "none")) | ||
| 1654 | sort_key = SORT_BY_NONE; | ||
| 1655 | else if (!strcmp(attr->value, "number")) | ||
| 1656 | sort_key = SORT_BY_NUMBER; | ||
| 1657 | else if (!strcmp(attr->value, "size")) | ||
| 1658 | sort_key = SORT_BY_SIZE; | ||
| 1659 | else if (!strcmp(attr->value, "date")) | ||
| 1660 | sort_key = SORT_BY_DATE; | ||
| 1661 | else if (!strcmp(attr->value, "thread-date")) | ||
| 1662 | sort_key = SORT_BY_TDATE; | ||
| 1663 | else if (!strcmp(attr->value, "from")) | ||
| 1664 | sort_key = SORT_BY_FROM; | ||
| 1665 | else if (!strcmp(attr->value, "subject")) | ||
| 1666 | sort_key = SORT_BY_SUBJECT; | ||
| 1667 | else if (!strcmp(attr->value, "score")) | ||
| 1668 | sort_key = SORT_BY_SCORE; | ||
| 1669 | else if (!strcmp(attr->value, "label")) | ||
| 1670 | sort_key = SORT_BY_LABEL; | ||
| 1671 | else if (!strcmp(attr->value, "mark")) | ||
| 1672 | sort_key = SORT_BY_MARK; | ||
| 1673 | else if (!strcmp(attr->value, "unread")) | ||
| 1674 | sort_key = SORT_BY_UNREAD; | ||
| 1675 | else if (!strcmp(attr->value, "mime")) | ||
| 1676 | sort_key = SORT_BY_MIME; | ||
| 1677 | else if (!strcmp(attr->value, "to")) | ||
| 1678 | sort_key = SORT_BY_TO; | ||
| 1679 | } else if (!strcmp(attr->name, "sort_type")) { | ||
| 1680 | if (!strcmp(attr->value, "ascending")) | ||
| 1681 | sort_type = SORT_ASCENDING; | ||
| 1682 | else | ||
| 1683 | sort_type = SORT_DESCENDING; | ||
| 1684 | } else if (!strcmp(attr->name, "qsearch_cond")) { | ||
| 1685 | if (!strcmp(attr->value, "all")) | ||
| 1686 | qsearch_cond_type = 0; | ||
| 1687 | else if (!strcmp(attr->value, "unread")) | ||
| 1688 | qsearch_cond_type = 1; | ||
| 1689 | else if (!strcmp(attr->value, "mark")) | ||
| 1690 | qsearch_cond_type = 2; | ||
| 1691 | else if (!strcmp(attr->value, "clabel")) | ||
| 1692 | qsearch_cond_type = 3; | ||
| 1693 | else if (!strcmp(attr->value, "mime")) | ||
| 1694 | qsearch_cond_type = 4; | ||
| 1695 | else if (!strcmp(attr->value, "w1day")) | ||
| 1696 | qsearch_cond_type = 5; | ||
| 1697 | else if (!strcmp(attr->value, "last5")) | ||
| 1698 | qsearch_cond_type = 6; | ||
| 1699 | else if (!strcmp(attr->value, "last7")) | ||
| 1700 | qsearch_cond_type = 7; | ||
| 1701 | else if (!strcmp(attr->value, "in-addressbook")) | ||
| 1702 | qsearch_cond_type = 8; | ||
| 1703 | else if (!strcmp(attr->value, "last30")) | ||
| 1704 | qsearch_cond_type = 9; | ||
| 1705 | } else if (!strcmp(attr->name, "account_id")) { | ||
| 1706 | account = account_find_from_id(atoi(attr->value)); | ||
| 1707 | if (!account) g_warning("account_id: %s not found\n", | ||
| 1708 | attr->value); | ||
| 1709 | } else if (!strcmp(attr->name, "account_apply_sub")) | ||
| 1710 | ac_apply_sub = *attr->value == '1' ? TRUE : FALSE; | ||
| 1711 | else if (!strcmp(attr->name, "to")) | ||
| 1712 | auto_to = g_strdup(attr->value); | ||
| 1713 | else if (!strcmp(attr->name, "use_auto_to_on_reply")) | ||
| 1714 | use_auto_to_on_reply = | ||
| 1715 | *attr->value == '1' ? TRUE : FALSE; | ||
| 1716 | else if (!strcmp(attr->name, "cc")) | ||
| 1717 | auto_cc = g_strdup(attr->value); | ||
| 1718 | else if (!strcmp(attr->name, "bcc")) | ||
| 1719 | auto_bcc = g_strdup(attr->value); | ||
| 1720 | else if (!strcmp(attr->name, "replyto")) | ||
| 1721 | auto_replyto = g_strdup(attr->value); | ||
| 1722 | else if (!strcmp(attr->name, "trim_summary_subject")) { | ||
| 1723 | trim_summary_subject = | ||
| 1724 | *attr->value == '1' ? TRUE : FALSE; | ||
| 1725 | } else if (!strcmp(attr->name, "trim_compose_subject")) { | ||
| 1726 | trim_compose_subject = | ||
| 1727 | *attr->value = '1' ? TRUE : FALSE; | ||
| 1728 | } | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | item = folder_item_new(name, path); | ||
| 1732 | item->stype = stype; | ||
| 1733 | item->mtime = mtime; | ||
| 1734 | item->new = new; | ||
| 1735 | item->unread = unread; | ||
| 1736 | item->total = total; | ||
| 1737 | item->no_sub = no_sub; | ||
| 1738 | item->no_select = no_select; | ||
| 1739 | item->collapsed = collapsed; | ||
| 1740 | item->threaded = threaded; | ||
| 1741 | item->sort_key = sort_key; | ||
| 1742 | item->sort_type = sort_type; | ||
| 1743 | item->node = node; | ||
| 1744 | item->parent = FOLDER_ITEM(node->parent->data); | ||
| 1745 | item->folder = folder; | ||
| 1746 | switch (stype) { | ||
| 1747 | case F_INBOX: folder->inbox = item; break; | ||
| 1748 | case F_OUTBOX: folder->outbox = item; break; | ||
| 1749 | case F_DRAFT: folder->draft = item; break; | ||
| 1750 | case F_QUEUE: folder->queue = item; break; | ||
| 1751 | case F_TRASH: folder->trash = item; break; | ||
| 1752 | case F_JUNK: folder_set_junk(folder, item); break; | ||
| 1753 | default: break; | ||
| 1754 | } | ||
| 1755 | item->account = account; | ||
| 1756 | item->ac_apply_sub = ac_apply_sub; | ||
| 1757 | item->auto_to = auto_to; | ||
| 1758 | item->use_auto_to_on_reply = use_auto_to_on_reply; | ||
| 1759 | item->auto_cc = auto_cc; | ||
| 1760 | item->auto_bcc = auto_bcc; | ||
| 1761 | item->auto_replyto = auto_replyto; | ||
| 1762 | item->trim_summary_subject = trim_summary_subject; | ||
| 1763 | item->trim_compose_subject = trim_compose_subject; | ||
| 1764 | item->qsearch_cond_type = qsearch_cond_type; | ||
| 1765 | node->data = item; | ||
| 1766 | xml_free_node(xmlnode); | ||
| 1767 | |||
| 1768 | return FALSE; | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | static gboolean folder_read_folder_func(GNode *node, gpointer data) | ||
| 1772 | { | ||
| 1773 | Folder *folder; | ||
| 1774 | FolderItem *item; | ||
| 1775 | XMLNode *xmlnode; | ||
| 1776 | GList *list; | ||
| 1777 | FolderType type = F_UNKNOWN; | ||
| 1778 | const gchar *name = NULL; | ||
| 1779 | const gchar *path = NULL; | ||
| 1780 | PrefsAccount *account = NULL; | ||
| 1781 | gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE; | ||
| 1782 | |||
| 1783 | if (g_node_depth(node) != 2) return FALSE; | ||
| 1784 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 1785 | |||
| 1786 | xmlnode = node->data; | ||
| 1787 | if (strcmp2(xmlnode->tag->tag, "folder") != 0) { | ||
| 1788 | g_warning("tag name != \"folder\"\n"); | ||
| 1789 | return TRUE; | ||
| 1790 | } | ||
| 1791 | g_node_unlink(node); | ||
| 1792 | list = xmlnode->tag->attr; | ||
| 1793 | for (; list != NULL; list = list->next) { | ||
| 1794 | XMLAttr *attr = list->data; | ||
| 1795 | |||
| 1796 | if (!attr || !attr->name || !attr->value) continue; | ||
| 1797 | if (!strcmp(attr->name, "type")) { | ||
| 1798 | if (!g_ascii_strcasecmp(attr->value, "mh")) | ||
| 1799 | type = F_MH; | ||
| 1800 | else if (!g_ascii_strcasecmp(attr->value, "mbox")) | ||
| 1801 | type = F_MBOX; | ||
| 1802 | else if (!g_ascii_strcasecmp(attr->value, "maildir")) | ||
| 1803 | type = F_MAILDIR; | ||
| 1804 | else if (!g_ascii_strcasecmp(attr->value, "imap")) | ||
| 1805 | type = F_IMAP; | ||
| 1806 | else if (!g_ascii_strcasecmp(attr->value, "news")) | ||
| 1807 | type = F_NEWS; | ||
| 1808 | } else if (!strcmp(attr->name, "name")) | ||
| 1809 | name = attr->value; | ||
| 1810 | else if (!strcmp(attr->name, "path")) | ||
| 1811 | path = attr->value; | ||
| 1812 | else if (!strcmp(attr->name, "collapsed")) | ||
| 1813 | collapsed = *attr->value == '1' ? TRUE : FALSE; | ||
| 1814 | else if (!strcmp(attr->name, "threaded")) | ||
| 1815 | threaded = *attr->value == '1' ? TRUE : FALSE; | ||
| 1816 | else if (!strcmp(attr->name, "account_id")) { | ||
| 1817 | account = account_find_from_id(atoi(attr->value)); | ||
| 1818 | if (!account) g_warning("account_id: %s not found\n", | ||
| 1819 | attr->value); | ||
| 1820 | } else if (!strcmp(attr->name, "account_apply_sub")) | ||
| 1821 | ac_apply_sub = *attr->value == '1' ? TRUE : FALSE; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | folder = folder_new(type, name, path); | ||
| 1825 | g_return_val_if_fail(folder != NULL, FALSE); | ||
| 1826 | if (account && FOLDER_IS_REMOTE(folder)) { | ||
| 1827 | folder->account = account; | ||
| 1828 | account->folder = REMOTE_FOLDER(folder); | ||
| 1829 | } | ||
| 1830 | if (account && FOLDER_IS_LOCAL(folder)) | ||
| 1831 | ac_apply_sub = TRUE; | ||
| 1832 | item = FOLDER_ITEM(folder->node->data); | ||
| 1833 | node->data = item; | ||
| 1834 | item->node = node; | ||
| 1835 | g_node_destroy(folder->node); | ||
| 1836 | folder->node = node; | ||
| 1837 | folder_add(folder); | ||
| 1838 | item->collapsed = collapsed; | ||
| 1839 | item->threaded = threaded; | ||
| 1840 | item->account = account; | ||
| 1841 | item->ac_apply_sub = ac_apply_sub; | ||
| 1842 | |||
| 1843 | g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1844 | folder_build_tree, folder); | ||
| 1845 | |||
| 1846 | return FALSE; | ||
| 1847 | } | ||
| 1848 | |||
| 1849 | static gchar *folder_get_list_path(void) | ||
| 1850 | { | ||
| 1851 | static gchar *filename = NULL; | ||
| 1852 | |||
| 1853 | if (!filename) | ||
| 1854 | filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 1855 | FOLDER_LIST, NULL); | ||
| 1856 | |||
| 1857 | return filename; | ||
| 1858 | } | ||
| 1859 | |||
| 1860 | #define PUT_ESCAPE_STR(fp, attr, str) \ | ||
| 1861 | { \ | ||
| 1862 | fputs(" " attr "=\"", fp); \ | ||
| 1863 | xml_file_put_escape_str(fp, str); \ | ||
| 1864 | fputs("\"", fp); \ | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | static void folder_write_list_recursive(GNode *node, gpointer data) | ||
| 1868 | { | ||
| 1869 | FILE *fp = (FILE *)data; | ||
| 1870 | FolderItem *item; | ||
| 1871 | gint i, depth; | ||
| 1872 | static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap", | ||
| 1873 | "news", "unknown"}; | ||
| 1874 | static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox", | ||
| 1875 | "draft", "queue", "trash", | ||
| 1876 | "junk", "virtual"}; | ||
| 1877 | static gchar *sort_key_str[] = {"none", "number", "size", "date", | ||
| 1878 | "thread-date", | ||
| 1879 | "from", "subject", "score", "label", | ||
| 1880 | "mark", "unread", "mime", "to"}; | ||
| 1881 | static gchar *qsearch_cond_str[] = {"all", "unread", "mark", "clabel", | ||
| 1882 | "mime", "w1day", "last5", "last7", | ||
| 1883 | "in-addressbook", "last30"}; | ||
| 1884 | |||
| 1885 | g_return_if_fail(node != NULL); | ||
| 1886 | g_return_if_fail(fp != NULL); | ||
| 1887 | |||
| 1888 | item = FOLDER_ITEM(node->data); | ||
| 1889 | g_return_if_fail(item != NULL); | ||
| 1890 | |||
| 1891 | depth = g_node_depth(node); | ||
| 1892 | for (i = 0; i < depth; i++) | ||
| 1893 | fputs(" ", fp); | ||
| 1894 | if (depth == 1) { | ||
| 1895 | Folder *folder = item->folder; | ||
| 1896 | |||
| 1897 | fprintf(fp, "<folder type=\"%s\"", | ||
| 1898 | folder_type_str[FOLDER_TYPE(folder)]); | ||
| 1899 | if (folder->name) | ||
| 1900 | PUT_ESCAPE_STR(fp, "name", folder->name); | ||
| 1901 | if (FOLDER_TYPE(folder) == F_MH) | ||
| 1902 | PUT_ESCAPE_STR(fp, "path", | ||
| 1903 | LOCAL_FOLDER(folder)->rootpath); | ||
| 1904 | if (item->collapsed && node->children) | ||
| 1905 | fputs(" collapsed=\"1\"", fp); | ||
| 1906 | if (folder->account) | ||
| 1907 | fprintf(fp, " account_id=\"%d\"", | ||
| 1908 | folder->account->account_id); | ||
| 1909 | else if (item->account) | ||
| 1910 | fprintf(fp, " account_id=\"%d\"", | ||
| 1911 | item->account->account_id); | ||
| 1912 | if (item->ac_apply_sub) | ||
| 1913 | fputs(" account_apply_sub=\"1\"", fp); | ||
| 1914 | } else { | ||
| 1915 | fprintf(fp, "<folderitem type=\"%s\"", | ||
| 1916 | folder_item_stype_str[item->stype]); | ||
| 1917 | if (item->name) | ||
| 1918 | PUT_ESCAPE_STR(fp, "name", item->name); | ||
| 1919 | if (item->path) | ||
| 1920 | PUT_ESCAPE_STR(fp, "path", item->path); | ||
| 1921 | |||
| 1922 | if (item->no_sub) | ||
| 1923 | fputs(" no_sub=\"1\"", fp); | ||
| 1924 | if (item->no_select) | ||
| 1925 | fputs(" no_select=\"1\"", fp); | ||
| 1926 | if (item->collapsed && node->children) | ||
| 1927 | fputs(" collapsed=\"1\"", fp); | ||
| 1928 | if (item->threaded) | ||
| 1929 | fputs(" threaded=\"1\"", fp); | ||
| 1930 | else | ||
| 1931 | fputs(" threaded=\"0\"", fp); | ||
| 1932 | |||
| 1933 | if (item->sort_key != SORT_BY_NONE) { | ||
| 1934 | fprintf(fp, " sort_key=\"%s\"", | ||
| 1935 | sort_key_str[item->sort_key]); | ||
| 1936 | if (item->sort_type == SORT_ASCENDING) | ||
| 1937 | fprintf(fp, " sort_type=\"ascending\""); | ||
| 1938 | else | ||
| 1939 | fprintf(fp, " sort_type=\"descending\""); | ||
| 1940 | } | ||
| 1941 | if (item->qsearch_cond_type > 0 && | ||
| 1942 | item->qsearch_cond_type < 10) { | ||
| 1943 | fprintf(fp, " qsearch_cond=\"%s\"", | ||
| 1944 | qsearch_cond_str[item->qsearch_cond_type]); | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | fprintf(fp, | ||
| 1948 | " mtime=\"%lld\" new=\"%d\" unread=\"%d\" total=\"%d\"", | ||
| 1949 | (gint64)item->mtime, item->new, item->unread, item->total); | ||
| 1950 | |||
| 1951 | if (item->account) | ||
| 1952 | fprintf(fp, " account_id=\"%d\"", | ||
| 1953 | item->account->account_id); | ||
| 1954 | if (item->ac_apply_sub) | ||
| 1955 | fputs(" account_apply_sub=\"1\"", fp); | ||
| 1956 | |||
| 1957 | if (item->auto_to) | ||
| 1958 | PUT_ESCAPE_STR(fp, "to", item->auto_to); | ||
| 1959 | if (item->use_auto_to_on_reply) | ||
| 1960 | fputs(" use_auto_to_on_reply=\"1\"", fp); | ||
| 1961 | if (item->auto_cc) | ||
| 1962 | PUT_ESCAPE_STR(fp, "cc", item->auto_cc); | ||
| 1963 | if (item->auto_bcc) | ||
| 1964 | PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc); | ||
| 1965 | if (item->auto_replyto) | ||
| 1966 | PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto); | ||
| 1967 | |||
| 1968 | if (item->trim_summary_subject) | ||
| 1969 | fputs(" trim_summary_subject=\"1\"", fp); | ||
| 1970 | if (item->trim_compose_subject) | ||
| 1971 | fputs(" trim_compose_subject=\"1\"", fp); | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | if (node->children) { | ||
| 1975 | GNode *child; | ||
| 1976 | fputs(">\n", fp); | ||
| 1977 | |||
| 1978 | child = node->children; | ||
| 1979 | while (child) { | ||
| 1980 | GNode *cur; | ||
| 1981 | |||
| 1982 | cur = child; | ||
| 1983 | child = cur->next; | ||
| 1984 | folder_write_list_recursive(cur, data); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | for (i = 0; i < depth; i++) | ||
| 1988 | fputs(" ", fp); | ||
| 1989 | fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem"); | ||
| 1990 | } else | ||
| 1991 | fputs(" />\n", fp); | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | #undef PUT_ESCAPE_STR | ||
diff --git a/libsylph/folder.h b/libsylph/folder.h new file mode 100644 index 0000000..ef9eda2 --- /dev/null +++ b/libsylph/folder.h | |||
| @@ -0,0 +1,475 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __FOLDER_H__ | ||
| 21 | #define __FOLDER_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <time.h> | ||
| 29 | |||
| 30 | typedef struct _Folder Folder; | ||
| 31 | typedef struct _FolderClass FolderClass; | ||
| 32 | |||
| 33 | typedef struct _LocalFolder LocalFolder; | ||
| 34 | typedef struct _RemoteFolder RemoteFolder; | ||
| 35 | #if 0 | ||
| 36 | typedef struct _MboxFolder MboxFolder; | ||
| 37 | typedef struct _MaildirFolder MaildirFolder; | ||
| 38 | #endif | ||
| 39 | |||
| 40 | typedef struct _FolderItem FolderItem; | ||
| 41 | |||
| 42 | #define FOLDER(obj) ((Folder *)obj) | ||
| 43 | #define FOLDER_CLASS(obj) (FOLDER(obj)->klass) | ||
| 44 | #define FOLDER_TYPE(obj) (FOLDER(obj)->klass->type) | ||
| 45 | |||
| 46 | #define LOCAL_FOLDER(obj) ((LocalFolder *)obj) | ||
| 47 | #define REMOTE_FOLDER(obj) ((RemoteFolder *)obj) | ||
| 48 | |||
| 49 | #define FOLDER_IS_LOCAL(obj) (FOLDER_TYPE(obj) == F_MH || \ | ||
| 50 | FOLDER_TYPE(obj) == F_MBOX || \ | ||
| 51 | FOLDER_TYPE(obj) == F_MAILDIR) | ||
| 52 | #define FOLDER_IS_REMOTE(obj) (FOLDER_TYPE(obj) == F_IMAP || \ | ||
| 53 | FOLDER_TYPE(obj) == F_NEWS) | ||
| 54 | |||
| 55 | #if 0 | ||
| 56 | #define MBOX_FOLDER(obj) ((MboxFolder *)obj) | ||
| 57 | #define MAILDIR_FOLDER(obj) ((MaildirFolder *)obj) | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #define FOLDER_ITEM(obj) ((FolderItem *)obj) | ||
| 61 | |||
| 62 | #define FOLDER_ITEM_CAN_ADD(obj) \ | ||
| 63 | ((obj) && FOLDER_ITEM(obj)->folder && \ | ||
| 64 | FOLDER_ITEM(obj)->path && \ | ||
| 65 | (FOLDER_IS_LOCAL(FOLDER_ITEM(obj)->folder) || \ | ||
| 66 | FOLDER_TYPE(FOLDER_ITEM(obj)->folder) == F_IMAP) && \ | ||
| 67 | !FOLDER_ITEM(obj)->no_select) | ||
| 68 | |||
| 69 | #define FOLDER_ITEM_IS_SENT_FOLDER(obj) ((obj) && \ | ||
| 70 | ((obj)->stype == F_OUTBOX || \ | ||
| 71 | (obj)->stype == F_DRAFT || \ | ||
| 72 | (obj)->stype == F_QUEUE)) | ||
| 73 | |||
| 74 | typedef enum | ||
| 75 | { | ||
| 76 | F_MH, | ||
| 77 | F_MBOX, | ||
| 78 | F_MAILDIR, | ||
| 79 | F_IMAP, | ||
| 80 | F_NEWS, | ||
| 81 | F_UNKNOWN | ||
| 82 | } FolderType; | ||
| 83 | |||
| 84 | typedef enum | ||
| 85 | { | ||
| 86 | F_NORMAL, | ||
| 87 | F_INBOX, | ||
| 88 | F_OUTBOX, | ||
| 89 | F_DRAFT, | ||
| 90 | F_QUEUE, | ||
| 91 | F_TRASH, | ||
| 92 | F_JUNK, | ||
| 93 | F_VIRTUAL | ||
| 94 | } SpecialFolderItemType; | ||
| 95 | |||
| 96 | typedef enum | ||
| 97 | { | ||
| 98 | SORT_BY_NONE, | ||
| 99 | SORT_BY_NUMBER, | ||
| 100 | SORT_BY_SIZE, | ||
| 101 | SORT_BY_DATE, | ||
| 102 | SORT_BY_TDATE, | ||
| 103 | SORT_BY_FROM, | ||
| 104 | SORT_BY_SUBJECT, | ||
| 105 | SORT_BY_SCORE, | ||
| 106 | SORT_BY_LABEL, | ||
| 107 | SORT_BY_MARK, | ||
| 108 | SORT_BY_UNREAD, | ||
| 109 | SORT_BY_MIME, | ||
| 110 | SORT_BY_TO | ||
| 111 | } FolderSortKey; | ||
| 112 | |||
| 113 | typedef enum | ||
| 114 | { | ||
| 115 | SORT_ASCENDING, | ||
| 116 | SORT_DESCENDING | ||
| 117 | } FolderSortType; | ||
| 118 | |||
| 119 | typedef void (*FolderUIFunc) (Folder *folder, | ||
| 120 | FolderItem *item, | ||
| 121 | gpointer data); | ||
| 122 | typedef gboolean (*FolderUIFunc2) (Folder *folder, | ||
| 123 | FolderItem *item, | ||
| 124 | guint count, | ||
| 125 | guint total, | ||
| 126 | gpointer data); | ||
| 127 | typedef void (*FolderDestroyNotify) (Folder *folder, | ||
| 128 | FolderItem *item, | ||
| 129 | gpointer data); | ||
| 130 | |||
| 131 | #include "prefs_account.h" | ||
| 132 | #include "session.h" | ||
| 133 | #include "procmsg.h" | ||
| 134 | #include "utils.h" | ||
| 135 | |||
| 136 | struct _Folder | ||
| 137 | { | ||
| 138 | FolderClass *klass; | ||
| 139 | |||
| 140 | gchar *name; | ||
| 141 | PrefsAccount *account; | ||
| 142 | |||
| 143 | FolderItem *inbox; | ||
| 144 | FolderItem *outbox; | ||
| 145 | FolderItem *draft; | ||
| 146 | FolderItem *queue; | ||
| 147 | FolderItem *trash; | ||
| 148 | |||
| 149 | FolderUIFunc ui_func; | ||
| 150 | gpointer ui_func_data; | ||
| 151 | |||
| 152 | GNode *node; | ||
| 153 | |||
| 154 | gpointer data; | ||
| 155 | }; | ||
| 156 | |||
| 157 | struct _FolderClass | ||
| 158 | { | ||
| 159 | FolderType type; | ||
| 160 | |||
| 161 | /* virtual functions */ | ||
| 162 | Folder * (*folder_new) (const gchar *name, | ||
| 163 | const gchar *path); | ||
| 164 | void (*destroy) (Folder *folder); | ||
| 165 | |||
| 166 | gint (*scan_tree) (Folder *folder); | ||
| 167 | gint (*create_tree) (Folder *folder); | ||
| 168 | |||
| 169 | GSList * (*get_msg_list) (Folder *folder, | ||
| 170 | FolderItem *item, | ||
| 171 | gboolean use_cache); | ||
| 172 | GSList * (*get_uncached_msg_list) | ||
| 173 | (Folder *folder, | ||
| 174 | FolderItem *item); | ||
| 175 | /* return value is filename encoding */ | ||
| 176 | gchar * (*fetch_msg) (Folder *folder, | ||
| 177 | FolderItem *item, | ||
| 178 | gint num); | ||
| 179 | MsgInfo * (*get_msginfo) (Folder *folder, | ||
| 180 | FolderItem *item, | ||
| 181 | gint num); | ||
| 182 | gint (*add_msg) (Folder *folder, | ||
| 183 | FolderItem *dest, | ||
| 184 | const gchar *file, | ||
| 185 | MsgFlags *flags, | ||
| 186 | gboolean remove_source); | ||
| 187 | gint (*add_msgs) (Folder *folder, | ||
| 188 | FolderItem *dest, | ||
| 189 | GSList *file_list, | ||
| 190 | gboolean remove_source, | ||
| 191 | gint *first); | ||
| 192 | gint (*add_msg_msginfo) (Folder *folder, | ||
| 193 | FolderItem *dest, | ||
| 194 | MsgInfo *msginfo, | ||
| 195 | gboolean remove_source); | ||
| 196 | gint (*add_msgs_msginfo) (Folder *folder, | ||
| 197 | FolderItem *dest, | ||
| 198 | GSList *msginfo_list, | ||
| 199 | gboolean remove_source, | ||
| 200 | gint *first); | ||
| 201 | gint (*move_msg) (Folder *folder, | ||
| 202 | FolderItem *dest, | ||
| 203 | MsgInfo *msginfo); | ||
| 204 | gint (*move_msgs) (Folder *folder, | ||
| 205 | FolderItem *dest, | ||
| 206 | GSList *msglist); | ||
| 207 | gint (*copy_msg) (Folder *folder, | ||
| 208 | FolderItem *dest, | ||
| 209 | MsgInfo *msginfo); | ||
| 210 | gint (*copy_msgs) (Folder *folder, | ||
| 211 | FolderItem *dest, | ||
| 212 | GSList *msglist); | ||
| 213 | gint (*remove_msg) (Folder *folder, | ||
| 214 | FolderItem *item, | ||
| 215 | MsgInfo *msginfo); | ||
| 216 | gint (*remove_msgs) (Folder *folder, | ||
| 217 | FolderItem *item, | ||
| 218 | GSList *msglist); | ||
| 219 | gint (*remove_all_msg) (Folder *folder, | ||
| 220 | FolderItem *item); | ||
| 221 | gboolean (*is_msg_changed) (Folder *folder, | ||
| 222 | FolderItem *item, | ||
| 223 | MsgInfo *msginfo); | ||
| 224 | gint (*close) (Folder *folder, | ||
| 225 | FolderItem *item); | ||
| 226 | gint (*scan) (Folder *folder, | ||
| 227 | FolderItem *item); | ||
| 228 | |||
| 229 | FolderItem * (*create_folder) (Folder *folder, | ||
| 230 | FolderItem *parent, | ||
| 231 | const gchar *name); | ||
| 232 | gint (*rename_folder) (Folder *folder, | ||
| 233 | FolderItem *item, | ||
| 234 | const gchar *name); | ||
| 235 | gint (*move_folder) (Folder *folder, | ||
| 236 | FolderItem *item, | ||
| 237 | FolderItem *new_parent); | ||
| 238 | gint (*remove_folder) (Folder *folder, | ||
| 239 | FolderItem *item); | ||
| 240 | }; | ||
| 241 | |||
| 242 | struct _LocalFolder | ||
| 243 | { | ||
| 244 | Folder folder; | ||
| 245 | |||
| 246 | gchar *rootpath; | ||
| 247 | }; | ||
| 248 | |||
| 249 | struct _RemoteFolder | ||
| 250 | { | ||
| 251 | Folder folder; | ||
| 252 | |||
| 253 | Session *session; | ||
| 254 | |||
| 255 | guint remove_cache_on_destroy : 1; | ||
| 256 | guint dummy1 : 1; | ||
| 257 | guint dummy2 : 1; | ||
| 258 | guint dummy3 : 1; | ||
| 259 | }; | ||
| 260 | |||
| 261 | #if 0 | ||
| 262 | struct _MboxFolder | ||
| 263 | { | ||
| 264 | LocalFolder lfolder; | ||
| 265 | }; | ||
| 266 | |||
| 267 | struct _MaildirFolder | ||
| 268 | { | ||
| 269 | LocalFolder lfolder; | ||
| 270 | }; | ||
| 271 | #endif | ||
| 272 | |||
| 273 | struct _FolderItem | ||
| 274 | { | ||
| 275 | SpecialFolderItemType stype; | ||
| 276 | |||
| 277 | gchar *name; /* UTF-8 */ | ||
| 278 | gchar *path; /* UTF-8 */ | ||
| 279 | |||
| 280 | stime_t mtime; | ||
| 281 | |||
| 282 | gint new; | ||
| 283 | gint unread; | ||
| 284 | gint total; | ||
| 285 | gint unmarked_num; | ||
| 286 | |||
| 287 | gint last_num; | ||
| 288 | |||
| 289 | /* special flags */ | ||
| 290 | guint no_sub : 1; /* no child allowed? */ | ||
| 291 | guint no_select : 1; /* not selectable? */ | ||
| 292 | guint collapsed : 1; /* collapsed item */ | ||
| 293 | guint threaded : 1; /* threaded folder view */ | ||
| 294 | |||
| 295 | guint opened : 1; /* opened by summary view */ | ||
| 296 | guint updated : 1; /* folderview should be updated */ | ||
| 297 | |||
| 298 | guint cache_dirty : 1; /* cache file needs to be updated */ | ||
| 299 | guint mark_dirty : 1; /* mark file needs to be updated */ | ||
| 300 | |||
| 301 | FolderSortKey sort_key; | ||
| 302 | FolderSortType sort_type; | ||
| 303 | |||
| 304 | GNode *node; | ||
| 305 | |||
| 306 | FolderItem *parent; | ||
| 307 | |||
| 308 | Folder *folder; | ||
| 309 | |||
| 310 | PrefsAccount *account; | ||
| 311 | |||
| 312 | gboolean ac_apply_sub; | ||
| 313 | |||
| 314 | gchar *auto_to; | ||
| 315 | gboolean use_auto_to_on_reply; | ||
| 316 | gchar *auto_cc; | ||
| 317 | gchar *auto_bcc; | ||
| 318 | gchar *auto_replyto; | ||
| 319 | |||
| 320 | gboolean trim_summary_subject; | ||
| 321 | gboolean trim_compose_subject; | ||
| 322 | |||
| 323 | GSList *cache_queue; | ||
| 324 | GSList *mark_queue; | ||
| 325 | |||
| 326 | guint last_selected; | ||
| 327 | gint qsearch_cond_type; | ||
| 328 | |||
| 329 | gpointer data; | ||
| 330 | }; | ||
| 331 | |||
| 332 | Folder *folder_new (FolderType type, | ||
| 333 | const gchar *name, | ||
| 334 | const gchar *path); | ||
| 335 | void folder_local_folder_init (Folder *folder, | ||
| 336 | const gchar *name, | ||
| 337 | const gchar *path); | ||
| 338 | void folder_remote_folder_init (Folder *folder, | ||
| 339 | const gchar *name, | ||
| 340 | const gchar *path); | ||
| 341 | |||
| 342 | void folder_destroy (Folder *folder); | ||
| 343 | void folder_local_folder_destroy (LocalFolder *lfolder); | ||
| 344 | void folder_remote_folder_destroy(RemoteFolder *rfolder); | ||
| 345 | |||
| 346 | gint folder_remote_folder_destroy_all_sessions (void); | ||
| 347 | |||
| 348 | gboolean folder_remote_folder_is_session_active | ||
| 349 | (RemoteFolder *rfolder); | ||
| 350 | gboolean folder_remote_folder_active_session_exist (void); | ||
| 351 | |||
| 352 | gint folder_scan_tree (Folder *folder); | ||
| 353 | gint folder_create_tree (Folder *folder); | ||
| 354 | |||
| 355 | FolderItem *folder_item_new (const gchar *name, | ||
| 356 | const gchar *path); | ||
| 357 | void folder_item_append (FolderItem *parent, | ||
| 358 | FolderItem *item); | ||
| 359 | FolderItem *folder_item_copy (FolderItem *item); | ||
| 360 | void folder_item_remove (FolderItem *item); | ||
| 361 | void folder_item_remove_children (FolderItem *item); | ||
| 362 | void folder_item_destroy (FolderItem *item); | ||
| 363 | |||
| 364 | gint folder_item_compare (FolderItem *item_a, | ||
| 365 | FolderItem *item_b); | ||
| 366 | |||
| 367 | void folder_set_ui_func (Folder *folder, | ||
| 368 | FolderUIFunc func, | ||
| 369 | gpointer data); | ||
| 370 | void folder_set_ui_func2 (Folder *folder, | ||
| 371 | FolderUIFunc2 func, | ||
| 372 | gpointer data); | ||
| 373 | FolderUIFunc2 folder_get_ui_func2 (Folder *folder); | ||
| 374 | gboolean folder_call_ui_func2 (Folder *folder, | ||
| 375 | FolderItem *item, | ||
| 376 | guint count, | ||
| 377 | guint total); | ||
| 378 | |||
| 379 | void folder_set_name (Folder *folder, | ||
| 380 | const gchar *name); | ||
| 381 | void folder_tree_destroy (Folder *folder); | ||
| 382 | |||
| 383 | void folder_add (Folder *folder); | ||
| 384 | |||
| 385 | GList *folder_get_list (void); | ||
| 386 | gint folder_read_list (void); | ||
| 387 | void folder_write_list (void); | ||
| 388 | |||
| 389 | gchar *folder_get_status (GPtrArray *folders, | ||
| 390 | gboolean full); | ||
| 391 | |||
| 392 | Folder *folder_find_from_path (const gchar *path); | ||
| 393 | Folder *folder_find_from_name (const gchar *name, | ||
| 394 | FolderType type); | ||
| 395 | FolderItem *folder_find_item_from_path (const gchar *path); | ||
| 396 | FolderItem *folder_find_child_item_by_name (FolderItem *item, | ||
| 397 | const gchar *name); | ||
| 398 | gchar *folder_get_identifier (Folder *folder); | ||
| 399 | gchar *folder_item_get_identifier (FolderItem *item); | ||
| 400 | FolderItem *folder_find_item_from_identifier (const gchar *identifier); | ||
| 401 | FolderItem *folder_find_item_and_num_from_id (const gchar *identifier, | ||
| 402 | gint *num); | ||
| 403 | |||
| 404 | Folder *folder_get_default_folder (void); | ||
| 405 | FolderItem *folder_get_default_inbox (void); | ||
| 406 | FolderItem *folder_get_default_outbox (void); | ||
| 407 | FolderItem *folder_get_default_draft (void); | ||
| 408 | FolderItem *folder_get_default_queue (void); | ||
| 409 | FolderItem *folder_get_default_trash (void); | ||
| 410 | FolderItem *folder_get_default_junk (void); | ||
| 411 | |||
| 412 | gboolean folder_item_is_trash (FolderItem *item); | ||
| 413 | |||
| 414 | FolderItem *folder_get_junk (Folder *folder); | ||
| 415 | void folder_set_junk (Folder *folder, | ||
| 416 | FolderItem *item); | ||
| 417 | |||
| 418 | void folder_set_missing_folders (void); | ||
| 419 | void folder_unref_account_all (PrefsAccount *account); | ||
| 420 | |||
| 421 | /* return value is filename encoding */ | ||
| 422 | gchar *folder_get_path (Folder *folder); | ||
| 423 | gchar *folder_item_get_path (FolderItem *item); | ||
| 424 | |||
| 425 | gint folder_item_scan (FolderItem *item); | ||
| 426 | void folder_item_scan_foreach (GHashTable *table); | ||
| 427 | GSList *folder_item_get_msg_list (FolderItem *item, | ||
| 428 | gboolean use_cache); | ||
| 429 | GSList *folder_item_get_uncached_msg_list | ||
| 430 | (FolderItem *item); | ||
| 431 | /* return value is filename encoding */ | ||
| 432 | gchar *folder_item_fetch_msg (FolderItem *item, | ||
| 433 | gint num); | ||
| 434 | gint folder_item_fetch_all_msg (FolderItem *item); | ||
| 435 | MsgInfo *folder_item_get_msginfo (FolderItem *item, | ||
| 436 | gint num); | ||
| 437 | gint folder_item_add_msg (FolderItem *dest, | ||
| 438 | const gchar *file, | ||
| 439 | MsgFlags *flags, | ||
| 440 | gboolean remove_source); | ||
| 441 | gint folder_item_add_msgs (FolderItem *dest, | ||
| 442 | GSList *file_list, | ||
| 443 | gboolean remove_source, | ||
| 444 | gint *first); | ||
| 445 | gint folder_item_add_msg_msginfo (FolderItem *dest, | ||
| 446 | MsgInfo *msginfo, | ||
| 447 | gboolean remove_source); | ||
| 448 | gint folder_item_add_msgs_msginfo (FolderItem *dest, | ||
| 449 | GSList *msginfo_list, | ||
| 450 | gboolean remove_source, | ||
| 451 | gint *first); | ||
| 452 | gint folder_item_move_msg (FolderItem *dest, | ||
| 453 | MsgInfo *msginfo); | ||
| 454 | gint folder_item_move_msgs (FolderItem *dest, | ||
| 455 | GSList *msglist); | ||
| 456 | gint folder_item_copy_msg (FolderItem *dest, | ||
| 457 | MsgInfo *msginfo); | ||
| 458 | gint folder_item_copy_msgs (FolderItem *dest, | ||
| 459 | GSList *msglist); | ||
| 460 | gint folder_item_remove_msg (FolderItem *item, | ||
| 461 | MsgInfo *msginfo); | ||
| 462 | gint folder_item_remove_msgs (FolderItem *item, | ||
| 463 | GSList *msglist); | ||
| 464 | gint folder_item_remove_all_msg (FolderItem *item); | ||
| 465 | |||
| 466 | gboolean folder_item_is_msg_changed (FolderItem *item, | ||
| 467 | MsgInfo *msginfo); | ||
| 468 | |||
| 469 | /* return value is filename encoding */ | ||
| 470 | gchar *folder_item_get_cache_file (FolderItem *item); | ||
| 471 | gchar *folder_item_get_mark_file (FolderItem *item); | ||
| 472 | |||
| 473 | gint folder_item_close (FolderItem *item); | ||
| 474 | |||
| 475 | #endif /* __FOLDER_H__ */ | ||
diff --git a/libsylph/html.c b/libsylph/html.c new file mode 100644 index 0000000..a86c07b --- /dev/null +++ b/libsylph/html.c | |||
| @@ -0,0 +1,791 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2011 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <glib.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <string.h> | ||
| 23 | #include <ctype.h> | ||
| 24 | |||
| 25 | #include "html.h" | ||
| 26 | #include "codeconv.h" | ||
| 27 | #include "utils.h" | ||
| 28 | |||
| 29 | #define HTMLBUFSIZE 8192 | ||
| 30 | #define HR_STR "------------------------------------------------" | ||
| 31 | |||
| 32 | typedef struct _HTMLSymbol HTMLSymbol; | ||
| 33 | |||
| 34 | struct _HTMLSymbol | ||
| 35 | { | ||
| 36 | gchar *const key; | ||
| 37 | gchar *const val; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static HTMLSymbol symbol_list[] = { | ||
| 41 | {"<" , "<"}, | ||
| 42 | {">" , ">"}, | ||
| 43 | {"&" , "&"}, | ||
| 44 | {""" , "\""} | ||
| 45 | }; | ||
| 46 | |||
| 47 | /*   - ÿ */ | ||
| 48 | static HTMLSymbol latin_symbol_list[] = { | ||
| 49 | {" " , " "}, | ||
| 50 | /* {" " , "\302\240"}, */ | ||
| 51 | {"¡" , "\302\241"}, | ||
| 52 | {"¢" , "\302\242"}, | ||
| 53 | {"£" , "\302\243"}, | ||
| 54 | {"¤", "\302\244"}, | ||
| 55 | {"¥" , "\302\245"}, | ||
| 56 | {"¦", "\302\246"}, | ||
| 57 | {"§" , "\302\247"}, | ||
| 58 | {"¨" , "\302\250"}, | ||
| 59 | {"©" , "\302\251"}, | ||
| 60 | {"ª" , "\302\252"}, | ||
| 61 | {"«" , "\302\253"}, | ||
| 62 | {"¬" , "\302\254"}, | ||
| 63 | {"­" , "\302\255"}, | ||
| 64 | {"®" , "\302\256"}, | ||
| 65 | {"¯" , "\302\257"}, | ||
| 66 | {"°" , "\302\260"}, | ||
| 67 | {"&plusm;" , "\302\261"}, | ||
| 68 | {"²" , "\302\262"}, | ||
| 69 | {"³" , "\302\263"}, | ||
| 70 | {"´" , "\302\264"}, | ||
| 71 | {"µ" , "\302\265"}, | ||
| 72 | {"¶" , "\302\266"}, | ||
| 73 | {"·", "\302\267"}, | ||
| 74 | {"¸" , "\302\270"}, | ||
| 75 | {"¹" , "\302\271"}, | ||
| 76 | {"º" , "\302\272"}, | ||
| 77 | {"»" , "\302\273"}, | ||
| 78 | {"¼", "\302\274"}, | ||
| 79 | {"½", "\302\275"}, | ||
| 80 | {"¾", "\302\276"}, | ||
| 81 | {"¿", "\302\277"}, | ||
| 82 | |||
| 83 | {"À", "\303\200"}, | ||
| 84 | {"Á", "\303\201"}, | ||
| 85 | {"Â" , "\303\202"}, | ||
| 86 | {"Ã", "\303\203"}, | ||
| 87 | {"Ä" , "\303\204"}, | ||
| 88 | {"Å" , "\303\205"}, | ||
| 89 | {"Æ" , "\303\206"}, | ||
| 90 | {"Ç", "\303\207"}, | ||
| 91 | {"È", "\303\210"}, | ||
| 92 | {"É", "\303\211"}, | ||
| 93 | {"Ê" , "\303\212"}, | ||
| 94 | {"Ë" , "\303\213"}, | ||
| 95 | {"Ì", "\303\214"}, | ||
| 96 | {"Í", "\303\215"}, | ||
| 97 | {"Î" , "\303\216"}, | ||
| 98 | {"Ï" , "\303\217"}, | ||
| 99 | {"Ð" , "\303\220"}, | ||
| 100 | {"Ñ", "\303\221"}, | ||
| 101 | {"Ò", "\303\222"}, | ||
| 102 | {"Ó", "\303\223"}, | ||
| 103 | {"Ô" , "\303\224"}, | ||
| 104 | {"Õ", "\303\225"}, | ||
| 105 | {"Ö" , "\303\226"}, | ||
| 106 | {"×" , "\303\227"}, | ||
| 107 | {"Ø", "\303\230"}, | ||
| 108 | {"Ù", "\303\231"}, | ||
| 109 | {"Ú", "\303\232"}, | ||
| 110 | {"Û" , "\303\233"}, | ||
| 111 | {"Ü" , "\303\234"}, | ||
| 112 | {"Ý", "\303\235"}, | ||
| 113 | {"Þ" , "\303\236"}, | ||
| 114 | {"ß" , "\303\237"}, | ||
| 115 | {"à", "\303\240"}, | ||
| 116 | {"á", "\303\241"}, | ||
| 117 | {"â" , "\303\242"}, | ||
| 118 | {"ã", "\303\243"}, | ||
| 119 | {"ä" , "\303\244"}, | ||
| 120 | {"å" , "\303\245"}, | ||
| 121 | {"æ" , "\303\246"}, | ||
| 122 | {"ç", "\303\247"}, | ||
| 123 | {"è", "\303\250"}, | ||
| 124 | {"é", "\303\251"}, | ||
| 125 | {"ê" , "\303\252"}, | ||
| 126 | {"ë" , "\303\253"}, | ||
| 127 | {"ì", "\303\254"}, | ||
| 128 | {"í", "\303\255"}, | ||
| 129 | {"î" , "\303\256"}, | ||
| 130 | {"ï" , "\303\257"}, | ||
| 131 | {"ð" , "\303\260"}, | ||
| 132 | {"ñ", "\303\261"}, | ||
| 133 | {"ò", "\303\262"}, | ||
| 134 | {"ó", "\303\263"}, | ||
| 135 | {"ô" , "\303\264"}, | ||
| 136 | {"õ", "\303\265"}, | ||
| 137 | {"ö" , "\303\266"}, | ||
| 138 | {"÷", "\303\267"}, | ||
| 139 | {"ø", "\303\270"}, | ||
| 140 | {"ù", "\303\271"}, | ||
| 141 | {"ú", "\303\272"}, | ||
| 142 | {"û" , "\303\273"}, | ||
| 143 | {"ü" , "\303\274"}, | ||
| 144 | {"ý", "\303\275"}, | ||
| 145 | {"þ" , "\303\276"}, | ||
| 146 | {"ÿ" , "\303\277"} | ||
| 147 | }; | ||
| 148 | |||
| 149 | static HTMLSymbol other_symbol_list[] = { | ||
| 150 | /* Non-standard? */ | ||
| 151 | {"…" , "..."}, | ||
| 152 | {"’" , "'"}, | ||
| 153 | {"–" , "-"}, | ||
| 154 | {"™" , "\xe2\x84\xa2"}, | ||
| 155 | {"œ" , "\xc5\x93"}, | ||
| 156 | |||
| 157 | /* Symbolic characters */ | ||
| 158 | {"™" , "\xe2\x84\xa2"}, | ||
| 159 | |||
| 160 | /* Latin extended */ | ||
| 161 | {"Œ" , "\xc5\x92"}, | ||
| 162 | {"œ" , "\xc5\x93"}, | ||
| 163 | {"Š", "\xc5\xa0"}, | ||
| 164 | {"š", "\xc5\xa1"}, | ||
| 165 | {"Ÿ" , "\xc5\xb8"}, | ||
| 166 | {"ˆ" , "\xcb\x86"}, | ||
| 167 | {"˜" , "\xcb\x9c"}, | ||
| 168 | {"ƒ" , "\xc6\x92"}, | ||
| 169 | }; | ||
| 170 | |||
| 171 | static GHashTable *default_symbol_table; | ||
| 172 | |||
| 173 | static HTMLState html_read_line (HTMLParser *parser); | ||
| 174 | |||
| 175 | static void html_append_char (HTMLParser *parser, | ||
| 176 | gchar ch); | ||
| 177 | static void html_append_str (HTMLParser *parser, | ||
| 178 | const gchar *str, | ||
| 179 | gint len); | ||
| 180 | |||
| 181 | static gchar *html_find_char (HTMLParser *parser, | ||
| 182 | gchar ch); | ||
| 183 | static gchar *html_find_str (HTMLParser *parser, | ||
| 184 | const gchar *str); | ||
| 185 | static gchar *html_find_str_case (HTMLParser *parser, | ||
| 186 | const gchar *str); | ||
| 187 | |||
| 188 | static HTMLState html_parse_tag (HTMLParser *parser); | ||
| 189 | static void html_parse_special (HTMLParser *parser); | ||
| 190 | static void html_get_parenthesis (HTMLParser *parser, | ||
| 191 | gchar *buf, | ||
| 192 | gint len); | ||
| 193 | |||
| 194 | static gchar *html_unescape_str (HTMLParser *parser, | ||
| 195 | const gchar *str); | ||
| 196 | |||
| 197 | |||
| 198 | HTMLParser *html_parser_new(FILE *fp, CodeConverter *conv) | ||
| 199 | { | ||
| 200 | HTMLParser *parser; | ||
| 201 | |||
| 202 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 203 | g_return_val_if_fail(conv != NULL, NULL); | ||
| 204 | |||
| 205 | parser = g_new0(HTMLParser, 1); | ||
| 206 | parser->fp = fp; | ||
| 207 | parser->conv = conv; | ||
| 208 | parser->str = g_string_new(NULL); | ||
| 209 | parser->buf = g_string_new(NULL); | ||
| 210 | parser->bufp = parser->buf->str; | ||
| 211 | parser->state = HTML_NORMAL; | ||
| 212 | parser->href = NULL; | ||
| 213 | parser->newline = TRUE; | ||
| 214 | parser->empty_line = TRUE; | ||
| 215 | parser->space = FALSE; | ||
| 216 | parser->pre = FALSE; | ||
| 217 | parser->blockquote = 0; | ||
| 218 | |||
| 219 | #define SYMBOL_TABLE_ADD(table, list) \ | ||
| 220 | { \ | ||
| 221 | gint i; \ | ||
| 222 | \ | ||
| 223 | for (i = 0; i < sizeof(list) / sizeof(list[0]); i++) \ | ||
| 224 | g_hash_table_insert(table, list[i].key, list[i].val); \ | ||
| 225 | } | ||
| 226 | |||
| 227 | if (!default_symbol_table) { | ||
| 228 | default_symbol_table = | ||
| 229 | g_hash_table_new(g_str_hash, g_str_equal); | ||
| 230 | SYMBOL_TABLE_ADD(default_symbol_table, symbol_list); | ||
| 231 | SYMBOL_TABLE_ADD(default_symbol_table, latin_symbol_list); | ||
| 232 | SYMBOL_TABLE_ADD(default_symbol_table, other_symbol_list); | ||
| 233 | } | ||
| 234 | |||
| 235 | #undef SYMBOL_TABLE_ADD | ||
| 236 | |||
| 237 | parser->symbol_table = default_symbol_table; | ||
| 238 | |||
| 239 | return parser; | ||
| 240 | } | ||
| 241 | |||
| 242 | void html_parser_destroy(HTMLParser *parser) | ||
| 243 | { | ||
| 244 | g_string_free(parser->str, TRUE); | ||
| 245 | g_string_free(parser->buf, TRUE); | ||
| 246 | g_free(parser->href); | ||
| 247 | g_free(parser); | ||
| 248 | } | ||
| 249 | |||
| 250 | const gchar *html_parse(HTMLParser *parser) | ||
| 251 | { | ||
| 252 | parser->state = HTML_NORMAL; | ||
| 253 | g_string_truncate(parser->str, 0); | ||
| 254 | |||
| 255 | if (*parser->bufp == '\0') { | ||
| 256 | g_string_truncate(parser->buf, 0); | ||
| 257 | parser->bufp = parser->buf->str; | ||
| 258 | if (html_read_line(parser) == HTML_EOF) | ||
| 259 | return NULL; | ||
| 260 | } | ||
| 261 | |||
| 262 | while (*parser->bufp != '\0') { | ||
| 263 | switch (*parser->bufp) { | ||
| 264 | case '<': | ||
| 265 | if (parser->str->len == 0) | ||
| 266 | html_parse_tag(parser); | ||
| 267 | else | ||
| 268 | return parser->str->str; | ||
| 269 | break; | ||
| 270 | case '&': | ||
| 271 | html_parse_special(parser); | ||
| 272 | break; | ||
| 273 | case ' ': | ||
| 274 | case '\t': | ||
| 275 | case '\r': | ||
| 276 | case '\n': | ||
| 277 | if (parser->bufp[0] == '\r' && parser->bufp[1] == '\n') | ||
| 278 | parser->bufp++; | ||
| 279 | |||
| 280 | if (!parser->pre) { | ||
| 281 | if (!parser->newline) | ||
| 282 | parser->space = TRUE; | ||
| 283 | |||
| 284 | parser->bufp++; | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | /* fallthrough */ | ||
| 288 | default: | ||
| 289 | html_append_char(parser, *parser->bufp++); | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | return parser->str->str; | ||
| 294 | } | ||
| 295 | |||
| 296 | static HTMLState html_read_line(HTMLParser *parser) | ||
| 297 | { | ||
| 298 | gchar buf[HTMLBUFSIZE]; | ||
| 299 | gchar *conv_str; | ||
| 300 | gint index; | ||
| 301 | |||
| 302 | if (fgets(buf, sizeof(buf), parser->fp) == NULL) { | ||
| 303 | parser->state = HTML_EOF; | ||
| 304 | return HTML_EOF; | ||
| 305 | } | ||
| 306 | |||
| 307 | conv_str = conv_convert(parser->conv, buf); | ||
| 308 | if (!conv_str) { | ||
| 309 | index = parser->bufp - parser->buf->str; | ||
| 310 | |||
| 311 | conv_str = conv_utf8todisp(buf, NULL); | ||
| 312 | g_string_append(parser->buf, conv_str); | ||
| 313 | g_free(conv_str); | ||
| 314 | |||
| 315 | parser->bufp = parser->buf->str + index; | ||
| 316 | |||
| 317 | return HTML_CONV_FAILED; | ||
| 318 | } | ||
| 319 | |||
| 320 | index = parser->bufp - parser->buf->str; | ||
| 321 | |||
| 322 | g_string_append(parser->buf, conv_str); | ||
| 323 | g_free(conv_str); | ||
| 324 | |||
| 325 | parser->bufp = parser->buf->str + index; | ||
| 326 | |||
| 327 | return HTML_NORMAL; | ||
| 328 | } | ||
| 329 | |||
| 330 | static void html_append_char(HTMLParser *parser, gchar ch) | ||
| 331 | { | ||
| 332 | GString *str = parser->str; | ||
| 333 | const gchar *bq_prefix = NULL; | ||
| 334 | |||
| 335 | if (!parser->pre && parser->space) { | ||
| 336 | g_string_append_c(str, ' '); | ||
| 337 | parser->space = FALSE; | ||
| 338 | } | ||
| 339 | |||
| 340 | if (parser->newline && parser->blockquote > 0) | ||
| 341 | bq_prefix = " "; | ||
| 342 | |||
| 343 | parser->empty_line = FALSE; | ||
| 344 | if (ch == '\n') { | ||
| 345 | parser->newline = TRUE; | ||
| 346 | if (str->len > 0 && str->str[str->len - 1] == '\n') | ||
| 347 | parser->empty_line = TRUE; | ||
| 348 | } else | ||
| 349 | parser->newline = FALSE; | ||
| 350 | |||
| 351 | if (bq_prefix) { | ||
| 352 | gint i; | ||
| 353 | for (i = 0; i < parser->blockquote; i++) | ||
| 354 | g_string_append(str, bq_prefix); | ||
| 355 | } | ||
| 356 | g_string_append_c(str, ch); | ||
| 357 | } | ||
| 358 | |||
| 359 | static void html_append_str(HTMLParser *parser, const gchar *str, gint len) | ||
| 360 | { | ||
| 361 | GString *string = parser->str; | ||
| 362 | const gchar *bq_prefix = NULL; | ||
| 363 | |||
| 364 | if (!parser->pre && parser->space) { | ||
| 365 | g_string_append_c(string, ' '); | ||
| 366 | parser->space = FALSE; | ||
| 367 | } | ||
| 368 | |||
| 369 | if (len == 0) return; | ||
| 370 | |||
| 371 | if (parser->newline && parser->blockquote > 0) | ||
| 372 | bq_prefix = " "; | ||
| 373 | |||
| 374 | if (bq_prefix) { | ||
| 375 | gint i; | ||
| 376 | for (i = 0; i < parser->blockquote; i++) | ||
| 377 | g_string_append(string, bq_prefix); | ||
| 378 | } | ||
| 379 | |||
| 380 | if (len < 0) | ||
| 381 | g_string_append(string, str); | ||
| 382 | else | ||
| 383 | g_string_append_len(string, str, len); | ||
| 384 | |||
| 385 | parser->empty_line = FALSE; | ||
| 386 | if (string->len > 0 && string->str[string->len - 1] == '\n') { | ||
| 387 | parser->newline = TRUE; | ||
| 388 | if (string->len > 1 && string->str[string->len - 2] == '\n') | ||
| 389 | parser->empty_line = TRUE; | ||
| 390 | } else | ||
| 391 | parser->newline = FALSE; | ||
| 392 | } | ||
| 393 | |||
| 394 | static gchar *html_find_char(HTMLParser *parser, gchar ch) | ||
| 395 | { | ||
| 396 | gchar *p; | ||
| 397 | |||
| 398 | while ((p = strchr(parser->bufp, ch)) == NULL) { | ||
| 399 | if (html_read_line(parser) == HTML_EOF) | ||
| 400 | return NULL; | ||
| 401 | } | ||
| 402 | |||
| 403 | return p; | ||
| 404 | } | ||
| 405 | |||
| 406 | static gchar *html_find_str(HTMLParser *parser, const gchar *str) | ||
| 407 | { | ||
| 408 | gchar *p; | ||
| 409 | |||
| 410 | while ((p = strstr(parser->bufp, str)) == NULL) { | ||
| 411 | if (html_read_line(parser) == HTML_EOF) | ||
| 412 | return NULL; | ||
| 413 | } | ||
| 414 | |||
| 415 | return p; | ||
| 416 | } | ||
| 417 | |||
| 418 | static gchar *html_find_str_case(HTMLParser *parser, const gchar *str) | ||
| 419 | { | ||
| 420 | gchar *p; | ||
| 421 | |||
| 422 | while ((p = strcasestr(parser->bufp, str)) == NULL) { | ||
| 423 | if (html_read_line(parser) == HTML_EOF) | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | |||
| 427 | return p; | ||
| 428 | } | ||
| 429 | |||
| 430 | static HTMLTag *html_get_tag(const gchar *str) | ||
| 431 | { | ||
| 432 | HTMLTag *tag; | ||
| 433 | gchar *tmp; | ||
| 434 | gchar *tmpp; | ||
| 435 | |||
| 436 | g_return_val_if_fail(str != NULL, NULL); | ||
| 437 | |||
| 438 | if (*str == '\0' || *str == '!') return NULL; | ||
| 439 | |||
| 440 | tmp = g_strdup(str); | ||
| 441 | |||
| 442 | tag = g_new0(HTMLTag, 1); | ||
| 443 | |||
| 444 | for (tmpp = tmp; *tmpp != '\0' && !g_ascii_isspace(*tmpp); tmpp++) { | ||
| 445 | if (tmpp > tmp && *tmpp == '/') { | ||
| 446 | *tmpp = '\0'; | ||
| 447 | break; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | if (*tmpp == '\0') { | ||
| 452 | g_strdown(tmp); | ||
| 453 | tag->name = tmp; | ||
| 454 | return tag; | ||
| 455 | } else { | ||
| 456 | *tmpp++ = '\0'; | ||
| 457 | g_strdown(tmp); | ||
| 458 | tag->name = g_strdup(tmp); | ||
| 459 | } | ||
| 460 | |||
| 461 | while (*tmpp != '\0') { | ||
| 462 | HTMLAttr *attr; | ||
| 463 | gchar *attr_name; | ||
| 464 | gchar *attr_value; | ||
| 465 | gchar *p; | ||
| 466 | gchar quote; | ||
| 467 | |||
| 468 | while (g_ascii_isspace(*tmpp)) tmpp++; | ||
| 469 | if (tmpp > tmp && *tmpp == '/') | ||
| 470 | break; | ||
| 471 | attr_name = tmpp; | ||
| 472 | |||
| 473 | while (*tmpp != '\0' && !g_ascii_isspace(*tmpp) && | ||
| 474 | *tmpp != '=') | ||
| 475 | tmpp++; | ||
| 476 | if (*tmpp != '\0' && *tmpp != '=') { | ||
| 477 | *tmpp++ = '\0'; | ||
| 478 | while (g_ascii_isspace(*tmpp)) tmpp++; | ||
| 479 | } | ||
| 480 | |||
| 481 | if (*tmpp == '=') { | ||
| 482 | *tmpp++ = '\0'; | ||
| 483 | while (g_ascii_isspace(*tmpp)) tmpp++; | ||
| 484 | |||
| 485 | if (*tmpp == '"' || *tmpp == '\'') { | ||
| 486 | /* name="value" */ | ||
| 487 | quote = *tmpp; | ||
| 488 | tmpp++; | ||
| 489 | attr_value = tmpp; | ||
| 490 | if ((p = strchr(attr_value, quote)) == NULL) { | ||
| 491 | g_warning("html_get_tag(): syntax error in tag: '%s'\n", str); | ||
| 492 | break; | ||
| 493 | } | ||
| 494 | tmpp = p; | ||
| 495 | *tmpp++ = '\0'; | ||
| 496 | while (g_ascii_isspace(*tmpp)) tmpp++; | ||
| 497 | } else { | ||
| 498 | /* name=value */ | ||
| 499 | attr_value = tmpp; | ||
| 500 | while (*tmpp != '\0' && !g_ascii_isspace(*tmpp)) tmpp++; | ||
| 501 | if (*tmpp != '\0') | ||
| 502 | *tmpp++ = '\0'; | ||
| 503 | } | ||
| 504 | } else | ||
| 505 | attr_value = ""; | ||
| 506 | |||
| 507 | g_strchomp(attr_name); | ||
| 508 | g_strdown(attr_name); | ||
| 509 | attr = g_new(HTMLAttr, 1); | ||
| 510 | attr->name = g_strdup(attr_name); | ||
| 511 | attr->value = g_strdup(attr_value); | ||
| 512 | tag->attr = g_list_append(tag->attr, attr); | ||
| 513 | } | ||
| 514 | |||
| 515 | g_free(tmp); | ||
| 516 | |||
| 517 | return tag; | ||
| 518 | } | ||
| 519 | |||
| 520 | static void html_free_tag(HTMLTag *tag) | ||
| 521 | { | ||
| 522 | if (!tag) return; | ||
| 523 | |||
| 524 | g_free(tag->name); | ||
| 525 | while (tag->attr != NULL) { | ||
| 526 | HTMLAttr *attr = (HTMLAttr *)tag->attr->data; | ||
| 527 | g_free(attr->name); | ||
| 528 | g_free(attr->value); | ||
| 529 | g_free(attr); | ||
| 530 | tag->attr = g_list_remove(tag->attr, tag->attr->data); | ||
| 531 | } | ||
| 532 | g_free(tag); | ||
| 533 | } | ||
| 534 | |||
| 535 | static HTMLState html_parse_tag(HTMLParser *parser) | ||
| 536 | { | ||
| 537 | gchar buf[HTMLBUFSIZE]; | ||
| 538 | HTMLTag *tag; | ||
| 539 | |||
| 540 | html_get_parenthesis(parser, buf, sizeof(buf)); | ||
| 541 | |||
| 542 | tag = html_get_tag(buf); | ||
| 543 | |||
| 544 | parser->state = HTML_UNKNOWN; | ||
| 545 | if (!tag) return HTML_UNKNOWN; | ||
| 546 | |||
| 547 | if (!strcmp(tag->name, "br")) { | ||
| 548 | parser->space = FALSE; | ||
| 549 | html_append_char(parser, '\n'); | ||
| 550 | parser->state = HTML_BR; | ||
| 551 | } else if (!strcmp(tag->name, "a")) { | ||
| 552 | GList *cur; | ||
| 553 | |||
| 554 | for (cur = tag->attr; cur != NULL; cur = cur->next) { | ||
| 555 | HTMLAttr *attr = (HTMLAttr *)cur->data; | ||
| 556 | |||
| 557 | if (attr && !strcmp(attr->name, "href")) { | ||
| 558 | g_free(parser->href); | ||
| 559 | parser->href = html_unescape_str(parser, attr->value); | ||
| 560 | parser->state = HTML_HREF; | ||
| 561 | break; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | } else if (!strcmp(tag->name, "/a")) { | ||
| 565 | g_free(parser->href); | ||
| 566 | parser->href = NULL; | ||
| 567 | parser->state = HTML_NORMAL; | ||
| 568 | } else if (!strcmp(tag->name, "p")) { | ||
| 569 | parser->space = FALSE; | ||
| 570 | if (!parser->empty_line) { | ||
| 571 | parser->space = FALSE; | ||
| 572 | if (!parser->newline) html_append_char(parser, '\n'); | ||
| 573 | html_append_char(parser, '\n'); | ||
| 574 | } | ||
| 575 | parser->state = HTML_PAR; | ||
| 576 | } else if (!strcmp(tag->name, "pre")) { | ||
| 577 | parser->pre = TRUE; | ||
| 578 | parser->state = HTML_PRE; | ||
| 579 | } else if (!strcmp(tag->name, "/pre")) { | ||
| 580 | parser->pre = FALSE; | ||
| 581 | parser->state = HTML_NORMAL; | ||
| 582 | } else if (!strcmp(tag->name, "blockquote")) { | ||
| 583 | parser->blockquote++; | ||
| 584 | parser->state = HTML_BLOCKQUOTE; | ||
| 585 | } else if (!strcmp(tag->name, "/blockquote")) { | ||
| 586 | parser->blockquote--; | ||
| 587 | if (parser->blockquote < 0) | ||
| 588 | parser->blockquote = 0; | ||
| 589 | parser->state = HTML_NORMAL; | ||
| 590 | } else if (!strcmp(tag->name, "hr")) { | ||
| 591 | if (!parser->newline) { | ||
| 592 | parser->space = FALSE; | ||
| 593 | html_append_char(parser, '\n'); | ||
| 594 | } | ||
| 595 | html_append_str(parser, HR_STR "\n", -1); | ||
| 596 | parser->state = HTML_HR; | ||
| 597 | } else if (!strcmp(tag->name, "div") || | ||
| 598 | !strcmp(tag->name, "ul") || | ||
| 599 | !strcmp(tag->name, "li") || | ||
| 600 | !strcmp(tag->name, "table") || | ||
| 601 | !strcmp(tag->name, "tr") || | ||
| 602 | (tag->name[0] == 'h' && g_ascii_isdigit(tag->name[1]))) { | ||
| 603 | if (!parser->newline) { | ||
| 604 | parser->space = FALSE; | ||
| 605 | html_append_char(parser, '\n'); | ||
| 606 | } | ||
| 607 | parser->state = HTML_NORMAL; | ||
| 608 | } else if (!strcmp(tag->name, "/table") || | ||
| 609 | (tag->name[0] == '/' && | ||
| 610 | tag->name[1] == 'h' && | ||
| 611 | g_ascii_isdigit(tag->name[1]))) { | ||
| 612 | if (!parser->empty_line) { | ||
| 613 | parser->space = FALSE; | ||
| 614 | if (!parser->newline) html_append_char(parser, '\n'); | ||
| 615 | html_append_char(parser, '\n'); | ||
| 616 | } | ||
| 617 | parser->state = HTML_NORMAL; | ||
| 618 | } else if (!strcmp(tag->name, "/div") || | ||
| 619 | !strcmp(tag->name, "/ul") || | ||
| 620 | !strcmp(tag->name, "/li")) { | ||
| 621 | if (!parser->newline) { | ||
| 622 | parser->space = FALSE; | ||
| 623 | html_append_char(parser, '\n'); | ||
| 624 | } | ||
| 625 | parser->state = HTML_NORMAL; | ||
| 626 | } | ||
| 627 | |||
| 628 | html_free_tag(tag); | ||
| 629 | |||
| 630 | return parser->state; | ||
| 631 | } | ||
| 632 | |||
| 633 | static void html_parse_special(HTMLParser *parser) | ||
| 634 | { | ||
| 635 | gchar symbol_name[9]; | ||
| 636 | gint n; | ||
| 637 | const gchar *val; | ||
| 638 | |||
| 639 | parser->state = HTML_UNKNOWN; | ||
| 640 | g_return_if_fail(*parser->bufp == '&'); | ||
| 641 | |||
| 642 | /* &foo; */ | ||
| 643 | for (n = 0; parser->bufp[n] != '\0' && parser->bufp[n] != ';'; n++) | ||
| 644 | ; | ||
| 645 | if (n > 7 || parser->bufp[n] != ';') { | ||
| 646 | /* output literal `&' */ | ||
| 647 | html_append_char(parser, *parser->bufp++); | ||
| 648 | parser->state = HTML_NORMAL; | ||
| 649 | return; | ||
| 650 | } | ||
| 651 | strncpy2(symbol_name, parser->bufp, n + 2); | ||
| 652 | parser->bufp += n + 1; | ||
| 653 | |||
| 654 | if ((val = g_hash_table_lookup(parser->symbol_table, symbol_name)) | ||
| 655 | != NULL) { | ||
| 656 | html_append_str(parser, val, -1); | ||
| 657 | parser->state = HTML_NORMAL; | ||
| 658 | return; | ||
| 659 | } else if (symbol_name[1] == '#' && g_ascii_isdigit(symbol_name[2])) { | ||
| 660 | gint ch; | ||
| 661 | |||
| 662 | ch = atoi(symbol_name + 2); | ||
| 663 | if (ch < 128 && g_ascii_isprint(ch)) { | ||
| 664 | html_append_char(parser, ch); | ||
| 665 | parser->state = HTML_NORMAL; | ||
| 666 | return; | ||
| 667 | } else { | ||
| 668 | /* ISO 10646 to UTF-8 */ | ||
| 669 | gchar buf[6]; | ||
| 670 | gint len; | ||
| 671 | |||
| 672 | len = g_unichar_to_utf8((gunichar)ch, buf); | ||
| 673 | if (len > 0) { | ||
| 674 | html_append_str(parser, buf, len); | ||
| 675 | parser->state = HTML_NORMAL; | ||
| 676 | return; | ||
| 677 | } | ||
| 678 | } | ||
| 679 | } | ||
| 680 | |||
| 681 | html_append_str(parser, symbol_name, -1); | ||
| 682 | } | ||
| 683 | |||
| 684 | static void html_get_parenthesis(HTMLParser *parser, gchar *buf, gint len) | ||
| 685 | { | ||
| 686 | gchar *p; | ||
| 687 | |||
| 688 | buf[0] = '\0'; | ||
| 689 | g_return_if_fail(*parser->bufp == '<'); | ||
| 690 | |||
| 691 | /* ignore comment / CSS / script stuff */ | ||
| 692 | if (!strncmp(parser->bufp, "<!--", 4)) { | ||
| 693 | parser->bufp += 4; | ||
| 694 | if ((p = html_find_str(parser, "-->")) != NULL) | ||
| 695 | parser->bufp = p + 3; | ||
| 696 | return; | ||
| 697 | } | ||
| 698 | if (!g_ascii_strncasecmp(parser->bufp, "<style", 6)) { | ||
| 699 | parser->bufp += 6; | ||
| 700 | if ((p = html_find_str_case(parser, "</style")) != NULL) { | ||
| 701 | parser->bufp = p + 7; | ||
| 702 | if ((p = html_find_char(parser, '>')) != NULL) | ||
| 703 | parser->bufp = p + 1; | ||
| 704 | } | ||
| 705 | return; | ||
| 706 | } | ||
| 707 | if (!g_ascii_strncasecmp(parser->bufp, "<script", 7)) { | ||
| 708 | parser->bufp += 7; | ||
| 709 | if ((p = html_find_str_case(parser, "</script")) != NULL) { | ||
| 710 | parser->bufp = p + 8; | ||
| 711 | if ((p = html_find_char(parser, '>')) != NULL) | ||
| 712 | parser->bufp = p + 1; | ||
| 713 | } | ||
| 714 | return; | ||
| 715 | } | ||
| 716 | |||
| 717 | parser->bufp++; | ||
| 718 | if ((p = html_find_char(parser, '>')) == NULL) | ||
| 719 | return; | ||
| 720 | |||
| 721 | strncpy2(buf, parser->bufp, MIN(p - parser->bufp + 1, len)); | ||
| 722 | g_strstrip(buf); | ||
| 723 | parser->bufp = p + 1; | ||
| 724 | } | ||
| 725 | |||
| 726 | static gchar *html_unescape_str(HTMLParser *parser, const gchar *str) | ||
| 727 | { | ||
| 728 | const gchar *p = str; | ||
| 729 | gchar symbol_name[9]; | ||
| 730 | gint n; | ||
| 731 | const gchar *val; | ||
| 732 | gchar *unescape_str; | ||
| 733 | gchar *up; | ||
| 734 | |||
| 735 | if (!str) | ||
| 736 | return NULL; | ||
| 737 | |||
| 738 | up = unescape_str = g_malloc(strlen(str) + 1); | ||
| 739 | |||
| 740 | while (*p != '\0') { | ||
| 741 | switch (*p) { | ||
| 742 | case '&': | ||
| 743 | for (n = 0; p[n] != '\0' && p[n] != ';'; n++) | ||
| 744 | ; | ||
| 745 | if (n > 7 || p[n] != ';') { | ||
| 746 | *up++ = *p++; | ||
| 747 | break; | ||
| 748 | } | ||
| 749 | strncpy2(symbol_name, p, n + 2); | ||
| 750 | p += n + 1; | ||
| 751 | |||
| 752 | if ((val = g_hash_table_lookup(parser->symbol_table, symbol_name)) != NULL) { | ||
| 753 | gint len = strlen(val); | ||
| 754 | if (len <= n + 1) { | ||
| 755 | strcpy(up, val); | ||
| 756 | up += len; | ||
| 757 | } else { | ||
| 758 | strcpy(up, symbol_name); | ||
| 759 | up += n + 1; | ||
| 760 | } | ||
| 761 | } else if (symbol_name[1] == '#' && g_ascii_isdigit(symbol_name[2])) { | ||
| 762 | gint ch; | ||
| 763 | |||
| 764 | ch = atoi(symbol_name + 2); | ||
| 765 | if (ch < 128 && g_ascii_isprint(ch)) { | ||
| 766 | *up++ = ch; | ||
| 767 | } else { | ||
| 768 | /* ISO 10646 to UTF-8 */ | ||
| 769 | gchar buf[6]; | ||
| 770 | gint len; | ||
| 771 | |||
| 772 | len = g_unichar_to_utf8((gunichar)ch, buf); | ||
| 773 | if (len > 0 && len <= 6 && len <= n + 1) { | ||
| 774 | memcpy(up, buf, len); | ||
| 775 | up += len; | ||
| 776 | } else { | ||
| 777 | strcpy(up, symbol_name); | ||
| 778 | up += n + 1; | ||
| 779 | } | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | break; | ||
| 784 | default: | ||
| 785 | *up++ = *p++; | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | *up = '\0'; | ||
| 790 | return unescape_str; | ||
| 791 | } | ||
diff --git a/libsylph/html.h b/libsylph/html.h new file mode 100644 index 0000000..bdf9257 --- /dev/null +++ b/libsylph/html.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __HTML_H__ | ||
| 21 | #define __HTML_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | |||
| 26 | #include "codeconv.h" | ||
| 27 | |||
| 28 | typedef enum | ||
| 29 | { | ||
| 30 | HTML_NORMAL, | ||
| 31 | HTML_PAR, | ||
| 32 | HTML_BR, | ||
| 33 | HTML_HR, | ||
| 34 | HTML_HREF, | ||
| 35 | HTML_IMG, | ||
| 36 | HTML_FONT, | ||
| 37 | HTML_PRE, | ||
| 38 | HTML_UNKNOWN, | ||
| 39 | HTML_CONV_FAILED, | ||
| 40 | HTML_ERR, | ||
| 41 | HTML_EOF, | ||
| 42 | HTML_BLOCKQUOTE | ||
| 43 | } HTMLState; | ||
| 44 | |||
| 45 | typedef struct _HTMLParser HTMLParser; | ||
| 46 | typedef struct _HTMLAttr HTMLAttr; | ||
| 47 | typedef struct _HTMLTag HTMLTag; | ||
| 48 | |||
| 49 | struct _HTMLParser | ||
| 50 | { | ||
| 51 | FILE *fp; | ||
| 52 | CodeConverter *conv; | ||
| 53 | |||
| 54 | GHashTable *symbol_table; | ||
| 55 | |||
| 56 | GString *str; | ||
| 57 | GString *buf; | ||
| 58 | |||
| 59 | gchar *bufp; | ||
| 60 | |||
| 61 | HTMLState state; | ||
| 62 | |||
| 63 | gchar *href; | ||
| 64 | |||
| 65 | gboolean newline; | ||
| 66 | gboolean empty_line; | ||
| 67 | gboolean space; | ||
| 68 | gboolean pre; | ||
| 69 | gint blockquote; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct _HTMLAttr | ||
| 73 | { | ||
| 74 | gchar *name; | ||
| 75 | gchar *value; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct _HTMLTag | ||
| 79 | { | ||
| 80 | gchar *name; | ||
| 81 | GList *attr; | ||
| 82 | }; | ||
| 83 | |||
| 84 | HTMLParser *html_parser_new (FILE *fp, | ||
| 85 | CodeConverter *conv); | ||
| 86 | void html_parser_destroy (HTMLParser *parser); | ||
| 87 | const gchar *html_parse (HTMLParser *parser); | ||
| 88 | |||
| 89 | #endif /* __HTML_H__ */ | ||
diff --git a/libsylph/imap.c b/libsylph/imap.c new file mode 100644 index 0000000..aa0d737 --- /dev/null +++ b/libsylph/imap.c | |||
| @@ -0,0 +1,5215 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2013 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <dirent.h> | ||
| 32 | #include <unistd.h> | ||
| 33 | #include <ctype.h> | ||
| 34 | #include <time.h> | ||
| 35 | #if HAVE_ICONV | ||
| 36 | # include <iconv.h> | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #include "sylmain.h" | ||
| 40 | #include "imap.h" | ||
| 41 | #include "socket.h" | ||
| 42 | #include "socks.h" | ||
| 43 | #include "ssl.h" | ||
| 44 | #include "recv.h" | ||
| 45 | #include "procmsg.h" | ||
| 46 | #include "procheader.h" | ||
| 47 | #include "folder.h" | ||
| 48 | #include "prefs_account.h" | ||
| 49 | #include "codeconv.h" | ||
| 50 | #include "md5_hmac.h" | ||
| 51 | #include "base64.h" | ||
| 52 | #include "utils.h" | ||
| 53 | #include "prefs_common.h" | ||
| 54 | #include "virtual.h" | ||
| 55 | |||
| 56 | #define IMAP4_PORT 143 | ||
| 57 | #if USE_SSL | ||
| 58 | #define IMAPS_PORT 993 | ||
| 59 | #endif | ||
| 60 | |||
| 61 | #define IMAP_COPY_LIMIT 200 | ||
| 62 | #define IMAP_CMD_LIMIT 1000 | ||
| 63 | |||
| 64 | #define QUOTE_IF_REQUIRED(out, str) \ | ||
| 65 | { \ | ||
| 66 | if (!str || *str == '\0') { \ | ||
| 67 | Xstrdup_a(out, "\"\"", return IMAP_ERROR); \ | ||
| 68 | } else if (strpbrk(str, " \t(){}[]%&*\"\\") != NULL) { \ | ||
| 69 | gchar *__tmp; \ | ||
| 70 | gint len; \ | ||
| 71 | const gchar *p; \ | ||
| 72 | gchar *tp; \ | ||
| 73 | \ | ||
| 74 | len = strlen(str) * 2 + 3; \ | ||
| 75 | Xalloca(__tmp, len, return IMAP_ERROR); \ | ||
| 76 | tp = __tmp; \ | ||
| 77 | *tp++ = '\"'; \ | ||
| 78 | for (p = str; *p != '\0'; p++) { \ | ||
| 79 | if (*p == '\"' || *p == '\\') \ | ||
| 80 | *tp++ = '\\'; \ | ||
| 81 | *tp++ = *p; \ | ||
| 82 | } \ | ||
| 83 | *tp++ = '\"'; \ | ||
| 84 | *tp = '\0'; \ | ||
| 85 | out = __tmp; \ | ||
| 86 | } else { \ | ||
| 87 | Xstrdup_a(out, str, return IMAP_ERROR); \ | ||
| 88 | } \ | ||
| 89 | } | ||
| 90 | |||
| 91 | typedef gint (*IMAPThreadFunc) (IMAPSession *session, | ||
| 92 | gpointer data); | ||
| 93 | typedef gint (*IMAPProgressFunc) (IMAPSession *session, | ||
| 94 | gint count, | ||
| 95 | gint total, | ||
| 96 | gpointer data); | ||
| 97 | |||
| 98 | typedef struct _IMAPRealSession | ||
| 99 | { | ||
| 100 | IMAPSession imap_session; | ||
| 101 | #if USE_THREADS | ||
| 102 | GThreadPool *pool; | ||
| 103 | IMAPThreadFunc thread_func; | ||
| 104 | gpointer thread_data; | ||
| 105 | gboolean is_running; | ||
| 106 | gint prog_count; | ||
| 107 | gint prog_total; | ||
| 108 | gint flag; | ||
| 109 | gint retval; | ||
| 110 | #endif | ||
| 111 | } IMAPRealSession; | ||
| 112 | |||
| 113 | static GList *session_list = NULL; | ||
| 114 | |||
| 115 | static void imap_folder_init (Folder *folder, | ||
| 116 | const gchar *name, | ||
| 117 | const gchar *path); | ||
| 118 | |||
| 119 | static Folder *imap_folder_new (const gchar *name, | ||
| 120 | const gchar *path); | ||
| 121 | static void imap_folder_destroy (Folder *folder); | ||
| 122 | |||
| 123 | static Session *imap_session_new (PrefsAccount *account); | ||
| 124 | static gint imap_session_connect (IMAPSession *session); | ||
| 125 | static gint imap_session_reconnect (IMAPSession *session); | ||
| 126 | static void imap_session_destroy (Session *session); | ||
| 127 | /* static void imap_session_destroy_all (void); */ | ||
| 128 | |||
| 129 | static gint imap_search_flags (IMAPSession *session, | ||
| 130 | GArray **uids, | ||
| 131 | GHashTable **flags_table); | ||
| 132 | static gint imap_fetch_flags (IMAPSession *session, | ||
| 133 | GArray **uids, | ||
| 134 | GHashTable **flags_table); | ||
| 135 | |||
| 136 | static GSList *imap_get_msg_list (Folder *folder, | ||
| 137 | FolderItem *item, | ||
| 138 | gboolean use_cache); | ||
| 139 | static GSList *imap_get_uncached_msg_list | ||
| 140 | (Folder *folder, | ||
| 141 | FolderItem *item); | ||
| 142 | static gchar *imap_fetch_msg (Folder *folder, | ||
| 143 | FolderItem *item, | ||
| 144 | gint uid); | ||
| 145 | static MsgInfo *imap_get_msginfo (Folder *folder, | ||
| 146 | FolderItem *item, | ||
| 147 | gint uid); | ||
| 148 | static gint imap_add_msg (Folder *folder, | ||
| 149 | FolderItem *dest, | ||
| 150 | const gchar *file, | ||
| 151 | MsgFlags *flags, | ||
| 152 | gboolean remove_source); | ||
| 153 | static gint imap_add_msgs (Folder *folder, | ||
| 154 | FolderItem *dest, | ||
| 155 | GSList *file_list, | ||
| 156 | gboolean remove_source, | ||
| 157 | gint *first); | ||
| 158 | static gint imap_add_msg_msginfo (Folder *folder, | ||
| 159 | FolderItem *dest, | ||
| 160 | MsgInfo *msginfo, | ||
| 161 | gboolean remove_source); | ||
| 162 | static gint imap_add_msgs_msginfo (Folder *folder, | ||
| 163 | FolderItem *dest, | ||
| 164 | GSList *msglist, | ||
| 165 | gboolean remove_source, | ||
| 166 | gint *first); | ||
| 167 | |||
| 168 | static gint imap_move_msg (Folder *folder, | ||
| 169 | FolderItem *dest, | ||
| 170 | MsgInfo *msginfo); | ||
| 171 | static gint imap_move_msgs (Folder *folder, | ||
| 172 | FolderItem *dest, | ||
| 173 | GSList *msglist); | ||
| 174 | static gint imap_copy_msg (Folder *folder, | ||
| 175 | FolderItem *dest, | ||
| 176 | MsgInfo *msginfo); | ||
| 177 | static gint imap_copy_msgs (Folder *folder, | ||
| 178 | FolderItem *dest, | ||
| 179 | GSList *msglist); | ||
| 180 | |||
| 181 | static gint imap_remove_msg (Folder *folder, | ||
| 182 | FolderItem *item, | ||
| 183 | MsgInfo *msginfo); | ||
| 184 | static gint imap_remove_msgs (Folder *folder, | ||
| 185 | FolderItem *item, | ||
| 186 | GSList *msglist); | ||
| 187 | static gint imap_remove_all_msg (Folder *folder, | ||
| 188 | FolderItem *item); | ||
| 189 | |||
| 190 | static gboolean imap_is_msg_changed (Folder *folder, | ||
| 191 | FolderItem *item, | ||
| 192 | MsgInfo *msginfo); | ||
| 193 | |||
| 194 | static gint imap_close (Folder *folder, | ||
| 195 | FolderItem *item); | ||
| 196 | |||
| 197 | static gint imap_scan_folder (Folder *folder, | ||
| 198 | FolderItem *item); | ||
| 199 | static gint imap_scan_tree (Folder *folder); | ||
| 200 | |||
| 201 | static gint imap_create_tree (Folder *folder); | ||
| 202 | |||
| 203 | static FolderItem *imap_create_folder (Folder *folder, | ||
| 204 | FolderItem *parent, | ||
| 205 | const gchar *name); | ||
| 206 | static gint imap_rename_folder (Folder *folder, | ||
| 207 | FolderItem *item, | ||
| 208 | const gchar *name); | ||
| 209 | static gint imap_move_folder (Folder *folder, | ||
| 210 | FolderItem *item, | ||
| 211 | FolderItem *new_parent); | ||
| 212 | static gint imap_remove_folder (Folder *folder, | ||
| 213 | FolderItem *item); | ||
| 214 | |||
| 215 | static IMAPSession *imap_session_get (Folder *folder); | ||
| 216 | |||
| 217 | static gint imap_greeting (IMAPSession *session); | ||
| 218 | static gint imap_auth (IMAPSession *session, | ||
| 219 | const gchar *user, | ||
| 220 | const gchar *pass, | ||
| 221 | IMAPAuthType type); | ||
| 222 | |||
| 223 | static gint imap_scan_tree_recursive (IMAPSession *session, | ||
| 224 | FolderItem *item, | ||
| 225 | GSList *item_list); | ||
| 226 | static GSList *imap_get_folder_list (IMAPSession *session, | ||
| 227 | FolderItem *item); | ||
| 228 | static GSList *imap_parse_list (IMAPSession *session, | ||
| 229 | const gchar *real_path, | ||
| 230 | gchar *separator); | ||
| 231 | static GSList *imap_add_inter_folders (GSList *item_list, | ||
| 232 | const gchar *root_path); | ||
| 233 | static GSList *imap_get_part_folder_list(GSList *item_list, | ||
| 234 | FolderItem *item); | ||
| 235 | |||
| 236 | static void imap_create_missing_folders (Folder *folder); | ||
| 237 | static FolderItem *imap_create_special_folder | ||
| 238 | (Folder *folder, | ||
| 239 | SpecialFolderItemType stype, | ||
| 240 | const gchar *name); | ||
| 241 | |||
| 242 | static gint imap_do_copy_msgs (Folder *folder, | ||
| 243 | FolderItem *dest, | ||
| 244 | GSList *msglist, | ||
| 245 | gboolean remove_source); | ||
| 246 | static gint imap_remove_msgs_by_seq_set (Folder *folder, | ||
| 247 | FolderItem *item, | ||
| 248 | GSList *seq_list); | ||
| 249 | |||
| 250 | static GSList *imap_get_uncached_messages (IMAPSession *session, | ||
| 251 | FolderItem *item, | ||
| 252 | guint32 first_uid, | ||
| 253 | guint32 last_uid, | ||
| 254 | gint exists, | ||
| 255 | gboolean update_count); | ||
| 256 | static void imap_delete_cached_message (FolderItem *item, | ||
| 257 | guint32 uid); | ||
| 258 | static GSList *imap_delete_cached_messages (GSList *mlist, | ||
| 259 | FolderItem *item, | ||
| 260 | guint32 first_uid, | ||
| 261 | guint32 last_uid); | ||
| 262 | static void imap_delete_all_cached_messages (FolderItem *item); | ||
| 263 | |||
| 264 | #if USE_SSL | ||
| 265 | static SockInfo *imap_open (const gchar *server, | ||
| 266 | gushort port, | ||
| 267 | SocksInfo *socks_info, | ||
| 268 | SSLType ssl_type); | ||
| 269 | #else | ||
| 270 | static SockInfo *imap_open (const gchar *server, | ||
| 271 | gushort port, | ||
| 272 | SocksInfo *socks_info); | ||
| 273 | #endif | ||
| 274 | |||
| 275 | static gint imap_msg_list_change_perm_flags (GSList *msglist, | ||
| 276 | MsgPermFlags flags, | ||
| 277 | gboolean is_set); | ||
| 278 | static gchar *imap_get_flag_str (IMAPFlags flags); | ||
| 279 | static gint imap_set_message_flags (IMAPSession *session, | ||
| 280 | const gchar *seq_set, | ||
| 281 | IMAPFlags flags, | ||
| 282 | gboolean is_set); | ||
| 283 | static gint imap_select (IMAPSession *session, | ||
| 284 | IMAPFolder *folder, | ||
| 285 | const gchar *path, | ||
| 286 | gint *exists, | ||
| 287 | gint *recent, | ||
| 288 | gint *unseen, | ||
| 289 | guint32 *uid_validity); | ||
| 290 | static gint imap_status (IMAPSession *session, | ||
| 291 | IMAPFolder *folder, | ||
| 292 | const gchar *path, | ||
| 293 | gint *messages, | ||
| 294 | gint *recent, | ||
| 295 | guint32 *uid_next, | ||
| 296 | guint32 *uid_validity, | ||
| 297 | gint *unseen); | ||
| 298 | |||
| 299 | static void imap_parse_namespace (IMAPSession *session, | ||
| 300 | IMAPFolder *folder); | ||
| 301 | static void imap_get_namespace_by_list (IMAPSession *session, | ||
| 302 | IMAPFolder *folder); | ||
| 303 | static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder, | ||
| 304 | const gchar *path); | ||
| 305 | static gchar imap_get_path_separator (IMAPFolder *folder, | ||
| 306 | const gchar *path); | ||
| 307 | static gchar *imap_get_real_path (IMAPFolder *folder, | ||
| 308 | const gchar *path); | ||
| 309 | |||
| 310 | static gchar *imap_parse_atom (IMAPSession *session, | ||
| 311 | gchar *src, | ||
| 312 | gchar *dest, | ||
| 313 | gint dest_len, | ||
| 314 | GString *str); | ||
| 315 | static MsgFlags imap_parse_flags (const gchar *flag_str); | ||
| 316 | static IMAPFlags imap_parse_imap_flags (const gchar *flag_str); | ||
| 317 | static MsgInfo *imap_parse_envelope (IMAPSession *session, | ||
| 318 | FolderItem *item, | ||
| 319 | GString *line_str); | ||
| 320 | |||
| 321 | static gboolean imap_has_capability (IMAPSession *session, | ||
| 322 | const gchar *capability); | ||
| 323 | static void imap_capability_free (IMAPSession *session); | ||
| 324 | |||
| 325 | /* low-level IMAP4rev1 commands */ | ||
| 326 | static gint imap_cmd_capability (IMAPSession *session); | ||
| 327 | static gint imap_cmd_authenticate | ||
| 328 | (IMAPSession *session, | ||
| 329 | const gchar *user, | ||
| 330 | const gchar *pass, | ||
| 331 | IMAPAuthType type); | ||
| 332 | static gint imap_cmd_login (IMAPSession *session, | ||
| 333 | const gchar *user, | ||
| 334 | const gchar *pass); | ||
| 335 | static gint imap_cmd_logout (IMAPSession *session); | ||
| 336 | static gint imap_cmd_noop (IMAPSession *session); | ||
| 337 | #if USE_SSL | ||
| 338 | static gint imap_cmd_starttls (IMAPSession *session); | ||
| 339 | #endif | ||
| 340 | static gint imap_cmd_namespace (IMAPSession *session, | ||
| 341 | gchar **ns_str); | ||
| 342 | static gint imap_cmd_list (IMAPSession *session, | ||
| 343 | const gchar *ref, | ||
| 344 | const gchar *mailbox, | ||
| 345 | GPtrArray *argbuf); | ||
| 346 | static gint imap_cmd_do_select (IMAPSession *session, | ||
| 347 | const gchar *folder, | ||
| 348 | gboolean examine, | ||
| 349 | gint *exists, | ||
| 350 | gint *recent, | ||
| 351 | gint *unseen, | ||
| 352 | guint32 *uid_validity); | ||
| 353 | static gint imap_cmd_select (IMAPSession *session, | ||
| 354 | const gchar *folder, | ||
| 355 | gint *exists, | ||
| 356 | gint *recent, | ||
| 357 | gint *unseen, | ||
| 358 | guint32 *uid_validity); | ||
| 359 | static gint imap_cmd_examine (IMAPSession *session, | ||
| 360 | const gchar *folder, | ||
| 361 | gint *exists, | ||
| 362 | gint *recent, | ||
| 363 | gint *unseen, | ||
| 364 | guint32 *uid_validity); | ||
| 365 | static gint imap_cmd_create (IMAPSession *session, | ||
| 366 | const gchar *folder); | ||
| 367 | static gint imap_cmd_rename (IMAPSession *session, | ||
| 368 | const gchar *oldfolder, | ||
| 369 | const gchar *newfolder); | ||
| 370 | static gint imap_cmd_delete (IMAPSession *session, | ||
| 371 | const gchar *folder); | ||
| 372 | static gint imap_cmd_subscribe (IMAPSession *session, | ||
| 373 | const gchar *folder); | ||
| 374 | static gint imap_cmd_envelope (IMAPSession *session, | ||
| 375 | const gchar *seq_set); | ||
| 376 | static gint imap_cmd_search (IMAPSession *session, | ||
| 377 | const gchar *criteria, | ||
| 378 | GArray **result); | ||
| 379 | static gint imap_cmd_fetch (IMAPSession *session, | ||
| 380 | guint32 uid, | ||
| 381 | const gchar *filename); | ||
| 382 | static gint imap_cmd_append (IMAPSession *session, | ||
| 383 | const gchar *destfolder, | ||
| 384 | const gchar *file, | ||
| 385 | IMAPFlags flags, | ||
| 386 | guint32 *new_uid); | ||
| 387 | static gint imap_cmd_copy (IMAPSession *session, | ||
| 388 | const gchar *seq_set, | ||
| 389 | const gchar *destfolder); | ||
| 390 | static gint imap_cmd_store (IMAPSession *session, | ||
| 391 | const gchar *seq_set, | ||
| 392 | const gchar *sub_cmd); | ||
| 393 | static gint imap_cmd_expunge (IMAPSession *session); | ||
| 394 | static gint imap_cmd_close (IMAPSession *session); | ||
| 395 | |||
| 396 | static gint imap_cmd_ok (IMAPSession *session, | ||
| 397 | GPtrArray *argbuf); | ||
| 398 | static gint imap_cmd_ok_real (IMAPSession *session, | ||
| 399 | GPtrArray *argbuf); | ||
| 400 | static gint imap_cmd_gen_send (IMAPSession *session, | ||
| 401 | const gchar *format, ...); | ||
| 402 | static gint imap_cmd_gen_recv (IMAPSession *session, | ||
| 403 | gchar **ret); | ||
| 404 | |||
| 405 | static gint imap_cmd_gen_recv_silent (IMAPSession *session, | ||
| 406 | gchar **ret); | ||
| 407 | |||
| 408 | /* misc utility functions */ | ||
| 409 | static gchar *strchr_cpy (const gchar *src, | ||
| 410 | gchar ch, | ||
| 411 | gchar *dest, | ||
| 412 | gint len); | ||
| 413 | static gchar *get_quoted (const gchar *src, | ||
| 414 | gchar ch, | ||
| 415 | gchar *dest, | ||
| 416 | gint len); | ||
| 417 | static gchar *search_array_contain_str (GPtrArray *array, | ||
| 418 | gchar *str); | ||
| 419 | static gchar *search_array_str (GPtrArray *array, | ||
| 420 | gchar *str); | ||
| 421 | static void imap_path_separator_subst (gchar *str, | ||
| 422 | gchar separator); | ||
| 423 | |||
| 424 | static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str); | ||
| 425 | static gchar *imap_utf8_to_modified_utf7 (const gchar *from); | ||
| 426 | |||
| 427 | static GSList *imap_get_seq_set_from_msglist (GSList *msglist, | ||
| 428 | gint limit); | ||
| 429 | static gint imap_seq_set_get_count (const gchar *seq_set); | ||
| 430 | static void imap_seq_set_free (GSList *seq_list); | ||
| 431 | |||
| 432 | static GHashTable *imap_get_uid_table (GArray *array); | ||
| 433 | |||
| 434 | static gboolean imap_rename_folder_func (GNode *node, | ||
| 435 | gpointer data); | ||
| 436 | |||
| 437 | #if USE_THREADS | ||
| 438 | static gint imap_thread_run (IMAPSession *session, | ||
| 439 | IMAPThreadFunc func, | ||
| 440 | gpointer data); | ||
| 441 | static gint imap_thread_run_progress (IMAPSession *session, | ||
| 442 | IMAPThreadFunc func, | ||
| 443 | IMAPProgressFunc progress_func, | ||
| 444 | gpointer data); | ||
| 445 | #endif | ||
| 446 | |||
| 447 | static FolderClass imap_class = | ||
| 448 | { | ||
| 449 | F_IMAP, | ||
| 450 | |||
| 451 | imap_folder_new, | ||
| 452 | imap_folder_destroy, | ||
| 453 | |||
| 454 | imap_scan_tree, | ||
| 455 | imap_create_tree, | ||
| 456 | |||
| 457 | imap_get_msg_list, | ||
| 458 | imap_get_uncached_msg_list, | ||
| 459 | imap_fetch_msg, | ||
| 460 | imap_get_msginfo, | ||
| 461 | imap_add_msg, | ||
| 462 | imap_add_msgs, | ||
| 463 | imap_add_msg_msginfo, | ||
| 464 | imap_add_msgs_msginfo, | ||
| 465 | imap_move_msg, | ||
| 466 | imap_move_msgs, | ||
| 467 | imap_copy_msg, | ||
| 468 | imap_copy_msgs, | ||
| 469 | imap_remove_msg, | ||
| 470 | imap_remove_msgs, | ||
| 471 | imap_remove_all_msg, | ||
| 472 | imap_is_msg_changed, | ||
| 473 | imap_close, | ||
| 474 | imap_scan_folder, | ||
| 475 | |||
| 476 | imap_create_folder, | ||
| 477 | imap_rename_folder, | ||
| 478 | imap_move_folder, | ||
| 479 | imap_remove_folder | ||
| 480 | }; | ||
| 481 | |||
| 482 | |||
| 483 | FolderClass *imap_get_class(void) | ||
| 484 | { | ||
| 485 | return &imap_class; | ||
| 486 | } | ||
| 487 | |||
| 488 | static Folder *imap_folder_new(const gchar *name, const gchar *path) | ||
| 489 | { | ||
| 490 | Folder *folder; | ||
| 491 | |||
| 492 | folder = (Folder *)g_new0(IMAPFolder, 1); | ||
| 493 | imap_folder_init(folder, name, path); | ||
| 494 | |||
| 495 | return folder; | ||
| 496 | } | ||
| 497 | |||
| 498 | static void imap_folder_destroy(Folder *folder) | ||
| 499 | { | ||
| 500 | g_return_if_fail(folder->account != NULL); | ||
| 501 | |||
| 502 | if (REMOTE_FOLDER(folder)->remove_cache_on_destroy) { | ||
| 503 | gchar *dir; | ||
| 504 | gchar *server; | ||
| 505 | |||
| 506 | dir = folder_get_path(folder); | ||
| 507 | if (is_dir_exist(dir)) | ||
| 508 | remove_dir_recursive(dir); | ||
| 509 | g_free(dir); | ||
| 510 | |||
| 511 | server = uriencode_for_filename(folder->account->recv_server); | ||
| 512 | dir = g_strconcat(get_imap_cache_dir(), G_DIR_SEPARATOR_S, | ||
| 513 | server, NULL); | ||
| 514 | if (is_dir_exist(dir)) | ||
| 515 | g_rmdir(dir); | ||
| 516 | g_free(dir); | ||
| 517 | g_free(server); | ||
| 518 | } | ||
| 519 | |||
| 520 | folder_remote_folder_destroy(REMOTE_FOLDER(folder)); | ||
| 521 | } | ||
| 522 | |||
| 523 | static void imap_folder_init(Folder *folder, const gchar *name, | ||
| 524 | const gchar *path) | ||
| 525 | { | ||
| 526 | folder->klass = imap_get_class(); | ||
| 527 | folder_remote_folder_init(folder, name, path); | ||
| 528 | } | ||
| 529 | |||
| 530 | static IMAPSession *imap_session_get(Folder *folder) | ||
| 531 | { | ||
| 532 | RemoteFolder *rfolder = REMOTE_FOLDER(folder); | ||
| 533 | gint ret; | ||
| 534 | |||
| 535 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 536 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL); | ||
| 537 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 538 | |||
| 539 | if (!prefs_common.online_mode) | ||
| 540 | return NULL; | ||
| 541 | |||
| 542 | if (!rfolder->session) { | ||
| 543 | rfolder->session = imap_session_new(folder->account); | ||
| 544 | if (rfolder->session) | ||
| 545 | imap_parse_namespace(IMAP_SESSION(rfolder->session), | ||
| 546 | IMAP_FOLDER(folder)); | ||
| 547 | return IMAP_SESSION(rfolder->session); | ||
| 548 | } | ||
| 549 | |||
| 550 | if (imap_is_session_active(IMAP_FOLDER(folder))) { | ||
| 551 | g_warning("imap_session_get: session is busy."); | ||
| 552 | return NULL; | ||
| 553 | } | ||
| 554 | |||
| 555 | if (time(NULL) - rfolder->session->last_access_time < | ||
| 556 | SESSION_TIMEOUT_INTERVAL) { | ||
| 557 | return IMAP_SESSION(rfolder->session); | ||
| 558 | } | ||
| 559 | |||
| 560 | if ((ret = imap_cmd_noop(IMAP_SESSION(rfolder->session))) != IMAP_SUCCESS) { | ||
| 561 | if (ret == IMAP_EAGAIN) { | ||
| 562 | g_warning("imap_session_get: session is busy."); | ||
| 563 | return NULL; | ||
| 564 | } | ||
| 565 | |||
| 566 | log_warning(_("IMAP4 connection to %s has been" | ||
| 567 | " disconnected. Reconnecting...\n"), | ||
| 568 | folder->account->recv_server); | ||
| 569 | if (imap_session_reconnect(IMAP_SESSION(rfolder->session)) | ||
| 570 | == IMAP_SUCCESS) | ||
| 571 | imap_parse_namespace(IMAP_SESSION(rfolder->session), | ||
| 572 | IMAP_FOLDER(folder)); | ||
| 573 | else { | ||
| 574 | session_destroy(rfolder->session); | ||
| 575 | rfolder->session = NULL; | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | return IMAP_SESSION(rfolder->session); | ||
| 580 | } | ||
| 581 | |||
| 582 | static gint imap_greeting(IMAPSession *session) | ||
| 583 | { | ||
| 584 | gchar *greeting; | ||
| 585 | gint ok; | ||
| 586 | |||
| 587 | if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS) { | ||
| 588 | log_warning("Cannot get greeting message (%d)\n", ok); | ||
| 589 | return ok; | ||
| 590 | } | ||
| 591 | |||
| 592 | if (greeting[0] != '*' || greeting[1] != ' ') | ||
| 593 | ok = IMAP_ERROR; | ||
| 594 | else if (!strncmp(greeting + 2, "OK", 2)) | ||
| 595 | ok = IMAP_SUCCESS; | ||
| 596 | else if (!strncmp(greeting + 2, "PREAUTH", 7)) { | ||
| 597 | session->authenticated = TRUE; | ||
| 598 | ok = IMAP_SUCCESS; | ||
| 599 | } else | ||
| 600 | ok = IMAP_ERROR; | ||
| 601 | |||
| 602 | g_free(greeting); | ||
| 603 | return ok; | ||
| 604 | } | ||
| 605 | |||
| 606 | static gint imap_auth(IMAPSession *session, const gchar *user, | ||
| 607 | const gchar *pass, IMAPAuthType type) | ||
| 608 | { | ||
| 609 | gboolean nologin; | ||
| 610 | gint ok = IMAP_AUTHFAIL; | ||
| 611 | |||
| 612 | nologin = imap_has_capability(session, "LOGINDISABLED"); | ||
| 613 | |||
| 614 | switch (type) { | ||
| 615 | case 0: | ||
| 616 | if (imap_has_capability(session, "AUTH=CRAM-MD5")) | ||
| 617 | ok = imap_cmd_authenticate(session, user, pass, | ||
| 618 | IMAP_AUTH_CRAM_MD5); | ||
| 619 | else if (imap_has_capability(session, "AUTH=PLAIN")) | ||
| 620 | ok = imap_cmd_authenticate(session, user, pass, | ||
| 621 | IMAP_AUTH_PLAIN); | ||
| 622 | else if (nologin) | ||
| 623 | log_print(_("IMAP4 server disables LOGIN.\n")); | ||
| 624 | else | ||
| 625 | ok = imap_cmd_login(session, user, pass); | ||
| 626 | break; | ||
| 627 | case IMAP_AUTH_LOGIN: | ||
| 628 | if (nologin) | ||
| 629 | log_warning(_("IMAP4 server disables LOGIN.\n")); | ||
| 630 | else | ||
| 631 | ok = imap_cmd_login(session, user, pass); | ||
| 632 | break; | ||
| 633 | case IMAP_AUTH_CRAM_MD5: | ||
| 634 | case IMAP_AUTH_PLAIN: | ||
| 635 | ok = imap_cmd_authenticate(session, user, pass, type); | ||
| 636 | break; | ||
| 637 | default: | ||
| 638 | break; | ||
| 639 | } | ||
| 640 | |||
| 641 | if (ok == IMAP_SUCCESS) | ||
| 642 | session->authenticated = TRUE; | ||
| 643 | |||
| 644 | return ok; | ||
| 645 | } | ||
| 646 | |||
| 647 | static Session *imap_session_new(PrefsAccount *account) | ||
| 648 | { | ||
| 649 | IMAPSession *session; | ||
| 650 | gushort port; | ||
| 651 | |||
| 652 | g_return_val_if_fail(account != NULL, NULL); | ||
| 653 | g_return_val_if_fail(account->recv_server != NULL, NULL); | ||
| 654 | g_return_val_if_fail(account->userid != NULL, NULL); | ||
| 655 | |||
| 656 | #if USE_SSL | ||
| 657 | port = account->set_imapport ? account->imapport | ||
| 658 | : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT; | ||
| 659 | #else | ||
| 660 | port = account->set_imapport ? account->imapport : IMAP4_PORT; | ||
| 661 | #endif | ||
| 662 | |||
| 663 | session = IMAP_SESSION(g_new0(IMAPRealSession, 1)); | ||
| 664 | |||
| 665 | session_init(SESSION(session)); | ||
| 666 | |||
| 667 | SESSION(session)->type = SESSION_IMAP; | ||
| 668 | SESSION(session)->sock = NULL; | ||
| 669 | SESSION(session)->server = g_strdup(account->recv_server); | ||
| 670 | SESSION(session)->port = port; | ||
| 671 | #if USE_SSL | ||
| 672 | SESSION(session)->ssl_type = account->ssl_imap; | ||
| 673 | #endif | ||
| 674 | SESSION(session)->last_access_time = time(NULL); | ||
| 675 | SESSION(session)->data = account; | ||
| 676 | |||
| 677 | SESSION(session)->destroy = imap_session_destroy; | ||
| 678 | |||
| 679 | session->authenticated = FALSE; | ||
| 680 | session->capability = NULL; | ||
| 681 | session->uidplus = FALSE; | ||
| 682 | session->mbox = NULL; | ||
| 683 | session->cmd_count = 0; | ||
| 684 | |||
| 685 | session_list = g_list_append(session_list, session); | ||
| 686 | |||
| 687 | if (imap_session_connect(session) != IMAP_SUCCESS) { | ||
| 688 | log_warning(_("Could not establish IMAP connection.\n")); | ||
| 689 | session_destroy(SESSION(session)); | ||
| 690 | return NULL; | ||
| 691 | } | ||
| 692 | |||
| 693 | return SESSION(session); | ||
| 694 | } | ||
| 695 | |||
| 696 | static gint imap_session_connect(IMAPSession *session) | ||
| 697 | { | ||
| 698 | SockInfo *sock; | ||
| 699 | SocksInfo *socks_info = NULL; | ||
| 700 | PrefsAccount *account; | ||
| 701 | const gchar *pass; | ||
| 702 | |||
| 703 | g_return_val_if_fail(session != NULL, IMAP_ERROR); | ||
| 704 | |||
| 705 | account = (PrefsAccount *)(SESSION(session)->data); | ||
| 706 | |||
| 707 | log_message(_("creating IMAP4 connection to %s:%d ...\n"), | ||
| 708 | SESSION(session)->server, SESSION(session)->port); | ||
| 709 | |||
| 710 | pass = account->passwd; | ||
| 711 | if (!pass) | ||
| 712 | pass = account->tmp_pass; | ||
| 713 | if (!pass) { | ||
| 714 | gchar *tmp_pass; | ||
| 715 | |||
| 716 | tmp_pass = input_query_password(account->recv_server, | ||
| 717 | account->userid); | ||
| 718 | if (!tmp_pass) | ||
| 719 | return IMAP_ERROR; | ||
| 720 | |||
| 721 | account->tmp_pass = tmp_pass; | ||
| 722 | pass = account->tmp_pass; | ||
| 723 | } | ||
| 724 | |||
| 725 | if (account->use_socks && account->use_socks_for_recv && | ||
| 726 | account->proxy_host) { | ||
| 727 | socks_info = socks_info_new(account->socks_type, account->proxy_host, account->proxy_port, account->use_proxy_auth ? account->proxy_name : NULL, account->use_proxy_auth ? account->proxy_pass : NULL); | ||
| 728 | } | ||
| 729 | |||
| 730 | #if USE_SSL | ||
| 731 | if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port, | ||
| 732 | socks_info, SESSION(session)->ssl_type)) == NULL) | ||
| 733 | #else | ||
| 734 | if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port, | ||
| 735 | socks_info)) | ||
| 736 | == NULL) | ||
| 737 | #endif | ||
| 738 | return IMAP_ERROR; | ||
| 739 | |||
| 740 | if (socks_info) | ||
| 741 | socks_info_free(socks_info); | ||
| 742 | |||
| 743 | SESSION(session)->sock = sock; | ||
| 744 | |||
| 745 | if (imap_greeting(session) != IMAP_SUCCESS) | ||
| 746 | return IMAP_ERROR; | ||
| 747 | if (imap_cmd_capability(session) != IMAP_SUCCESS) | ||
| 748 | return IMAP_ERROR; | ||
| 749 | |||
| 750 | if (imap_has_capability(session, "UIDPLUS")) | ||
| 751 | session->uidplus = TRUE; | ||
| 752 | |||
| 753 | #if USE_SSL | ||
| 754 | if (account->ssl_imap == SSL_STARTTLS && | ||
| 755 | imap_has_capability(session, "STARTTLS")) { | ||
| 756 | gint ok; | ||
| 757 | |||
| 758 | ok = imap_cmd_starttls(session); | ||
| 759 | if (ok != IMAP_SUCCESS) { | ||
| 760 | log_warning(_("Can't start TLS session.\n")); | ||
| 761 | return IMAP_ERROR; | ||
| 762 | } | ||
| 763 | if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) | ||
| 764 | return IMAP_SOCKET; | ||
| 765 | |||
| 766 | /* capability can be changed after STARTTLS */ | ||
| 767 | if (imap_cmd_capability(session) != IMAP_SUCCESS) | ||
| 768 | return IMAP_ERROR; | ||
| 769 | } | ||
| 770 | #endif | ||
| 771 | |||
| 772 | if (!session->authenticated && | ||
| 773 | imap_auth(session, account->userid, pass, account->imap_auth_type) | ||
| 774 | != IMAP_SUCCESS) { | ||
| 775 | if (account->tmp_pass) { | ||
| 776 | g_free(account->tmp_pass); | ||
| 777 | account->tmp_pass = NULL; | ||
| 778 | } | ||
| 779 | imap_cmd_logout(session); | ||
| 780 | return IMAP_AUTHFAIL; | ||
| 781 | } | ||
| 782 | |||
| 783 | return IMAP_SUCCESS; | ||
| 784 | } | ||
| 785 | |||
| 786 | static gint imap_session_reconnect(IMAPSession *session) | ||
| 787 | { | ||
| 788 | g_return_val_if_fail(session != NULL, IMAP_ERROR); | ||
| 789 | |||
| 790 | session_disconnect(SESSION(session)); | ||
| 791 | |||
| 792 | imap_capability_free(session); | ||
| 793 | session->uidplus = FALSE; | ||
| 794 | g_free(session->mbox); | ||
| 795 | session->mbox = NULL; | ||
| 796 | session->authenticated = FALSE; | ||
| 797 | SESSION(session)->state = SESSION_READY; | ||
| 798 | |||
| 799 | return imap_session_connect(session); | ||
| 800 | } | ||
| 801 | |||
| 802 | static void imap_session_destroy(Session *session) | ||
| 803 | { | ||
| 804 | #if USE_THREADS | ||
| 805 | IMAPRealSession *real = (IMAPRealSession *)session; | ||
| 806 | |||
| 807 | if (real->pool) | ||
| 808 | g_thread_pool_free(real->pool, TRUE, TRUE); | ||
| 809 | #endif | ||
| 810 | imap_capability_free(IMAP_SESSION(session)); | ||
| 811 | g_free(IMAP_SESSION(session)->mbox); | ||
| 812 | session_list = g_list_remove(session_list, session); | ||
| 813 | } | ||
| 814 | |||
| 815 | #if 0 | ||
| 816 | static void imap_session_destroy_all(void) | ||
| 817 | { | ||
| 818 | while (session_list != NULL) { | ||
| 819 | IMAPSession *session = (IMAPSession *)session_list->data; | ||
| 820 | |||
| 821 | imap_cmd_logout(session); | ||
| 822 | session_destroy(SESSION(session)); | ||
| 823 | } | ||
| 824 | } | ||
| 825 | #endif | ||
| 826 | |||
| 827 | #define THROW goto catch | ||
| 828 | |||
| 829 | static gint imap_search_flags(IMAPSession *session, GArray **uids, | ||
| 830 | GHashTable **flags_table) | ||
| 831 | { | ||
| 832 | gint ok; | ||
| 833 | gint i; | ||
| 834 | GArray *flag_uids; | ||
| 835 | GHashTable *unseen_table; | ||
| 836 | GHashTable *flagged_table; | ||
| 837 | GHashTable *answered_table; | ||
| 838 | guint32 uid; | ||
| 839 | IMAPFlags flags; | ||
| 840 | |||
| 841 | ok = imap_cmd_search(session, "ALL", uids); | ||
| 842 | if (ok != IMAP_SUCCESS) return ok; | ||
| 843 | |||
| 844 | ok = imap_cmd_search(session, "UNSEEN", &flag_uids); | ||
| 845 | if (ok != IMAP_SUCCESS) { | ||
| 846 | g_array_free(*uids, TRUE); | ||
| 847 | return ok; | ||
| 848 | } | ||
| 849 | unseen_table = imap_get_uid_table(flag_uids); | ||
| 850 | g_array_free(flag_uids, TRUE); | ||
| 851 | ok = imap_cmd_search(session, "FLAGGED", &flag_uids); | ||
| 852 | if (ok != IMAP_SUCCESS) { | ||
| 853 | g_hash_table_destroy(unseen_table); | ||
| 854 | g_array_free(*uids, TRUE); | ||
| 855 | return ok; | ||
| 856 | } | ||
| 857 | flagged_table = imap_get_uid_table(flag_uids); | ||
| 858 | g_array_free(flag_uids, TRUE); | ||
| 859 | ok = imap_cmd_search(session, "ANSWERED", &flag_uids); | ||
| 860 | if (ok != IMAP_SUCCESS) { | ||
| 861 | g_hash_table_destroy(flagged_table); | ||
| 862 | g_hash_table_destroy(unseen_table); | ||
| 863 | g_array_free(*uids, TRUE); | ||
| 864 | return ok; | ||
| 865 | } | ||
| 866 | answered_table = imap_get_uid_table(flag_uids); | ||
| 867 | g_array_free(flag_uids, TRUE); | ||
| 868 | |||
| 869 | *flags_table = g_hash_table_new(NULL, g_direct_equal); | ||
| 870 | |||
| 871 | for (i = 0; i < (*uids)->len; i++) { | ||
| 872 | uid = g_array_index(*uids, guint32, i); | ||
| 873 | flags = IMAP_FLAG_DRAFT; | ||
| 874 | if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid))) | ||
| 875 | flags |= IMAP_FLAG_SEEN; | ||
| 876 | if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid))) | ||
| 877 | flags |= IMAP_FLAG_FLAGGED; | ||
| 878 | if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid))) | ||
| 879 | flags |= IMAP_FLAG_ANSWERED; | ||
| 880 | g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid), | ||
| 881 | GINT_TO_POINTER(flags)); | ||
| 882 | } | ||
| 883 | |||
| 884 | g_hash_table_destroy(answered_table); | ||
| 885 | g_hash_table_destroy(flagged_table); | ||
| 886 | g_hash_table_destroy(unseen_table); | ||
| 887 | |||
| 888 | return IMAP_SUCCESS; | ||
| 889 | } | ||
| 890 | |||
| 891 | static gint imap_fetch_flags(IMAPSession *session, GArray **uids, | ||
| 892 | GHashTable **flags_table) | ||
| 893 | { | ||
| 894 | gint ok; | ||
| 895 | gchar *tmp; | ||
| 896 | gchar *cur_pos; | ||
| 897 | gchar buf[IMAPBUFSIZE]; | ||
| 898 | guint32 uid; | ||
| 899 | IMAPFlags flags; | ||
| 900 | |||
| 901 | if (imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)") != IMAP_SUCCESS) | ||
| 902 | return IMAP_ERROR; | ||
| 903 | |||
| 904 | *uids = g_array_new(FALSE, FALSE, sizeof(guint32)); | ||
| 905 | *flags_table = g_hash_table_new(NULL, g_direct_equal); | ||
| 906 | |||
| 907 | log_print("IMAP4< %s\n", _("(retrieving FLAGS...)")); | ||
| 908 | |||
| 909 | while ((ok = imap_cmd_gen_recv_silent(session, &tmp)) == IMAP_SUCCESS) { | ||
| 910 | if (tmp[0] != '*' || tmp[1] != ' ') { | ||
| 911 | log_print("IMAP4< %s\n", tmp); | ||
| 912 | g_free(tmp); | ||
| 913 | break; | ||
| 914 | } | ||
| 915 | cur_pos = tmp + 2; | ||
| 916 | |||
| 917 | #define PARSE_ONE_ELEMENT(ch) \ | ||
| 918 | { \ | ||
| 919 | cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \ | ||
| 920 | if (cur_pos == NULL) { \ | ||
| 921 | g_warning("cur_pos == NULL\n"); \ | ||
| 922 | g_free(tmp); \ | ||
| 923 | g_hash_table_destroy(*flags_table); \ | ||
| 924 | g_array_free(*uids, TRUE); \ | ||
| 925 | return IMAP_ERROR; \ | ||
| 926 | } \ | ||
| 927 | } | ||
| 928 | |||
| 929 | PARSE_ONE_ELEMENT(' '); | ||
| 930 | PARSE_ONE_ELEMENT(' '); | ||
| 931 | if (strcmp(buf, "FETCH") != 0) { | ||
| 932 | g_free(tmp); | ||
| 933 | continue; | ||
| 934 | } | ||
| 935 | if (*cur_pos != '(') { | ||
| 936 | g_free(tmp); | ||
| 937 | continue; | ||
| 938 | } | ||
| 939 | cur_pos++; | ||
| 940 | uid = 0; | ||
| 941 | flags = 0; | ||
| 942 | |||
| 943 | while (*cur_pos != '\0' && *cur_pos != ')') { | ||
| 944 | while (*cur_pos == ' ') cur_pos++; | ||
| 945 | |||
| 946 | if (!strncmp(cur_pos, "UID ", 4)) { | ||
| 947 | cur_pos += 4; | ||
| 948 | uid = strtoul(cur_pos, &cur_pos, 10); | ||
| 949 | } else if (!strncmp(cur_pos, "FLAGS ", 6)) { | ||
| 950 | cur_pos += 6; | ||
| 951 | if (*cur_pos != '(') { | ||
| 952 | g_warning("*cur_pos != '('\n"); | ||
| 953 | break; | ||
| 954 | } | ||
| 955 | cur_pos++; | ||
| 956 | PARSE_ONE_ELEMENT(')'); | ||
| 957 | flags = imap_parse_imap_flags(buf); | ||
| 958 | flags |= IMAP_FLAG_DRAFT; | ||
| 959 | } else { | ||
| 960 | g_warning("invalid FETCH response: %s\n", cur_pos); | ||
| 961 | break; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | #undef PARSE_ONE_ELEMENT | ||
| 966 | |||
| 967 | if (uid > 0) { | ||
| 968 | g_array_append_val(*uids, uid); | ||
| 969 | g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid), | ||
| 970 | GINT_TO_POINTER(flags)); | ||
| 971 | } | ||
| 972 | |||
| 973 | g_free(tmp); | ||
| 974 | } | ||
| 975 | |||
| 976 | if (ok != IMAP_SUCCESS) { | ||
| 977 | g_hash_table_destroy(*flags_table); | ||
| 978 | g_array_free(*uids, TRUE); | ||
| 979 | } | ||
| 980 | |||
| 981 | return ok; | ||
| 982 | } | ||
| 983 | |||
| 984 | static GSList *imap_get_msg_list_full(Folder *folder, FolderItem *item, | ||
| 985 | gboolean use_cache, | ||
| 986 | gboolean uncached_only) | ||
| 987 | { | ||
| 988 | GSList *mlist = NULL; | ||
| 989 | IMAPSession *session; | ||
| 990 | gint ok, exists = 0, recent = 0, unseen = 0; | ||
| 991 | guint32 uid_validity = 0; | ||
| 992 | guint32 first_uid = 0, last_uid = 0; | ||
| 993 | GSList *newlist = NULL; | ||
| 994 | |||
| 995 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 996 | g_return_val_if_fail(item != NULL, NULL); | ||
| 997 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL); | ||
| 998 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 999 | |||
| 1000 | item->new = item->unread = item->total = 0; | ||
| 1001 | |||
| 1002 | session = imap_session_get(folder); | ||
| 1003 | |||
| 1004 | if (!session) { | ||
| 1005 | if (uncached_only) | ||
| 1006 | return NULL; | ||
| 1007 | mlist = procmsg_read_cache(item, FALSE); | ||
| 1008 | item->last_num = procmsg_get_last_num_in_msg_list(mlist); | ||
| 1009 | procmsg_set_flags(mlist, item); | ||
| 1010 | return mlist; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | ok = imap_select(session, IMAP_FOLDER(folder), item->path, | ||
| 1014 | &exists, &recent, &unseen, &uid_validity); | ||
| 1015 | if (ok != IMAP_SUCCESS) THROW; | ||
| 1016 | |||
| 1017 | if (exists == 0) { | ||
| 1018 | imap_delete_all_cached_messages(item); | ||
| 1019 | return NULL; | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | /* invalidate current cache if UIDVALIDITY has been changed */ | ||
| 1023 | if (item->mtime != uid_validity) { | ||
| 1024 | debug_print("imap_get_msg_list: " | ||
| 1025 | "UIDVALIDITY has been changed.\n"); | ||
| 1026 | use_cache = FALSE; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | if (use_cache) { | ||
| 1030 | GArray *uids; | ||
| 1031 | GHashTable *msg_table; | ||
| 1032 | GHashTable *flags_table; | ||
| 1033 | guint32 cache_last; | ||
| 1034 | guint32 begin = 0; | ||
| 1035 | GSList *cur, *next = NULL; | ||
| 1036 | MsgInfo *msginfo; | ||
| 1037 | IMAPFlags imap_flags; | ||
| 1038 | guint color; | ||
| 1039 | |||
| 1040 | /* get cache data */ | ||
| 1041 | mlist = procmsg_read_cache(item, FALSE); | ||
| 1042 | procmsg_set_flags(mlist, item); | ||
| 1043 | cache_last = procmsg_get_last_num_in_msg_list(mlist); | ||
| 1044 | |||
| 1045 | /* get all UID list and flags */ | ||
| 1046 | #if 0 | ||
| 1047 | ok = imap_search_flags(session, &uids, &flags_table); | ||
| 1048 | if (ok != IMAP_SUCCESS) { | ||
| 1049 | if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW; | ||
| 1050 | ok = imap_fetch_flags(session, &uids, &flags_table); | ||
| 1051 | if (ok != IMAP_SUCCESS) THROW; | ||
| 1052 | } | ||
| 1053 | #else | ||
| 1054 | ok = imap_fetch_flags(session, &uids, &flags_table); | ||
| 1055 | if (ok != IMAP_SUCCESS) THROW; | ||
| 1056 | #endif | ||
| 1057 | |||
| 1058 | if (uids->len > 0) { | ||
| 1059 | first_uid = g_array_index(uids, guint32, 0); | ||
| 1060 | last_uid = g_array_index(uids, guint32, uids->len - 1); | ||
| 1061 | } else { | ||
| 1062 | g_array_free(uids, TRUE); | ||
| 1063 | g_hash_table_destroy(flags_table); | ||
| 1064 | THROW; | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | /* sync message flags with server */ | ||
| 1068 | for (cur = mlist; cur != NULL; cur = next) { | ||
| 1069 | msginfo = (MsgInfo *)cur->data; | ||
| 1070 | next = cur->next; | ||
| 1071 | imap_flags = GPOINTER_TO_INT(g_hash_table_lookup | ||
| 1072 | (flags_table, | ||
| 1073 | GUINT_TO_POINTER(msginfo->msgnum))); | ||
| 1074 | |||
| 1075 | if (imap_flags == 0) { | ||
| 1076 | debug_print("imap_get_msg_list: " | ||
| 1077 | "message %u has been deleted.\n", | ||
| 1078 | msginfo->msgnum); | ||
| 1079 | imap_delete_cached_message | ||
| 1080 | (item, msginfo->msgnum); | ||
| 1081 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 1082 | item->new--; | ||
| 1083 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 1084 | item->unread--; | ||
| 1085 | item->total--; | ||
| 1086 | mlist = g_slist_remove(mlist, msginfo); | ||
| 1087 | procmsg_msginfo_free(msginfo); | ||
| 1088 | item->cache_dirty = TRUE; | ||
| 1089 | item->mark_dirty = TRUE; | ||
| 1090 | continue; | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | if (!IMAP_IS_SEEN(imap_flags)) { | ||
| 1094 | if (!MSG_IS_UNREAD(msginfo->flags)) { | ||
| 1095 | item->unread++; | ||
| 1096 | MSG_SET_PERM_FLAGS(msginfo->flags, | ||
| 1097 | MSG_UNREAD); | ||
| 1098 | item->mark_dirty = TRUE; | ||
| 1099 | } | ||
| 1100 | } else { | ||
| 1101 | if (MSG_IS_NEW(msginfo->flags)) { | ||
| 1102 | item->new--; | ||
| 1103 | item->mark_dirty = TRUE; | ||
| 1104 | } | ||
| 1105 | if (MSG_IS_UNREAD(msginfo->flags)) { | ||
| 1106 | item->unread--; | ||
| 1107 | item->mark_dirty = TRUE; | ||
| 1108 | } | ||
| 1109 | MSG_UNSET_PERM_FLAGS(msginfo->flags, | ||
| 1110 | MSG_NEW|MSG_UNREAD); | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | if (IMAP_IS_FLAGGED(imap_flags)) { | ||
| 1114 | if (!MSG_IS_MARKED(msginfo->flags)) { | ||
| 1115 | MSG_SET_PERM_FLAGS(msginfo->flags, | ||
| 1116 | MSG_MARKED); | ||
| 1117 | item->mark_dirty = TRUE; | ||
| 1118 | } | ||
| 1119 | } else { | ||
| 1120 | if (MSG_IS_MARKED(msginfo->flags)) { | ||
| 1121 | MSG_UNSET_PERM_FLAGS(msginfo->flags, | ||
| 1122 | MSG_MARKED); | ||
| 1123 | item->mark_dirty = TRUE; | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | if (IMAP_IS_ANSWERED(imap_flags)) { | ||
| 1127 | if (!MSG_IS_REPLIED(msginfo->flags)) { | ||
| 1128 | MSG_SET_PERM_FLAGS(msginfo->flags, | ||
| 1129 | MSG_REPLIED); | ||
| 1130 | item->mark_dirty = TRUE; | ||
| 1131 | } | ||
| 1132 | } else { | ||
| 1133 | if (MSG_IS_REPLIED(msginfo->flags)) { | ||
| 1134 | MSG_UNSET_PERM_FLAGS(msginfo->flags, | ||
| 1135 | MSG_REPLIED); | ||
| 1136 | item->mark_dirty = TRUE; | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | color = IMAP_GET_COLORLABEL_VALUE(imap_flags); | ||
| 1141 | if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != color) { | ||
| 1142 | MSG_UNSET_PERM_FLAGS(msginfo->flags, | ||
| 1143 | MSG_CLABEL_FLAG_MASK); | ||
| 1144 | MSG_SET_COLORLABEL_VALUE(msginfo->flags, color); | ||
| 1145 | item->mark_dirty = TRUE; | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | /* check for the first new message */ | ||
| 1150 | msg_table = procmsg_msg_hash_table_create(mlist); | ||
| 1151 | if (msg_table == NULL) | ||
| 1152 | begin = first_uid; | ||
| 1153 | else { | ||
| 1154 | gint i; | ||
| 1155 | |||
| 1156 | for (i = 0; i < uids->len; i++) { | ||
| 1157 | guint32 uid; | ||
| 1158 | |||
| 1159 | uid = g_array_index(uids, guint32, i); | ||
| 1160 | if (g_hash_table_lookup | ||
| 1161 | (msg_table, GUINT_TO_POINTER(uid)) | ||
| 1162 | == NULL) { | ||
| 1163 | debug_print("imap_get_msg_list: " | ||
| 1164 | "first new UID: %u\n", uid); | ||
| 1165 | begin = uid; | ||
| 1166 | break; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | g_hash_table_destroy(msg_table); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | g_array_free(uids, TRUE); | ||
| 1173 | g_hash_table_destroy(flags_table); | ||
| 1174 | |||
| 1175 | /* remove ununsed caches */ | ||
| 1176 | if (first_uid > 0 && last_uid > 0) { | ||
| 1177 | mlist = imap_delete_cached_messages | ||
| 1178 | (mlist, item, 0, first_uid - 1); | ||
| 1179 | mlist = imap_delete_cached_messages | ||
| 1180 | (mlist, item, begin > 0 ? begin : last_uid + 1, | ||
| 1181 | UINT_MAX); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | if (begin > 0 && begin <= last_uid) { | ||
| 1185 | newlist = imap_get_uncached_messages | ||
| 1186 | (session, item, begin, last_uid, | ||
| 1187 | exists - item->total, TRUE); | ||
| 1188 | if (newlist) { | ||
| 1189 | item->cache_dirty = TRUE; | ||
| 1190 | item->mark_dirty = TRUE; | ||
| 1191 | } | ||
| 1192 | mlist = g_slist_concat(mlist, newlist); | ||
| 1193 | } | ||
| 1194 | } else { | ||
| 1195 | imap_delete_all_cached_messages(item); | ||
| 1196 | mlist = imap_get_uncached_messages(session, item, 0, 0, exists, | ||
| 1197 | TRUE); | ||
| 1198 | last_uid = procmsg_get_last_num_in_msg_list(mlist); | ||
| 1199 | item->cache_dirty = TRUE; | ||
| 1200 | item->mark_dirty = TRUE; | ||
| 1201 | newlist = mlist; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | if (!uncached_only) | ||
| 1205 | mlist = procmsg_sort_msg_list(mlist, item->sort_key, | ||
| 1206 | item->sort_type); | ||
| 1207 | |||
| 1208 | item->last_num = last_uid; | ||
| 1209 | |||
| 1210 | if (item->mark_queue) | ||
| 1211 | item->mark_dirty = TRUE; | ||
| 1212 | |||
| 1213 | debug_print("cache_dirty: %d, mark_dirty: %d\n", | ||
| 1214 | item->cache_dirty, item->mark_dirty); | ||
| 1215 | |||
| 1216 | if (!item->opened) { | ||
| 1217 | item->mtime = uid_validity; | ||
| 1218 | if (item->cache_dirty) | ||
| 1219 | procmsg_write_cache_list(item, mlist); | ||
| 1220 | if (item->mark_dirty) | ||
| 1221 | procmsg_write_flags_list(item, mlist); | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | catch: | ||
| 1225 | if (uncached_only) { | ||
| 1226 | GSList *cur; | ||
| 1227 | |||
| 1228 | if (newlist == NULL) { | ||
| 1229 | procmsg_msg_list_free(mlist); | ||
| 1230 | return NULL; | ||
| 1231 | } | ||
| 1232 | if (mlist == newlist) | ||
| 1233 | return newlist; | ||
| 1234 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1235 | if (cur->next == newlist) { | ||
| 1236 | cur->next = NULL; | ||
| 1237 | procmsg_msg_list_free(mlist); | ||
| 1238 | return newlist; | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | procmsg_msg_list_free(mlist); | ||
| 1242 | return NULL; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | return mlist; | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | #undef THROW | ||
| 1249 | |||
| 1250 | static GSList *imap_get_msg_list(Folder *folder, FolderItem *item, | ||
| 1251 | gboolean use_cache) | ||
| 1252 | { | ||
| 1253 | return imap_get_msg_list_full(folder, item, use_cache, FALSE); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | static GSList *imap_get_uncached_msg_list(Folder *folder, FolderItem *item) | ||
| 1257 | { | ||
| 1258 | return imap_get_msg_list_full(folder, item, TRUE, TRUE); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid) | ||
| 1262 | { | ||
| 1263 | gchar *path, *filename; | ||
| 1264 | IMAPSession *session; | ||
| 1265 | gchar nstr[16]; | ||
| 1266 | guint32 uid32 = (guint32)uid; | ||
| 1267 | gint ok; | ||
| 1268 | |||
| 1269 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 1270 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1271 | |||
| 1272 | path = folder_item_get_path(item); | ||
| 1273 | if (!is_dir_exist(path)) | ||
| 1274 | make_dir_hier(path); | ||
| 1275 | g_snprintf(nstr, sizeof(nstr), "%u", uid32); | ||
| 1276 | filename = g_strconcat(path, G_DIR_SEPARATOR_S, nstr, NULL); | ||
| 1277 | g_free(path); | ||
| 1278 | |||
| 1279 | if (is_file_exist(filename) && get_file_size(filename) > 0) { | ||
| 1280 | debug_print("message %u has been already cached.\n", uid32); | ||
| 1281 | return filename; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | session = imap_session_get(folder); | ||
| 1285 | if (!session) { | ||
| 1286 | g_free(filename); | ||
| 1287 | return NULL; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | ok = imap_select(session, IMAP_FOLDER(folder), item->path, | ||
| 1291 | NULL, NULL, NULL, NULL); | ||
| 1292 | if (ok != IMAP_SUCCESS) { | ||
| 1293 | g_warning("can't select mailbox %s\n", item->path); | ||
| 1294 | g_free(filename); | ||
| 1295 | return NULL; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | status_print(_("Getting message %u"), uid32); | ||
| 1299 | debug_print("getting message %u...\n", uid32); | ||
| 1300 | ok = imap_cmd_fetch(session, uid32, filename); | ||
| 1301 | |||
| 1302 | if (ok != IMAP_SUCCESS) { | ||
| 1303 | g_warning("can't fetch message %u\n", uid32); | ||
| 1304 | g_free(filename); | ||
| 1305 | return NULL; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | return filename; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid) | ||
| 1312 | { | ||
| 1313 | IMAPSession *session; | ||
| 1314 | GSList *list; | ||
| 1315 | MsgInfo *msginfo = NULL; | ||
| 1316 | gint ok; | ||
| 1317 | |||
| 1318 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 1319 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1320 | |||
| 1321 | session = imap_session_get(folder); | ||
| 1322 | g_return_val_if_fail(session != NULL, NULL); | ||
| 1323 | |||
| 1324 | ok = imap_select(session, IMAP_FOLDER(folder), item->path, | ||
| 1325 | NULL, NULL, NULL, NULL); | ||
| 1326 | if (ok != IMAP_SUCCESS) | ||
| 1327 | return NULL; | ||
| 1328 | |||
| 1329 | list = imap_get_uncached_messages(session, item, uid, uid, 0, FALSE); | ||
| 1330 | if (list) { | ||
| 1331 | msginfo = (MsgInfo *)list->data; | ||
| 1332 | list->data = NULL; | ||
| 1333 | } | ||
| 1334 | procmsg_msg_list_free(list); | ||
| 1335 | |||
| 1336 | return msginfo; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, | ||
| 1340 | MsgFlags *flags, gboolean remove_source) | ||
| 1341 | { | ||
| 1342 | GSList file_list; | ||
| 1343 | MsgFileInfo fileinfo; | ||
| 1344 | |||
| 1345 | g_return_val_if_fail(file != NULL, -1); | ||
| 1346 | |||
| 1347 | fileinfo.file = (gchar *)file; | ||
| 1348 | fileinfo.flags = flags; | ||
| 1349 | file_list.data = &fileinfo; | ||
| 1350 | file_list.next = NULL; | ||
| 1351 | |||
| 1352 | return imap_add_msgs(folder, dest, &file_list, remove_source, NULL); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, | ||
| 1356 | gboolean remove_source, gint *first) | ||
| 1357 | { | ||
| 1358 | gchar *destdir; | ||
| 1359 | IMAPSession *session; | ||
| 1360 | gint messages, recent, unseen; | ||
| 1361 | guint32 uid_next, uid_validity; | ||
| 1362 | guint32 last_uid = 0; | ||
| 1363 | GSList *cur; | ||
| 1364 | MsgFileInfo *fileinfo; | ||
| 1365 | gint count = 1; | ||
| 1366 | gint total; | ||
| 1367 | gint ok; | ||
| 1368 | GTimeVal tv_prev, tv_cur; | ||
| 1369 | |||
| 1370 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1371 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1372 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 1373 | |||
| 1374 | session = imap_session_get(folder); | ||
| 1375 | if (!session) return -1; | ||
| 1376 | |||
| 1377 | g_get_current_time(&tv_prev); | ||
| 1378 | ui_update(); | ||
| 1379 | |||
| 1380 | ok = imap_status(session, IMAP_FOLDER(folder), dest->path, | ||
| 1381 | &messages, &recent, &uid_next, &uid_validity, &unseen); | ||
| 1382 | if (ok != IMAP_SUCCESS) { | ||
| 1383 | g_warning("can't append messages\n"); | ||
| 1384 | return -1; | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path); | ||
| 1388 | |||
| 1389 | if (!session->uidplus) | ||
| 1390 | last_uid = uid_next - 1; | ||
| 1391 | if (first) | ||
| 1392 | *first = uid_next; | ||
| 1393 | |||
| 1394 | total = g_slist_length(file_list); | ||
| 1395 | |||
| 1396 | for (cur = file_list; cur != NULL; cur = cur->next) { | ||
| 1397 | IMAPFlags iflags = 0; | ||
| 1398 | guint32 new_uid = 0; | ||
| 1399 | |||
| 1400 | fileinfo = (MsgFileInfo *)cur->data; | ||
| 1401 | |||
| 1402 | if (fileinfo->flags) { | ||
| 1403 | if (MSG_IS_MARKED(*fileinfo->flags)) | ||
| 1404 | iflags |= IMAP_FLAG_FLAGGED; | ||
| 1405 | if (MSG_IS_REPLIED(*fileinfo->flags)) | ||
| 1406 | iflags |= IMAP_FLAG_ANSWERED; | ||
| 1407 | if (!MSG_IS_UNREAD(*fileinfo->flags)) | ||
| 1408 | iflags |= IMAP_FLAG_SEEN; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | if (dest->stype == F_OUTBOX || | ||
| 1412 | dest->stype == F_QUEUE || | ||
| 1413 | dest->stype == F_DRAFT) | ||
| 1414 | iflags |= IMAP_FLAG_SEEN; | ||
| 1415 | |||
| 1416 | g_get_current_time(&tv_cur); | ||
| 1417 | if (tv_cur.tv_sec > tv_prev.tv_sec || | ||
| 1418 | tv_cur.tv_usec - tv_prev.tv_usec > | ||
| 1419 | PROGRESS_UPDATE_INTERVAL * 1000) { | ||
| 1420 | status_print(_("Appending messages to %s (%d / %d)"), | ||
| 1421 | dest->path, count, total); | ||
| 1422 | progress_show(count, total); | ||
| 1423 | ui_update(); | ||
| 1424 | tv_prev = tv_cur; | ||
| 1425 | } | ||
| 1426 | ++count; | ||
| 1427 | |||
| 1428 | ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, | ||
| 1429 | &new_uid); | ||
| 1430 | |||
| 1431 | if (ok != IMAP_SUCCESS) { | ||
| 1432 | g_warning("can't append message %s\n", fileinfo->file); | ||
| 1433 | g_free(destdir); | ||
| 1434 | progress_show(0, 0); | ||
| 1435 | return -1; | ||
| 1436 | } | ||
| 1437 | |||
| 1438 | if (syl_app_get()) | ||
| 1439 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, fileinfo->file, new_uid); | ||
| 1440 | |||
| 1441 | if (!session->uidplus) | ||
| 1442 | last_uid++; | ||
| 1443 | else if (last_uid < new_uid) | ||
| 1444 | last_uid = new_uid; | ||
| 1445 | |||
| 1446 | dest->last_num = last_uid; | ||
| 1447 | dest->total++; | ||
| 1448 | dest->updated = TRUE; | ||
| 1449 | |||
| 1450 | if (fileinfo->flags) { | ||
| 1451 | if (MSG_IS_UNREAD(*fileinfo->flags)) | ||
| 1452 | dest->unread++; | ||
| 1453 | } else | ||
| 1454 | dest->unread++; | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | progress_show(0, 0); | ||
| 1458 | g_free(destdir); | ||
| 1459 | |||
| 1460 | if (remove_source) { | ||
| 1461 | for (cur = file_list; cur != NULL; cur = cur->next) { | ||
| 1462 | fileinfo = (MsgFileInfo *)cur->data; | ||
| 1463 | if (g_unlink(fileinfo->file) < 0) | ||
| 1464 | FILE_OP_ERROR(fileinfo->file, "unlink"); | ||
| 1465 | } | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | return last_uid; | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | static gint imap_add_msg_msginfo(Folder *folder, FolderItem *dest, | ||
| 1472 | MsgInfo *msginfo, gboolean remove_source) | ||
| 1473 | { | ||
| 1474 | GSList msglist; | ||
| 1475 | |||
| 1476 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1477 | |||
| 1478 | msglist.data = msginfo; | ||
| 1479 | msglist.next = NULL; | ||
| 1480 | |||
| 1481 | return imap_add_msgs_msginfo(folder, dest, &msglist, remove_source, | ||
| 1482 | NULL); | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | static gint imap_add_msgs_msginfo(Folder *folder, FolderItem *dest, | ||
| 1486 | GSList *msglist, gboolean remove_source, | ||
| 1487 | gint *first) | ||
| 1488 | { | ||
| 1489 | GSList *file_list; | ||
| 1490 | gint ret; | ||
| 1491 | |||
| 1492 | file_list = procmsg_get_message_file_list(msglist); | ||
| 1493 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 1494 | |||
| 1495 | ret = imap_add_msgs(folder, dest, file_list, remove_source, first); | ||
| 1496 | |||
| 1497 | procmsg_message_file_list_free(file_list); | ||
| 1498 | |||
| 1499 | return ret; | ||
| 1500 | } | ||
| 1501 | |||
| 1502 | static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist, | ||
| 1503 | gboolean remove_source) | ||
| 1504 | { | ||
| 1505 | FolderItem *src; | ||
| 1506 | gchar *destdir; | ||
| 1507 | GSList *seq_list, *cur; | ||
| 1508 | MsgInfo *msginfo; | ||
| 1509 | IMAPSession *session; | ||
| 1510 | gint count = 0, total; | ||
| 1511 | gint ok = IMAP_SUCCESS; | ||
| 1512 | |||
| 1513 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1514 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1515 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1516 | |||
| 1517 | session = imap_session_get(folder); | ||
| 1518 | if (!session) return -1; | ||
| 1519 | |||
| 1520 | ui_update(); | ||
| 1521 | |||
| 1522 | msginfo = (MsgInfo *)msglist->data; | ||
| 1523 | |||
| 1524 | src = msginfo->folder; | ||
| 1525 | if (src == dest) { | ||
| 1526 | g_warning("the src folder is identical to the dest.\n"); | ||
| 1527 | return -1; | ||
| 1528 | } | ||
| 1529 | |||
| 1530 | ok = imap_select(session, IMAP_FOLDER(folder), src->path, | ||
| 1531 | NULL, NULL, NULL, NULL); | ||
| 1532 | if (ok != IMAP_SUCCESS) | ||
| 1533 | return ok; | ||
| 1534 | |||
| 1535 | destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path); | ||
| 1536 | |||
| 1537 | total = g_slist_length(msglist); | ||
| 1538 | seq_list = imap_get_seq_set_from_msglist(msglist, IMAP_COPY_LIMIT); | ||
| 1539 | |||
| 1540 | for (cur = seq_list; cur != NULL; cur = cur->next) { | ||
| 1541 | gchar *seq_set = (gchar *)cur->data; | ||
| 1542 | |||
| 1543 | count += imap_seq_set_get_count(seq_set); | ||
| 1544 | |||
| 1545 | if (remove_source) { | ||
| 1546 | status_print(_("Moving messages %s to %s ..."), | ||
| 1547 | seq_set, dest->path); | ||
| 1548 | debug_print("Moving message %s/[%s] to %s ...\n", | ||
| 1549 | src->path, seq_set, dest->path); | ||
| 1550 | } else { | ||
| 1551 | status_print(_("Copying messages %s to %s ..."), | ||
| 1552 | seq_set, dest->path); | ||
| 1553 | debug_print("Copying message %s/[%s] to %s ...\n", | ||
| 1554 | src->path, seq_set, dest->path); | ||
| 1555 | } | ||
| 1556 | progress_show(count, total); | ||
| 1557 | ui_update(); | ||
| 1558 | |||
| 1559 | ok = imap_cmd_copy(session, seq_set, destdir); | ||
| 1560 | if (ok != IMAP_SUCCESS) { | ||
| 1561 | imap_seq_set_free(seq_list); | ||
| 1562 | progress_show(0, 0); | ||
| 1563 | return -1; | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | progress_show(0, 0); | ||
| 1568 | |||
| 1569 | dest->updated = TRUE; | ||
| 1570 | |||
| 1571 | imap_seq_set_free(seq_list); | ||
| 1572 | g_free(destdir); | ||
| 1573 | |||
| 1574 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 1575 | msginfo = (MsgInfo *)cur->data; | ||
| 1576 | |||
| 1577 | if (syl_app_get()) | ||
| 1578 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, NULL, 0); | ||
| 1579 | |||
| 1580 | dest->total++; | ||
| 1581 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 1582 | dest->new++; | ||
| 1583 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 1584 | dest->unread++; | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | if (remove_source) { | ||
| 1588 | ok = imap_remove_msgs(folder, src, msglist); | ||
| 1589 | if (ok != IMAP_SUCCESS) | ||
| 1590 | return ok; | ||
| 1591 | } | ||
| 1592 | |||
| 1593 | if (ok == IMAP_SUCCESS) | ||
| 1594 | return 0; | ||
| 1595 | else | ||
| 1596 | return -1; | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) | ||
| 1600 | { | ||
| 1601 | GSList msglist; | ||
| 1602 | |||
| 1603 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1604 | |||
| 1605 | msglist.data = msginfo; | ||
| 1606 | msglist.next = NULL; | ||
| 1607 | |||
| 1608 | return imap_move_msgs(folder, dest, &msglist); | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist) | ||
| 1612 | { | ||
| 1613 | MsgInfo *msginfo; | ||
| 1614 | GSList *file_list; | ||
| 1615 | gint ret = 0; | ||
| 1616 | |||
| 1617 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1618 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1619 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1620 | |||
| 1621 | msginfo = (MsgInfo *)msglist->data; | ||
| 1622 | g_return_val_if_fail(msginfo->folder != NULL, -1); | ||
| 1623 | |||
| 1624 | if (folder == msginfo->folder->folder) | ||
| 1625 | return imap_do_copy_msgs(folder, dest, msglist, TRUE); | ||
| 1626 | |||
| 1627 | file_list = procmsg_get_message_file_list(msglist); | ||
| 1628 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 1629 | |||
| 1630 | ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL); | ||
| 1631 | |||
| 1632 | procmsg_message_file_list_free(file_list); | ||
| 1633 | |||
| 1634 | if (ret != -1) | ||
| 1635 | ret = folder_item_remove_msgs(msginfo->folder, msglist); | ||
| 1636 | |||
| 1637 | return ret; | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) | ||
| 1641 | { | ||
| 1642 | GSList msglist; | ||
| 1643 | |||
| 1644 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1645 | |||
| 1646 | msglist.data = msginfo; | ||
| 1647 | msglist.next = NULL; | ||
| 1648 | |||
| 1649 | return imap_copy_msgs(folder, dest, &msglist); | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist) | ||
| 1653 | { | ||
| 1654 | MsgInfo *msginfo; | ||
| 1655 | GSList *file_list; | ||
| 1656 | gint ret; | ||
| 1657 | |||
| 1658 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1659 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1660 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1661 | |||
| 1662 | msginfo = (MsgInfo *)msglist->data; | ||
| 1663 | g_return_val_if_fail(msginfo->folder != NULL, -1); | ||
| 1664 | |||
| 1665 | if (folder == msginfo->folder->folder) | ||
| 1666 | return imap_do_copy_msgs(folder, dest, msglist, FALSE); | ||
| 1667 | |||
| 1668 | file_list = procmsg_get_message_file_list(msglist); | ||
| 1669 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 1670 | |||
| 1671 | ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL); | ||
| 1672 | |||
| 1673 | procmsg_message_file_list_free(file_list); | ||
| 1674 | |||
| 1675 | return ret; | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item, | ||
| 1679 | GSList *seq_list) | ||
| 1680 | { | ||
| 1681 | gint ok; | ||
| 1682 | IMAPSession *session; | ||
| 1683 | GSList *cur; | ||
| 1684 | |||
| 1685 | g_return_val_if_fail(seq_list != NULL, -1); | ||
| 1686 | |||
| 1687 | session = imap_session_get(folder); | ||
| 1688 | if (!session) return -1; | ||
| 1689 | |||
| 1690 | for (cur = seq_list; cur != NULL; cur = cur->next) { | ||
| 1691 | gchar *seq_set = (gchar *)cur->data; | ||
| 1692 | |||
| 1693 | status_print(_("Removing messages %s"), seq_set); | ||
| 1694 | ui_update(); | ||
| 1695 | |||
| 1696 | ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED, | ||
| 1697 | TRUE); | ||
| 1698 | if (ok != IMAP_SUCCESS) { | ||
| 1699 | log_warning(_("can't set deleted flags: %s\n"), | ||
| 1700 | seq_set); | ||
| 1701 | return ok; | ||
| 1702 | } | ||
| 1703 | } | ||
| 1704 | |||
| 1705 | ok = imap_cmd_expunge(session); | ||
| 1706 | if (ok != IMAP_SUCCESS) { | ||
| 1707 | log_warning(_("can't expunge\n")); | ||
| 1708 | } else { | ||
| 1709 | /* for some broken IMAP servers */ | ||
| 1710 | ok = imap_cmd_noop(session); | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | item->updated = TRUE; | ||
| 1714 | |||
| 1715 | return ok; | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo) | ||
| 1719 | { | ||
| 1720 | GSList msglist; | ||
| 1721 | |||
| 1722 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1723 | |||
| 1724 | msglist.data = msginfo; | ||
| 1725 | msglist.next = NULL; | ||
| 1726 | |||
| 1727 | return imap_remove_msgs(folder, item, &msglist); | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist) | ||
| 1731 | { | ||
| 1732 | gint ok; | ||
| 1733 | IMAPSession *session; | ||
| 1734 | GSList *seq_list, *cur; | ||
| 1735 | gchar *dir; | ||
| 1736 | gboolean dir_exist; | ||
| 1737 | |||
| 1738 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1739 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1); | ||
| 1740 | g_return_val_if_fail(item != NULL, -1); | ||
| 1741 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 1742 | |||
| 1743 | session = imap_session_get(folder); | ||
| 1744 | if (!session) return -1; | ||
| 1745 | |||
| 1746 | ok = imap_select(session, IMAP_FOLDER(folder), item->path, | ||
| 1747 | NULL, NULL, NULL, NULL); | ||
| 1748 | if (ok != IMAP_SUCCESS) | ||
| 1749 | return ok; | ||
| 1750 | |||
| 1751 | seq_list = imap_get_seq_set_from_msglist(msglist, 0); | ||
| 1752 | ok = imap_remove_msgs_by_seq_set(folder, item, seq_list); | ||
| 1753 | imap_seq_set_free(seq_list); | ||
| 1754 | if (ok != IMAP_SUCCESS) | ||
| 1755 | return ok; | ||
| 1756 | |||
| 1757 | dir = folder_item_get_path(item); | ||
| 1758 | dir_exist = is_dir_exist(dir); | ||
| 1759 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 1760 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 1761 | guint32 uid = msginfo->msgnum; | ||
| 1762 | |||
| 1763 | if (syl_app_get()) | ||
| 1764 | g_signal_emit_by_name(syl_app_get(), "remove-msg", item, NULL, uid); | ||
| 1765 | |||
| 1766 | if (dir_exist) | ||
| 1767 | remove_numbered_files(dir, uid, uid); | ||
| 1768 | item->total--; | ||
| 1769 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 1770 | item->new--; | ||
| 1771 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 1772 | item->unread--; | ||
| 1773 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID); | ||
| 1774 | } | ||
| 1775 | g_free(dir); | ||
| 1776 | |||
| 1777 | return IMAP_SUCCESS; | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | static gint imap_remove_all_msg(Folder *folder, FolderItem *item) | ||
| 1781 | { | ||
| 1782 | gint ok; | ||
| 1783 | IMAPSession *session; | ||
| 1784 | gchar *dir; | ||
| 1785 | |||
| 1786 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1787 | g_return_val_if_fail(item != NULL, -1); | ||
| 1788 | |||
| 1789 | session = imap_session_get(folder); | ||
| 1790 | if (!session) return -1; | ||
| 1791 | |||
| 1792 | ok = imap_select(session, IMAP_FOLDER(folder), item->path, | ||
| 1793 | NULL, NULL, NULL, NULL); | ||
| 1794 | if (ok != IMAP_SUCCESS) | ||
| 1795 | return ok; | ||
| 1796 | |||
| 1797 | status_print(_("Removing all messages in %s"), item->path); | ||
| 1798 | ui_update(); | ||
| 1799 | |||
| 1800 | ok = imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)"); | ||
| 1801 | if (ok != IMAP_SUCCESS) { | ||
| 1802 | log_warning(_("can't set deleted flags: 1:*\n")); | ||
| 1803 | return ok; | ||
| 1804 | } | ||
| 1805 | ok = imap_cmd_ok(session, NULL); | ||
| 1806 | if (ok != IMAP_SUCCESS) { | ||
| 1807 | log_warning(_("can't set deleted flags: 1:*\n")); | ||
| 1808 | return ok; | ||
| 1809 | } | ||
| 1810 | |||
| 1811 | ok = imap_cmd_expunge(session); | ||
| 1812 | if (ok != IMAP_SUCCESS) { | ||
| 1813 | log_warning(_("can't expunge\n")); | ||
| 1814 | return ok; | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | if (syl_app_get()) | ||
| 1818 | g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item); | ||
| 1819 | |||
| 1820 | item->new = item->unread = item->total = 0; | ||
| 1821 | item->updated = TRUE; | ||
| 1822 | |||
| 1823 | dir = folder_item_get_path(item); | ||
| 1824 | if (is_dir_exist(dir)) | ||
| 1825 | remove_all_numbered_files(dir); | ||
| 1826 | g_free(dir); | ||
| 1827 | |||
| 1828 | return IMAP_SUCCESS; | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, | ||
| 1832 | MsgInfo *msginfo) | ||
| 1833 | { | ||
| 1834 | /* TODO: properly implement this method */ | ||
| 1835 | return FALSE; | ||
| 1836 | } | ||
| 1837 | |||
| 1838 | static gint imap_close(Folder *folder, FolderItem *item) | ||
| 1839 | { | ||
| 1840 | gint ok; | ||
| 1841 | IMAPSession *session; | ||
| 1842 | |||
| 1843 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1844 | |||
| 1845 | if (!item->path) return 0; | ||
| 1846 | |||
| 1847 | if (!REMOTE_FOLDER(folder)->session) | ||
| 1848 | return 0; | ||
| 1849 | |||
| 1850 | session = imap_session_get(folder); | ||
| 1851 | if (!session) return -1; | ||
| 1852 | |||
| 1853 | if (session->mbox) { | ||
| 1854 | if (strcmp2(session->mbox, item->path) != 0) return -1; | ||
| 1855 | |||
| 1856 | ok = imap_cmd_close(session); | ||
| 1857 | if (ok != IMAP_SUCCESS) | ||
| 1858 | log_warning(_("can't close folder\n")); | ||
| 1859 | |||
| 1860 | g_free(session->mbox); | ||
| 1861 | session->mbox = NULL; | ||
| 1862 | |||
| 1863 | return ok; | ||
| 1864 | } else | ||
| 1865 | return 0; | ||
| 1866 | } | ||
| 1867 | |||
| 1868 | static gint imap_scan_folder(Folder *folder, FolderItem *item) | ||
| 1869 | { | ||
| 1870 | IMAPSession *session; | ||
| 1871 | gint messages, recent, unseen; | ||
| 1872 | guint32 uid_next, uid_validity; | ||
| 1873 | gint ok; | ||
| 1874 | |||
| 1875 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1876 | g_return_val_if_fail(item != NULL, -1); | ||
| 1877 | |||
| 1878 | session = imap_session_get(folder); | ||
| 1879 | if (!session) return -1; | ||
| 1880 | |||
| 1881 | ok = imap_status(session, IMAP_FOLDER(folder), item->path, | ||
| 1882 | &messages, &recent, &uid_next, &uid_validity, &unseen); | ||
| 1883 | if (ok != IMAP_SUCCESS) return -1; | ||
| 1884 | |||
| 1885 | item->new = unseen > 0 ? recent : 0; | ||
| 1886 | item->unread = unseen; | ||
| 1887 | item->total = messages; | ||
| 1888 | item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0; | ||
| 1889 | /* item->mtime = uid_validity; */ | ||
| 1890 | item->updated = TRUE; | ||
| 1891 | |||
| 1892 | return 0; | ||
| 1893 | } | ||
| 1894 | |||
| 1895 | static gint imap_scan_tree(Folder *folder) | ||
| 1896 | { | ||
| 1897 | FolderItem *item = NULL; | ||
| 1898 | IMAPSession *session; | ||
| 1899 | gchar *root_folder = NULL; | ||
| 1900 | GSList *item_list, *cur; | ||
| 1901 | |||
| 1902 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1903 | g_return_val_if_fail(folder->account != NULL, -1); | ||
| 1904 | |||
| 1905 | session = imap_session_get(folder); | ||
| 1906 | if (!session) { | ||
| 1907 | if (!folder->node) { | ||
| 1908 | folder_tree_destroy(folder); | ||
| 1909 | item = folder_item_new(folder->name, NULL); | ||
| 1910 | item->folder = folder; | ||
| 1911 | folder->node = item->node = g_node_new(item); | ||
| 1912 | } | ||
| 1913 | return -1; | ||
| 1914 | } | ||
| 1915 | |||
| 1916 | if (folder->account->imap_dir && *folder->account->imap_dir) { | ||
| 1917 | gchar *real_path; | ||
| 1918 | GPtrArray *argbuf; | ||
| 1919 | gint ok; | ||
| 1920 | |||
| 1921 | Xstrdup_a(root_folder, folder->account->imap_dir, return -1); | ||
| 1922 | extract_quote(root_folder, '"'); | ||
| 1923 | subst_char(root_folder, | ||
| 1924 | imap_get_path_separator(IMAP_FOLDER(folder), | ||
| 1925 | root_folder), | ||
| 1926 | '/'); | ||
| 1927 | strtailchomp(root_folder, '/'); | ||
| 1928 | real_path = imap_get_real_path | ||
| 1929 | (IMAP_FOLDER(folder), root_folder); | ||
| 1930 | debug_print("IMAP root directory: %s\n", real_path); | ||
| 1931 | |||
| 1932 | /* check if root directory exist */ | ||
| 1933 | argbuf = g_ptr_array_new(); | ||
| 1934 | ok = imap_cmd_list(session, NULL, real_path, argbuf); | ||
| 1935 | if (ok != IMAP_SUCCESS || | ||
| 1936 | search_array_str(argbuf, "LIST ") == NULL) { | ||
| 1937 | log_warning(_("root folder %s not exist\n"), real_path); | ||
| 1938 | g_ptr_array_free(argbuf, TRUE); | ||
| 1939 | g_free(real_path); | ||
| 1940 | return -1; | ||
| 1941 | } | ||
| 1942 | g_ptr_array_free(argbuf, TRUE); | ||
| 1943 | g_free(real_path); | ||
| 1944 | } | ||
| 1945 | |||
| 1946 | if (folder->node) | ||
| 1947 | item = FOLDER_ITEM(folder->node->data); | ||
| 1948 | if (!item || ((item->path || root_folder) && | ||
| 1949 | strcmp2(item->path, root_folder) != 0)) { | ||
| 1950 | folder_tree_destroy(folder); | ||
| 1951 | item = folder_item_new(folder->name, root_folder); | ||
| 1952 | item->folder = folder; | ||
| 1953 | folder->node = item->node = g_node_new(item); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | item_list = imap_get_folder_list(session, item); | ||
| 1957 | imap_scan_tree_recursive(session, item, item_list); | ||
| 1958 | imap_create_missing_folders(folder); | ||
| 1959 | |||
| 1960 | for (cur = item_list; cur != NULL; cur = cur->next) | ||
| 1961 | folder_item_destroy(FOLDER_ITEM(cur->data)); | ||
| 1962 | g_slist_free(item_list); | ||
| 1963 | |||
| 1964 | return 0; | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, | ||
| 1968 | GSList *item_list) | ||
| 1969 | { | ||
| 1970 | Folder *folder; | ||
| 1971 | FolderItem *new_item; | ||
| 1972 | GSList *part_list, *cur; | ||
| 1973 | GNode *node; | ||
| 1974 | |||
| 1975 | g_return_val_if_fail(item != NULL, -1); | ||
| 1976 | g_return_val_if_fail(item->folder != NULL, -1); | ||
| 1977 | g_return_val_if_fail(item->no_sub == FALSE, -1); | ||
| 1978 | |||
| 1979 | folder = item->folder; | ||
| 1980 | |||
| 1981 | part_list = imap_get_part_folder_list(item_list, item); | ||
| 1982 | |||
| 1983 | node = item->node->children; | ||
| 1984 | while (node != NULL) { | ||
| 1985 | FolderItem *old_item = FOLDER_ITEM(node->data); | ||
| 1986 | GNode *next = node->next; | ||
| 1987 | |||
| 1988 | new_item = NULL; | ||
| 1989 | |||
| 1990 | for (cur = part_list; cur != NULL; cur = cur->next) { | ||
| 1991 | FolderItem *cur_item = FOLDER_ITEM(cur->data); | ||
| 1992 | if (!strcmp2(old_item->path, cur_item->path)) { | ||
| 1993 | new_item = cur_item; | ||
| 1994 | break; | ||
| 1995 | } | ||
| 1996 | } | ||
| 1997 | if (!new_item) { | ||
| 1998 | if (old_item->stype != F_VIRTUAL) { | ||
| 1999 | debug_print("folder '%s' not found. removing...\n", old_item->path); | ||
| 2000 | folder_item_remove(old_item); | ||
| 2001 | } | ||
| 2002 | } else if (old_item->stype == F_VIRTUAL) { | ||
| 2003 | debug_print("IMAP4 folder found at the location of virtual folder '%s'. removing virtual folder...\n", old_item->path); | ||
| 2004 | virtual_get_class()->remove_folder | ||
| 2005 | (folder, old_item); | ||
| 2006 | } else { | ||
| 2007 | old_item->no_sub = new_item->no_sub; | ||
| 2008 | old_item->no_select = new_item->no_select; | ||
| 2009 | if (old_item->no_select == TRUE) | ||
| 2010 | old_item->new = old_item->unread = | ||
| 2011 | old_item->total = 0; | ||
| 2012 | if (old_item->no_sub == TRUE && node->children) { | ||
| 2013 | debug_print("folder '%s' doesn't have " | ||
| 2014 | "subfolders. removing...\n", | ||
| 2015 | old_item->path); | ||
| 2016 | folder_item_remove_children(old_item); | ||
| 2017 | } | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | node = next; | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | for (cur = part_list; cur != NULL; cur = cur->next) { | ||
| 2024 | FolderItem *cur_item = FOLDER_ITEM(cur->data); | ||
| 2025 | new_item = NULL; | ||
| 2026 | for (node = item->node->children; node != NULL; | ||
| 2027 | node = node->next) { | ||
| 2028 | if (!strcmp2(FOLDER_ITEM(node->data)->path, | ||
| 2029 | cur_item->path)) { | ||
| 2030 | new_item = FOLDER_ITEM(node->data); | ||
| 2031 | cur_item = NULL; | ||
| 2032 | break; | ||
| 2033 | } | ||
| 2034 | } | ||
| 2035 | if (!new_item) { | ||
| 2036 | new_item = folder_item_copy(cur_item); | ||
| 2037 | debug_print("new folder '%s' found.\n", new_item->path); | ||
| 2038 | folder_item_append(item, new_item); | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | if (!g_ascii_strcasecmp(new_item->path, "INBOX")) { | ||
| 2042 | new_item->stype = F_INBOX; | ||
| 2043 | folder->inbox = new_item; | ||
| 2044 | } else if (!item->parent || item->stype == F_INBOX) { | ||
| 2045 | const gchar *base; | ||
| 2046 | |||
| 2047 | base = g_basename(new_item->path); | ||
| 2048 | |||
| 2049 | if (!folder->outbox && | ||
| 2050 | !g_ascii_strcasecmp(base, "Sent")) { | ||
| 2051 | new_item->stype = F_OUTBOX; | ||
| 2052 | folder->outbox = new_item; | ||
| 2053 | } else if (!folder->draft && | ||
| 2054 | !g_ascii_strcasecmp(base, "Drafts")) { | ||
| 2055 | new_item->stype = F_DRAFT; | ||
| 2056 | folder->draft = new_item; | ||
| 2057 | } else if (!folder->queue && | ||
| 2058 | !g_ascii_strcasecmp(base, "Queue")) { | ||
| 2059 | new_item->stype = F_QUEUE; | ||
| 2060 | folder->queue = new_item; | ||
| 2061 | } else if (!folder->trash && | ||
| 2062 | !g_ascii_strcasecmp(base, "Trash")) { | ||
| 2063 | new_item->stype = F_TRASH; | ||
| 2064 | folder->trash = new_item; | ||
| 2065 | } | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | #if 0 | ||
| 2069 | if (new_item->no_select == FALSE) | ||
| 2070 | imap_scan_folder(folder, new_item); | ||
| 2071 | #endif | ||
| 2072 | if (new_item->no_sub == FALSE) | ||
| 2073 | imap_scan_tree_recursive(session, new_item, item_list); | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | g_slist_free(part_list); | ||
| 2077 | |||
| 2078 | return IMAP_SUCCESS; | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | static GSList *imap_get_folder_list(IMAPSession *session, FolderItem *item) | ||
| 2082 | { | ||
| 2083 | Folder *folder; | ||
| 2084 | IMAPFolder *imapfolder; | ||
| 2085 | gchar *real_path; | ||
| 2086 | gchar *wildcard_path; | ||
| 2087 | gchar separator; | ||
| 2088 | GSList *item_list = NULL; | ||
| 2089 | |||
| 2090 | folder = item->folder; | ||
| 2091 | imapfolder = IMAP_FOLDER(folder); | ||
| 2092 | |||
| 2093 | separator = imap_get_path_separator(imapfolder, item->path); | ||
| 2094 | |||
| 2095 | if (folder->ui_func) | ||
| 2096 | folder->ui_func(folder, item, folder->ui_func_data); | ||
| 2097 | |||
| 2098 | if (item->path) { | ||
| 2099 | real_path = imap_get_real_path(imapfolder, item->path); | ||
| 2100 | strtailchomp(real_path, separator); | ||
| 2101 | wildcard_path = g_strdup_printf("%s%c*", real_path, separator); | ||
| 2102 | } else { | ||
| 2103 | real_path = g_strdup(""); | ||
| 2104 | wildcard_path = g_strdup("*"); | ||
| 2105 | } | ||
| 2106 | |||
| 2107 | if (imap_cmd_gen_send(session, "LIST \"\" \"%s\"", wildcard_path) == IMAP_SUCCESS) { | ||
| 2108 | item_list = imap_parse_list(session, real_path, NULL); | ||
| 2109 | item_list = imap_add_inter_folders(item_list, item->path); | ||
| 2110 | } | ||
| 2111 | g_free(real_path); | ||
| 2112 | g_free(wildcard_path); | ||
| 2113 | |||
| 2114 | return item_list; | ||
| 2115 | } | ||
| 2116 | |||
| 2117 | static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path, | ||
| 2118 | gchar *separator) | ||
| 2119 | { | ||
| 2120 | gchar buf[IMAPBUFSIZE]; | ||
| 2121 | gchar flags[256]; | ||
| 2122 | gchar separator_str[16]; | ||
| 2123 | gchar *p; | ||
| 2124 | const gchar *name; | ||
| 2125 | gchar *loc_name, *loc_path; | ||
| 2126 | GSList *item_list = NULL; | ||
| 2127 | GString *str; | ||
| 2128 | FolderItem *new_item; | ||
| 2129 | |||
| 2130 | debug_print("getting list of %s ...\n", | ||
| 2131 | *real_path ? real_path : "\"\""); | ||
| 2132 | |||
| 2133 | str = g_string_new(NULL); | ||
| 2134 | |||
| 2135 | for (;;) { | ||
| 2136 | if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) { | ||
| 2137 | log_warning(_("error occurred while getting LIST.\n")); | ||
| 2138 | break; | ||
| 2139 | } | ||
| 2140 | strretchomp(buf); | ||
| 2141 | if (buf[0] != '*' || buf[1] != ' ') { | ||
| 2142 | log_print("IMAP4< %s\n", buf); | ||
| 2143 | if (sscanf(buf, "%*d %16s", buf) < 1 || | ||
| 2144 | strcmp(buf, "OK") != 0) | ||
| 2145 | log_warning(_("error occurred while getting LIST.\n")); | ||
| 2146 | |||
| 2147 | break; | ||
| 2148 | } | ||
| 2149 | debug_print("IMAP4< %s\n", buf); | ||
| 2150 | |||
| 2151 | g_string_assign(str, buf); | ||
| 2152 | p = str->str + 2; | ||
| 2153 | if (strncmp(p, "LIST ", 5) != 0) continue; | ||
| 2154 | p += 5; | ||
| 2155 | |||
| 2156 | if (*p != '(') continue; | ||
| 2157 | p++; | ||
| 2158 | p = strchr_cpy(p, ')', flags, sizeof(flags)); | ||
| 2159 | if (!p) continue; | ||
| 2160 | while (*p == ' ') p++; | ||
| 2161 | |||
| 2162 | p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str)); | ||
| 2163 | if (!p) continue; | ||
| 2164 | extract_quote(separator_str, '"'); | ||
| 2165 | if (!strcmp(separator_str, "NIL")) | ||
| 2166 | separator_str[0] = '\0'; | ||
| 2167 | if (separator) | ||
| 2168 | *separator = separator_str[0]; | ||
| 2169 | |||
| 2170 | buf[0] = '\0'; | ||
| 2171 | while (*p == ' ') p++; | ||
| 2172 | if ((*p == '~' && *(p + 1) == '{') || *p == '{' || *p == '"') | ||
| 2173 | p = imap_parse_atom(session, p, buf, sizeof(buf), str); | ||
| 2174 | else | ||
| 2175 | strncpy2(buf, p, sizeof(buf)); | ||
| 2176 | strtailchomp(buf, separator_str[0]); | ||
| 2177 | if (buf[0] == '\0') continue; | ||
| 2178 | if (!strcmp(buf, real_path)) continue; | ||
| 2179 | |||
| 2180 | if (separator_str[0] != '\0') | ||
| 2181 | subst_char(buf, separator_str[0], '/'); | ||
| 2182 | name = g_basename(buf); | ||
| 2183 | if (name[0] == '.') continue; | ||
| 2184 | |||
| 2185 | loc_name = imap_modified_utf7_to_utf8(name); | ||
| 2186 | loc_path = imap_modified_utf7_to_utf8(buf); | ||
| 2187 | new_item = folder_item_new(loc_name, loc_path); | ||
| 2188 | if (strcasestr(flags, "\\Noinferiors") != NULL) | ||
| 2189 | new_item->no_sub = TRUE; | ||
| 2190 | if (g_ascii_strcasecmp(buf, "INBOX") != 0 && | ||
| 2191 | strcasestr(flags, "\\Noselect") != NULL) | ||
| 2192 | new_item->no_select = TRUE; | ||
| 2193 | |||
| 2194 | item_list = g_slist_prepend(item_list, new_item); | ||
| 2195 | |||
| 2196 | debug_print("folder '%s' found.\n", loc_path); | ||
| 2197 | g_free(loc_path); | ||
| 2198 | g_free(loc_name); | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | g_string_free(str, TRUE); | ||
| 2202 | |||
| 2203 | item_list = g_slist_reverse(item_list); | ||
| 2204 | return item_list; | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | static GSList *imap_add_inter_folders(GSList *item_list, const gchar *root_path) | ||
| 2208 | { | ||
| 2209 | FolderItem *item; | ||
| 2210 | GSList *cur; | ||
| 2211 | GSList *add_list = NULL; | ||
| 2212 | GHashTable *exist; | ||
| 2213 | const gchar *p; | ||
| 2214 | gint root_path_len = 0; | ||
| 2215 | |||
| 2216 | if (root_path) | ||
| 2217 | root_path_len = strlen(root_path); | ||
| 2218 | |||
| 2219 | exist = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 2220 | |||
| 2221 | for (cur = item_list; cur != NULL; cur = cur->next) { | ||
| 2222 | item = FOLDER_ITEM(cur->data); | ||
| 2223 | |||
| 2224 | if (root_path_len > 0 && | ||
| 2225 | strncmp(root_path, item->path, root_path_len) != 0) | ||
| 2226 | continue; | ||
| 2227 | p = item->path + root_path_len; | ||
| 2228 | if (root_path_len > 0 && *p != '/') continue; | ||
| 2229 | while (*p == '/') p++; | ||
| 2230 | if (*p == '\0') continue; | ||
| 2231 | g_hash_table_insert(exist, (gpointer)p, GINT_TO_POINTER(1)); | ||
| 2232 | } | ||
| 2233 | |||
| 2234 | for (cur = item_list; cur != NULL; cur = cur->next) { | ||
| 2235 | const gchar *q, *r; | ||
| 2236 | gchar *parent, *full_parent; | ||
| 2237 | FolderItem *new_item; | ||
| 2238 | |||
| 2239 | item = FOLDER_ITEM(cur->data); | ||
| 2240 | |||
| 2241 | if (root_path_len > 0 && | ||
| 2242 | strncmp(root_path, item->path, root_path_len) != 0) | ||
| 2243 | continue; | ||
| 2244 | p = item->path + root_path_len; | ||
| 2245 | if (root_path_len > 0 && *p != '/') continue; | ||
| 2246 | while (*p == '/') p++; | ||
| 2247 | if (*p == '\0') continue; | ||
| 2248 | |||
| 2249 | q = p; | ||
| 2250 | while ((q = strchr(q, '/')) != NULL) { | ||
| 2251 | parent = g_strndup(p, q - p); | ||
| 2252 | if (!g_hash_table_lookup(exist, parent)) { | ||
| 2253 | if (root_path_len > 0) | ||
| 2254 | full_parent = g_strconcat | ||
| 2255 | (root_path, "/", parent, NULL); | ||
| 2256 | else | ||
| 2257 | full_parent = g_strdup(parent); | ||
| 2258 | new_item = folder_item_new(g_basename(parent), | ||
| 2259 | full_parent); | ||
| 2260 | new_item->no_select = TRUE; | ||
| 2261 | add_list = g_slist_prepend(add_list, new_item); | ||
| 2262 | r = new_item->path + root_path_len; | ||
| 2263 | while (*r == '/') r++; | ||
| 2264 | g_hash_table_insert(exist, (gpointer)r, | ||
| 2265 | GINT_TO_POINTER(1)); | ||
| 2266 | debug_print("intermediate folder '%s' added\n", | ||
| 2267 | full_parent); | ||
| 2268 | g_free(full_parent); | ||
| 2269 | } | ||
| 2270 | g_free(parent); | ||
| 2271 | while (*q == '/') q++; | ||
| 2272 | } | ||
| 2273 | } | ||
| 2274 | |||
| 2275 | g_hash_table_destroy(exist); | ||
| 2276 | |||
| 2277 | add_list = g_slist_reverse(add_list); | ||
| 2278 | item_list = g_slist_concat(item_list, add_list); | ||
| 2279 | |||
| 2280 | return item_list; | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | static GSList *imap_get_part_folder_list(GSList *item_list, FolderItem *item) | ||
| 2284 | { | ||
| 2285 | FolderItem *cur_item; | ||
| 2286 | GSList *part_list = NULL, *cur; | ||
| 2287 | gint len; | ||
| 2288 | |||
| 2289 | if (!item->path) { | ||
| 2290 | debug_print("imap_get_part_folder_list(): get root folders\n"); | ||
| 2291 | for (cur = item_list; cur != NULL; cur = cur->next) { | ||
| 2292 | cur_item = FOLDER_ITEM(cur->data); | ||
| 2293 | |||
| 2294 | if (!strchr(cur_item->path, '/')) { | ||
| 2295 | part_list = g_slist_prepend(part_list, | ||
| 2296 | cur_item); | ||
| 2297 | debug_print("append '%s'\n", cur_item->path); | ||
| 2298 | } | ||
| 2299 | } | ||
| 2300 | part_list = g_slist_reverse(part_list); | ||
| 2301 | return part_list; | ||
| 2302 | } | ||
| 2303 | |||
| 2304 | len = strlen(item->path); | ||
| 2305 | debug_print("imap_get_part_folder_list(): get folders under '%s'\n", | ||
| 2306 | item->path); | ||
| 2307 | |||
| 2308 | for (cur = item_list; cur != NULL; cur = cur->next) { | ||
| 2309 | cur_item = FOLDER_ITEM(cur->data); | ||
| 2310 | |||
| 2311 | if (!strncmp(cur_item->path, item->path, len) && | ||
| 2312 | cur_item->path[len] == '/' && | ||
| 2313 | !strchr(cur_item->path + len + 1, '/')) { | ||
| 2314 | part_list = g_slist_prepend(part_list, cur_item); | ||
| 2315 | debug_print("append '%s'\n", cur_item->path); | ||
| 2316 | } | ||
| 2317 | } | ||
| 2318 | |||
| 2319 | part_list = g_slist_reverse(part_list); | ||
| 2320 | return part_list; | ||
| 2321 | } | ||
| 2322 | |||
| 2323 | static gint imap_create_tree(Folder *folder) | ||
| 2324 | { | ||
| 2325 | g_return_val_if_fail(folder != NULL, -1); | ||
| 2326 | g_return_val_if_fail(folder->node != NULL, -1); | ||
| 2327 | g_return_val_if_fail(folder->node->data != NULL, -1); | ||
| 2328 | g_return_val_if_fail(folder->account != NULL, -1); | ||
| 2329 | |||
| 2330 | if (imap_scan_tree(folder) < 0) | ||
| 2331 | return -1; | ||
| 2332 | imap_create_missing_folders(folder); | ||
| 2333 | |||
| 2334 | return 0; | ||
| 2335 | } | ||
| 2336 | |||
| 2337 | static void imap_create_missing_folders(Folder *folder) | ||
| 2338 | { | ||
| 2339 | g_return_if_fail(folder != NULL); | ||
| 2340 | |||
| 2341 | if (!folder->inbox) | ||
| 2342 | folder->inbox = imap_create_special_folder | ||
| 2343 | (folder, F_INBOX, "INBOX"); | ||
| 2344 | #if 0 | ||
| 2345 | if (!folder->outbox) | ||
| 2346 | folder->outbox = imap_create_special_folder | ||
| 2347 | (folder, F_OUTBOX, "Sent"); | ||
| 2348 | if (!folder->draft) | ||
| 2349 | folder->draft = imap_create_special_folder | ||
| 2350 | (folder, F_DRAFT, "Drafts"); | ||
| 2351 | if (!folder->queue) | ||
| 2352 | folder->queue = imap_create_special_folder | ||
| 2353 | (folder, F_QUEUE, "Queue"); | ||
| 2354 | #endif | ||
| 2355 | if (!folder->trash) | ||
| 2356 | folder->trash = imap_create_special_folder | ||
| 2357 | (folder, F_TRASH, "Trash"); | ||
| 2358 | } | ||
| 2359 | |||
| 2360 | static FolderItem *imap_create_special_folder(Folder *folder, | ||
| 2361 | SpecialFolderItemType stype, | ||
| 2362 | const gchar *name) | ||
| 2363 | { | ||
| 2364 | FolderItem *item; | ||
| 2365 | FolderItem *new_item; | ||
| 2366 | |||
| 2367 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 2368 | g_return_val_if_fail(folder->node != NULL, NULL); | ||
| 2369 | g_return_val_if_fail(folder->node->data != NULL, NULL); | ||
| 2370 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 2371 | g_return_val_if_fail(name != NULL, NULL); | ||
| 2372 | |||
| 2373 | item = FOLDER_ITEM(folder->node->data); | ||
| 2374 | new_item = imap_create_folder(folder, item, name); | ||
| 2375 | |||
| 2376 | if (!new_item) { | ||
| 2377 | g_warning(_("Can't create '%s'\n"), name); | ||
| 2378 | if (!folder->inbox) return NULL; | ||
| 2379 | |||
| 2380 | new_item = imap_create_folder(folder, folder->inbox, name); | ||
| 2381 | if (!new_item) | ||
| 2382 | g_warning(_("Can't create '%s' under INBOX\n"), name); | ||
| 2383 | else | ||
| 2384 | new_item->stype = stype; | ||
| 2385 | } else | ||
| 2386 | new_item->stype = stype; | ||
| 2387 | |||
| 2388 | return new_item; | ||
| 2389 | } | ||
| 2390 | |||
| 2391 | static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent, | ||
| 2392 | const gchar *name) | ||
| 2393 | { | ||
| 2394 | gchar *dirpath, *imap_path; | ||
| 2395 | IMAPSession *session; | ||
| 2396 | FolderItem *new_item; | ||
| 2397 | gchar separator; | ||
| 2398 | gchar *new_name; | ||
| 2399 | const gchar *p; | ||
| 2400 | gboolean no_sub = FALSE, no_select = FALSE; | ||
| 2401 | gint ok; | ||
| 2402 | |||
| 2403 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 2404 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 2405 | g_return_val_if_fail(parent != NULL, NULL); | ||
| 2406 | g_return_val_if_fail(name != NULL, NULL); | ||
| 2407 | |||
| 2408 | session = imap_session_get(folder); | ||
| 2409 | if (!session) return NULL; | ||
| 2410 | |||
| 2411 | if (!parent->parent && g_ascii_strcasecmp(name, "INBOX") == 0) | ||
| 2412 | dirpath = g_strdup(name); | ||
| 2413 | else if (parent->path) | ||
| 2414 | dirpath = g_strconcat(parent->path, "/", name, NULL); | ||
| 2415 | else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0') | ||
| 2416 | dirpath = g_strdup(name); | ||
| 2417 | else if (folder->account->imap_dir && *folder->account->imap_dir) { | ||
| 2418 | gchar *imap_dir; | ||
| 2419 | |||
| 2420 | Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL); | ||
| 2421 | strtailchomp(imap_dir, '/'); | ||
| 2422 | dirpath = g_strconcat(imap_dir, "/", name, NULL); | ||
| 2423 | } else | ||
| 2424 | dirpath = g_strdup(name); | ||
| 2425 | |||
| 2426 | /* keep trailing directory separator to create a folder that contains | ||
| 2427 | sub folder */ | ||
| 2428 | imap_path = imap_utf8_to_modified_utf7(dirpath); | ||
| 2429 | strtailchomp(dirpath, '/'); | ||
| 2430 | Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;}); | ||
| 2431 | strtailchomp(new_name, '/'); | ||
| 2432 | separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path); | ||
| 2433 | imap_path_separator_subst(imap_path, separator); | ||
| 2434 | subst_char(new_name, '/', separator); | ||
| 2435 | |||
| 2436 | if (g_ascii_strcasecmp(name, "INBOX") != 0) { | ||
| 2437 | GPtrArray *argbuf; | ||
| 2438 | gint i; | ||
| 2439 | gboolean exist = FALSE; | ||
| 2440 | |||
| 2441 | argbuf = g_ptr_array_new(); | ||
| 2442 | ok = imap_cmd_list(session, NULL, imap_path, argbuf); | ||
| 2443 | if (ok != IMAP_SUCCESS) { | ||
| 2444 | log_warning(_("can't create mailbox: LIST failed\n")); | ||
| 2445 | g_free(imap_path); | ||
| 2446 | g_free(dirpath); | ||
| 2447 | g_ptr_array_free(argbuf, TRUE); | ||
| 2448 | return NULL; | ||
| 2449 | } | ||
| 2450 | |||
| 2451 | for (i = 0; i < argbuf->len; i++) { | ||
| 2452 | gchar *str; | ||
| 2453 | str = g_ptr_array_index(argbuf, i); | ||
| 2454 | if (!strncmp(str, "LIST ", 5)) { | ||
| 2455 | exist = TRUE; | ||
| 2456 | if (strcasestr(str + 5, "\\Noinferiors")) | ||
| 2457 | no_sub = TRUE; | ||
| 2458 | if (strcasestr(str + 5, "\\Noselect")) | ||
| 2459 | no_select = TRUE; | ||
| 2460 | break; | ||
| 2461 | } | ||
| 2462 | } | ||
| 2463 | |||
| 2464 | g_ptr_array_free(argbuf, TRUE); | ||
| 2465 | argbuf = NULL; | ||
| 2466 | |||
| 2467 | if (!exist) { | ||
| 2468 | ok = imap_cmd_create(session, imap_path); | ||
| 2469 | if (ok != IMAP_SUCCESS) { | ||
| 2470 | log_warning(_("can't create mailbox\n")); | ||
| 2471 | g_free(imap_path); | ||
| 2472 | g_free(dirpath); | ||
| 2473 | return NULL; | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | ok = imap_cmd_subscribe(session, imap_path); | ||
| 2477 | if (ok != IMAP_SUCCESS) { | ||
| 2478 | log_warning(_("can't subscribe mailbox\n")); | ||
| 2479 | } | ||
| 2480 | |||
| 2481 | argbuf = g_ptr_array_new(); | ||
| 2482 | ok = imap_cmd_list(session, NULL, imap_path, argbuf); | ||
| 2483 | if (ok != IMAP_SUCCESS) { | ||
| 2484 | log_warning("LIST failed\n"); | ||
| 2485 | g_free(imap_path); | ||
| 2486 | g_free(dirpath); | ||
| 2487 | g_ptr_array_free(argbuf, TRUE); | ||
| 2488 | return NULL; | ||
| 2489 | } | ||
| 2490 | |||
| 2491 | for (i = 0; i < argbuf->len; i++) { | ||
| 2492 | gchar *str; | ||
| 2493 | str = g_ptr_array_index(argbuf, i); | ||
| 2494 | if (!strncmp(str, "LIST ", 5)) { | ||
| 2495 | if (strcasestr(str + 5, "\\Noinferiors")) | ||
| 2496 | no_sub = TRUE; | ||
| 2497 | if (strcasestr(str + 5, "\\Noselect")) | ||
| 2498 | no_select = TRUE; | ||
| 2499 | break; | ||
| 2500 | } | ||
| 2501 | } | ||
| 2502 | g_ptr_array_free(argbuf, TRUE); | ||
| 2503 | } | ||
| 2504 | |||
| 2505 | } | ||
| 2506 | |||
| 2507 | new_item = folder_item_new(new_name, dirpath); | ||
| 2508 | new_item->no_sub = no_sub; | ||
| 2509 | new_item->no_select = no_select; | ||
| 2510 | folder_item_append(parent, new_item); | ||
| 2511 | g_free(imap_path); | ||
| 2512 | g_free(dirpath); | ||
| 2513 | |||
| 2514 | dirpath = folder_item_get_path(new_item); | ||
| 2515 | if (!is_dir_exist(dirpath)) | ||
| 2516 | make_dir_hier(dirpath); | ||
| 2517 | g_free(dirpath); | ||
| 2518 | |||
| 2519 | return new_item; | ||
| 2520 | } | ||
| 2521 | |||
| 2522 | static gint imap_rename_folder_real(Folder *folder, FolderItem *item, | ||
| 2523 | FolderItem *new_parent, const gchar *name) | ||
| 2524 | { | ||
| 2525 | gchar *newpath; | ||
| 2526 | gchar *real_oldpath; | ||
| 2527 | gchar *real_newpath; | ||
| 2528 | gchar *paths[2]; | ||
| 2529 | gchar *old_cache_dir; | ||
| 2530 | gchar *new_cache_dir; | ||
| 2531 | IMAPSession *session; | ||
| 2532 | gchar separator; | ||
| 2533 | gint ok; | ||
| 2534 | gint exists, recent, unseen; | ||
| 2535 | guint32 uid_validity; | ||
| 2536 | gchar *old_id, *new_id; | ||
| 2537 | |||
| 2538 | g_return_val_if_fail(folder != NULL, -1); | ||
| 2539 | g_return_val_if_fail(item != NULL, -1); | ||
| 2540 | g_return_val_if_fail(folder == item->folder, -1); | ||
| 2541 | g_return_val_if_fail(item->path != NULL, -1); | ||
| 2542 | g_return_val_if_fail(new_parent != NULL || name != NULL, -1); | ||
| 2543 | if (new_parent) { | ||
| 2544 | g_return_val_if_fail(item != new_parent, -1); | ||
| 2545 | g_return_val_if_fail(item->parent != new_parent, -1); | ||
| 2546 | g_return_val_if_fail(item->folder == new_parent->folder, -1); | ||
| 2547 | if (g_node_is_ancestor(item->node, new_parent->node)) { | ||
| 2548 | g_warning("folder to be moved is ancestor of new parent\n"); | ||
| 2549 | return -1; | ||
| 2550 | } | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | session = imap_session_get(folder); | ||
| 2554 | if (!session) return -1; | ||
| 2555 | |||
| 2556 | real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path); | ||
| 2557 | |||
| 2558 | g_free(session->mbox); | ||
| 2559 | session->mbox = NULL; | ||
| 2560 | ok = imap_cmd_examine(session, "INBOX", | ||
| 2561 | &exists, &recent, &unseen, &uid_validity); | ||
| 2562 | if (ok != IMAP_SUCCESS) { | ||
| 2563 | g_free(real_oldpath); | ||
| 2564 | return -1; | ||
| 2565 | } | ||
| 2566 | |||
| 2567 | separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path); | ||
| 2568 | if (new_parent) { | ||
| 2569 | if (name) { | ||
| 2570 | if (new_parent->path) | ||
| 2571 | newpath = g_strconcat(new_parent->path, | ||
| 2572 | "/", name, NULL); | ||
| 2573 | else | ||
| 2574 | newpath = g_strdup(name); | ||
| 2575 | } else { | ||
| 2576 | gchar *name_; | ||
| 2577 | |||
| 2578 | name_ = g_path_get_basename(item->path); | ||
| 2579 | if (new_parent->path) | ||
| 2580 | newpath = g_strconcat(new_parent->path, | ||
| 2581 | "/", name_, NULL); | ||
| 2582 | else | ||
| 2583 | newpath = g_strdup(name_); | ||
| 2584 | AUTORELEASE_STR(name_, ); | ||
| 2585 | name = name_; | ||
| 2586 | } | ||
| 2587 | } else { | ||
| 2588 | if (strchr(item->path, '/')) { | ||
| 2589 | gchar *dirpath; | ||
| 2590 | |||
| 2591 | dirpath = g_dirname(item->path); | ||
| 2592 | newpath = g_strconcat(dirpath, "/", name, NULL); | ||
| 2593 | g_free(dirpath); | ||
| 2594 | } else | ||
| 2595 | newpath = g_strdup(name); | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | real_newpath = imap_utf8_to_modified_utf7(newpath); | ||
| 2599 | imap_path_separator_subst(real_newpath, separator); | ||
| 2600 | |||
| 2601 | ok = imap_cmd_rename(session, real_oldpath, real_newpath); | ||
| 2602 | if (ok != IMAP_SUCCESS) { | ||
| 2603 | log_warning(_("can't rename mailbox: %s to %s\n"), | ||
| 2604 | real_oldpath, real_newpath); | ||
| 2605 | g_free(real_oldpath); | ||
| 2606 | g_free(newpath); | ||
| 2607 | g_free(real_newpath); | ||
| 2608 | return -1; | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | old_id = folder_item_get_identifier(item); | ||
| 2612 | |||
| 2613 | if (new_parent) { | ||
| 2614 | g_node_unlink(item->node); | ||
| 2615 | g_node_append(new_parent->node, item->node); | ||
| 2616 | item->parent = new_parent; | ||
| 2617 | } | ||
| 2618 | |||
| 2619 | g_free(item->name); | ||
| 2620 | item->name = g_strdup(name); | ||
| 2621 | |||
| 2622 | old_cache_dir = folder_item_get_path(item); | ||
| 2623 | |||
| 2624 | paths[0] = g_strdup(item->path); | ||
| 2625 | paths[1] = newpath; | ||
| 2626 | g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 2627 | imap_rename_folder_func, paths); | ||
| 2628 | |||
| 2629 | if (is_dir_exist(old_cache_dir)) { | ||
| 2630 | new_cache_dir = folder_item_get_path(item); | ||
| 2631 | if (g_rename(old_cache_dir, new_cache_dir) < 0) { | ||
| 2632 | FILE_OP_ERROR(old_cache_dir, "rename"); | ||
| 2633 | } | ||
| 2634 | g_free(new_cache_dir); | ||
| 2635 | } | ||
| 2636 | |||
| 2637 | g_free(old_cache_dir); | ||
| 2638 | g_free(paths[0]); | ||
| 2639 | g_free(newpath); | ||
| 2640 | g_free(real_oldpath); | ||
| 2641 | g_free(real_newpath); | ||
| 2642 | |||
| 2643 | new_id = folder_item_get_identifier(item); | ||
| 2644 | if (syl_app_get()) | ||
| 2645 | g_signal_emit_by_name(syl_app_get(), "move-folder", item, | ||
| 2646 | old_id, new_id); | ||
| 2647 | g_free(new_id); | ||
| 2648 | g_free(old_id); | ||
| 2649 | |||
| 2650 | return 0; | ||
| 2651 | } | ||
| 2652 | |||
| 2653 | static gint imap_rename_folder(Folder *folder, FolderItem *item, | ||
| 2654 | const gchar *name) | ||
| 2655 | { | ||
| 2656 | return imap_rename_folder_real(folder, item, NULL, name); | ||
| 2657 | } | ||
| 2658 | |||
| 2659 | static gint imap_move_folder(Folder *folder, FolderItem *item, | ||
| 2660 | FolderItem *new_parent) | ||
| 2661 | { | ||
| 2662 | return imap_rename_folder_real(folder, item, new_parent, NULL); | ||
| 2663 | } | ||
| 2664 | |||
| 2665 | static gint imap_remove_folder(Folder *folder, FolderItem *item) | ||
| 2666 | { | ||
| 2667 | gint ok; | ||
| 2668 | IMAPSession *session; | ||
| 2669 | gchar *path; | ||
| 2670 | gchar *cache_dir; | ||
| 2671 | gint exists, recent, unseen; | ||
| 2672 | guint32 uid_validity; | ||
| 2673 | |||
| 2674 | g_return_val_if_fail(folder != NULL, -1); | ||
| 2675 | g_return_val_if_fail(item != NULL, -1); | ||
| 2676 | g_return_val_if_fail(item->path != NULL, -1); | ||
| 2677 | |||
| 2678 | session = imap_session_get(folder); | ||
| 2679 | if (!session) return -1; | ||
| 2680 | |||
| 2681 | path = imap_get_real_path(IMAP_FOLDER(folder), item->path); | ||
| 2682 | |||
| 2683 | ok = imap_cmd_examine(session, "INBOX", | ||
| 2684 | &exists, &recent, &unseen, &uid_validity); | ||
| 2685 | if (ok != IMAP_SUCCESS) { | ||
| 2686 | g_free(path); | ||
| 2687 | return -1; | ||
| 2688 | } | ||
| 2689 | |||
| 2690 | ok = imap_cmd_delete(session, path); | ||
| 2691 | if (ok != IMAP_SUCCESS) { | ||
| 2692 | log_warning(_("can't delete mailbox\n")); | ||
| 2693 | g_free(path); | ||
| 2694 | return -1; | ||
| 2695 | } | ||
| 2696 | |||
| 2697 | g_free(path); | ||
| 2698 | cache_dir = folder_item_get_path(item); | ||
| 2699 | if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0) | ||
| 2700 | g_warning("can't remove directory '%s'\n", cache_dir); | ||
| 2701 | g_free(cache_dir); | ||
| 2702 | |||
| 2703 | if (syl_app_get()) | ||
| 2704 | g_signal_emit_by_name(syl_app_get(), "remove-folder", item); | ||
| 2705 | folder_item_remove(item); | ||
| 2706 | |||
| 2707 | return 0; | ||
| 2708 | } | ||
| 2709 | |||
| 2710 | typedef struct _IMAPGetData | ||
| 2711 | { | ||
| 2712 | FolderItem *item; | ||
| 2713 | gint exists; | ||
| 2714 | gboolean update_count; | ||
| 2715 | GSList *newlist; | ||
| 2716 | } IMAPGetData; | ||
| 2717 | |||
| 2718 | static gint imap_get_uncached_messages_progress_func(IMAPSession *session, | ||
| 2719 | gint count, gint total, | ||
| 2720 | gpointer data) | ||
| 2721 | { | ||
| 2722 | status_print(_("Getting message headers (%d / %d)"), count, total); | ||
| 2723 | progress_show(count, total); | ||
| 2724 | #ifndef USE_THREADS | ||
| 2725 | ui_update(); | ||
| 2726 | #endif | ||
| 2727 | return 0; | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | static gint imap_get_uncached_messages_func(IMAPSession *session, gpointer data) | ||
| 2731 | { | ||
| 2732 | gchar *tmp; | ||
| 2733 | GSList *newlist = NULL; | ||
| 2734 | GSList *llast = NULL; | ||
| 2735 | GString *str; | ||
| 2736 | MsgInfo *msginfo; | ||
| 2737 | gint count = 1; | ||
| 2738 | GTimeVal tv_prev, tv_cur; | ||
| 2739 | IMAPGetData *get_data = (IMAPGetData *)data; | ||
| 2740 | FolderItem *item = get_data->item; | ||
| 2741 | gint exists = get_data->exists; | ||
| 2742 | gboolean update_count = get_data->update_count; | ||
| 2743 | |||
| 2744 | g_get_current_time(&tv_prev); | ||
| 2745 | #ifndef USE_THREADS | ||
| 2746 | ui_update(); | ||
| 2747 | #endif | ||
| 2748 | |||
| 2749 | #if USE_THREADS | ||
| 2750 | ((IMAPRealSession *)session)->prog_total = exists; | ||
| 2751 | #endif | ||
| 2752 | |||
| 2753 | str = g_string_new(NULL); | ||
| 2754 | |||
| 2755 | for (;;) { | ||
| 2756 | if (exists > 0 && count <= exists) { | ||
| 2757 | g_get_current_time(&tv_cur); | ||
| 2758 | if (tv_cur.tv_sec > tv_prev.tv_sec || | ||
| 2759 | tv_cur.tv_usec - tv_prev.tv_usec > | ||
| 2760 | PROGRESS_UPDATE_INTERVAL * 1000) { | ||
| 2761 | #if USE_THREADS | ||
| 2762 | ((IMAPRealSession *)session)->prog_count = count; | ||
| 2763 | g_main_context_wakeup(NULL); | ||
| 2764 | #else | ||
| 2765 | imap_get_uncached_messages_progress_func | ||
| 2766 | (session, count, exists, data); | ||
| 2767 | #endif | ||
| 2768 | tv_prev = tv_cur; | ||
| 2769 | } | ||
| 2770 | } | ||
| 2771 | ++count; | ||
| 2772 | |||
| 2773 | if (sock_getline(SESSION(session)->sock, &tmp) < 0) { | ||
| 2774 | log_warning(_("error occurred while getting envelope.\n")); | ||
| 2775 | g_string_free(str, TRUE); | ||
| 2776 | return IMAP_SOCKET; | ||
| 2777 | } | ||
| 2778 | strretchomp(tmp); | ||
| 2779 | if (tmp[0] != '*' || tmp[1] != ' ') { | ||
| 2780 | log_print("IMAP4< %s\n", tmp); | ||
| 2781 | g_free(tmp); | ||
| 2782 | break; | ||
| 2783 | } | ||
| 2784 | if (strstr(tmp, "FETCH") == NULL) { | ||
| 2785 | log_print("IMAP4< %s\n", tmp); | ||
| 2786 | g_free(tmp); | ||
| 2787 | continue; | ||
| 2788 | } | ||
| 2789 | log_print("IMAP4< %s\n", tmp); | ||
| 2790 | g_string_assign(str, tmp); | ||
| 2791 | g_free(tmp); | ||
| 2792 | |||
| 2793 | msginfo = imap_parse_envelope(session, item, str); | ||
| 2794 | if (!msginfo) { | ||
| 2795 | log_warning(_("can't parse envelope: %s\n"), str->str); | ||
| 2796 | continue; | ||
| 2797 | } | ||
| 2798 | if (update_count) { | ||
| 2799 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 2800 | item->new++; | ||
| 2801 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 2802 | item->unread++; | ||
| 2803 | } | ||
| 2804 | if (item->stype == F_QUEUE) { | ||
| 2805 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED); | ||
| 2806 | } else if (item->stype == F_DRAFT) { | ||
| 2807 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT); | ||
| 2808 | } | ||
| 2809 | |||
| 2810 | msginfo->folder = item; | ||
| 2811 | |||
| 2812 | if (!newlist) | ||
| 2813 | llast = newlist = g_slist_append(newlist, msginfo); | ||
| 2814 | else { | ||
| 2815 | llast = g_slist_append(llast, msginfo); | ||
| 2816 | llast = llast->next; | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | if (update_count) | ||
| 2820 | item->total++; | ||
| 2821 | } | ||
| 2822 | |||
| 2823 | g_string_free(str, TRUE); | ||
| 2824 | |||
| 2825 | session_set_access_time(SESSION(session)); | ||
| 2826 | |||
| 2827 | get_data->newlist = newlist; | ||
| 2828 | return IMAP_SUCCESS; | ||
| 2829 | } | ||
| 2830 | |||
| 2831 | static GSList *imap_get_uncached_messages(IMAPSession *session, | ||
| 2832 | FolderItem *item, | ||
| 2833 | guint32 first_uid, guint32 last_uid, | ||
| 2834 | gint exists, gboolean update_count) | ||
| 2835 | { | ||
| 2836 | IMAPGetData get_data = {item, exists, update_count, NULL}; | ||
| 2837 | gchar seq_set[22]; | ||
| 2838 | gint ok; | ||
| 2839 | |||
| 2840 | g_return_val_if_fail(session != NULL, NULL); | ||
| 2841 | g_return_val_if_fail(item != NULL, NULL); | ||
| 2842 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 2843 | g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL); | ||
| 2844 | g_return_val_if_fail(first_uid <= last_uid, NULL); | ||
| 2845 | |||
| 2846 | if (first_uid == 0 && last_uid == 0) | ||
| 2847 | strcpy(seq_set, "1:*"); | ||
| 2848 | else | ||
| 2849 | g_snprintf(seq_set, sizeof(seq_set), "%u:%u", | ||
| 2850 | first_uid, last_uid); | ||
| 2851 | if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) { | ||
| 2852 | log_warning(_("can't get envelope\n")); | ||
| 2853 | return NULL; | ||
| 2854 | } | ||
| 2855 | |||
| 2856 | #if USE_THREADS | ||
| 2857 | ok = imap_thread_run_progress(session, imap_get_uncached_messages_func, | ||
| 2858 | imap_get_uncached_messages_progress_func, | ||
| 2859 | &get_data); | ||
| 2860 | #else | ||
| 2861 | ok = imap_get_uncached_messages_func(session, &get_data); | ||
| 2862 | #endif | ||
| 2863 | |||
| 2864 | progress_show(0, 0); | ||
| 2865 | return get_data.newlist; | ||
| 2866 | } | ||
| 2867 | |||
| 2868 | static void imap_delete_cached_message(FolderItem *item, guint32 uid) | ||
| 2869 | { | ||
| 2870 | gchar *dir; | ||
| 2871 | gchar *file; | ||
| 2872 | |||
| 2873 | g_return_if_fail(item != NULL); | ||
| 2874 | g_return_if_fail(item->folder != NULL); | ||
| 2875 | g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP); | ||
| 2876 | |||
| 2877 | dir = folder_item_get_path(item); | ||
| 2878 | file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid); | ||
| 2879 | |||
| 2880 | debug_print("Deleting cached message: %s\n", file); | ||
| 2881 | |||
| 2882 | g_unlink(file); | ||
| 2883 | |||
| 2884 | g_free(file); | ||
| 2885 | g_free(dir); | ||
| 2886 | } | ||
| 2887 | |||
| 2888 | static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item, | ||
| 2889 | guint32 first_uid, guint32 last_uid) | ||
| 2890 | { | ||
| 2891 | GSList *cur, *next; | ||
| 2892 | MsgInfo *msginfo; | ||
| 2893 | gchar *dir; | ||
| 2894 | |||
| 2895 | g_return_val_if_fail(item != NULL, mlist); | ||
| 2896 | g_return_val_if_fail(item->folder != NULL, mlist); | ||
| 2897 | g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist); | ||
| 2898 | |||
| 2899 | if (first_uid == 0 && last_uid == 0) | ||
| 2900 | return mlist; | ||
| 2901 | |||
| 2902 | debug_print("Deleting cached messages %u - %u ... ", | ||
| 2903 | first_uid, last_uid); | ||
| 2904 | |||
| 2905 | dir = folder_item_get_path(item); | ||
| 2906 | if (is_dir_exist(dir)) | ||
| 2907 | remove_numbered_files(dir, first_uid, last_uid); | ||
| 2908 | g_free(dir); | ||
| 2909 | |||
| 2910 | for (cur = mlist; cur != NULL; ) { | ||
| 2911 | next = cur->next; | ||
| 2912 | |||
| 2913 | msginfo = (MsgInfo *)cur->data; | ||
| 2914 | if (msginfo != NULL && first_uid <= msginfo->msgnum && | ||
| 2915 | msginfo->msgnum <= last_uid) { | ||
| 2916 | procmsg_msginfo_free(msginfo); | ||
| 2917 | mlist = g_slist_remove(mlist, msginfo); | ||
| 2918 | } | ||
| 2919 | |||
| 2920 | cur = next; | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | debug_print("done.\n"); | ||
| 2924 | |||
| 2925 | return mlist; | ||
| 2926 | } | ||
| 2927 | |||
| 2928 | static void imap_delete_all_cached_messages(FolderItem *item) | ||
| 2929 | { | ||
| 2930 | gchar *dir; | ||
| 2931 | |||
| 2932 | g_return_if_fail(item != NULL); | ||
| 2933 | g_return_if_fail(item->folder != NULL); | ||
| 2934 | g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP); | ||
| 2935 | |||
| 2936 | debug_print("Deleting all cached messages... "); | ||
| 2937 | |||
| 2938 | dir = folder_item_get_path(item); | ||
| 2939 | if (is_dir_exist(dir)) | ||
| 2940 | remove_all_numbered_files(dir); | ||
| 2941 | g_free(dir); | ||
| 2942 | |||
| 2943 | debug_print("done.\n"); | ||
| 2944 | } | ||
| 2945 | |||
| 2946 | #if USE_SSL | ||
| 2947 | static SockInfo *imap_open(const gchar *server, gushort port, | ||
| 2948 | SocksInfo *socks_info, SSLType ssl_type) | ||
| 2949 | #else | ||
| 2950 | static SockInfo *imap_open(const gchar *server, gushort port, | ||
| 2951 | SocksInfo *socks_info) | ||
| 2952 | #endif | ||
| 2953 | { | ||
| 2954 | SockInfo *sock = NULL; | ||
| 2955 | const gchar *server_; | ||
| 2956 | gushort port_; | ||
| 2957 | #if USE_THREADS | ||
| 2958 | gint conn_id; | ||
| 2959 | #endif | ||
| 2960 | |||
| 2961 | if (socks_info) { | ||
| 2962 | server_ = socks_info->proxy_host; | ||
| 2963 | port_ = socks_info->proxy_port; | ||
| 2964 | } else { | ||
| 2965 | server_ = server; | ||
| 2966 | port_ = port; | ||
| 2967 | } | ||
| 2968 | |||
| 2969 | #if USE_THREADS | ||
| 2970 | if ((conn_id = sock_connect_async_thread(server_, port_)) < 0 || | ||
| 2971 | sock_connect_async_thread_wait(conn_id, &sock) < 0) { | ||
| 2972 | log_warning(_("Can't connect to IMAP4 server: %s:%d\n"), | ||
| 2973 | server, port); | ||
| 2974 | return NULL; | ||
| 2975 | } | ||
| 2976 | #else | ||
| 2977 | if ((sock = sock_connect(server_, port_)) == NULL) { | ||
| 2978 | log_warning(_("Can't connect to IMAP4 server: %s:%d\n"), | ||
| 2979 | server, port); | ||
| 2980 | return NULL; | ||
| 2981 | } | ||
| 2982 | #endif | ||
| 2983 | |||
| 2984 | if (socks_info) { | ||
| 2985 | if (socks_connect(sock, server, port, socks_info) < 0) { | ||
| 2986 | log_warning("Can't establish SOCKS connection: %s:%d\n", | ||
| 2987 | server, port); | ||
| 2988 | sock_close(sock); | ||
| 2989 | return NULL; | ||
| 2990 | } | ||
| 2991 | } | ||
| 2992 | |||
| 2993 | #if USE_SSL | ||
| 2994 | if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) { | ||
| 2995 | log_warning(_("Can't establish IMAP4 session with: %s:%d\n"), | ||
| 2996 | server, port); | ||
| 2997 | sock_close(sock); | ||
| 2998 | return NULL; | ||
| 2999 | } | ||
| 3000 | #endif | ||
| 3001 | |||
| 3002 | return sock; | ||
| 3003 | } | ||
| 3004 | |||
| 3005 | static GList *imap_parse_namespace_str(gchar *str) | ||
| 3006 | { | ||
| 3007 | gchar *p = str; | ||
| 3008 | gchar *name; | ||
| 3009 | gchar *separator; | ||
| 3010 | IMAPNameSpace *namespace; | ||
| 3011 | GList *ns_list = NULL; | ||
| 3012 | |||
| 3013 | while (*p != '\0') { | ||
| 3014 | /* parse ("#foo" "/") */ | ||
| 3015 | |||
| 3016 | while (*p && *p != '(') p++; | ||
| 3017 | if (*p == '\0') break; | ||
| 3018 | p++; | ||
| 3019 | |||
| 3020 | while (*p && *p != '"') p++; | ||
| 3021 | if (*p == '\0') break; | ||
| 3022 | p++; | ||
| 3023 | name = p; | ||
| 3024 | |||
| 3025 | while (*p && *p != '"') p++; | ||
| 3026 | if (*p == '\0') break; | ||
| 3027 | *p = '\0'; | ||
| 3028 | p++; | ||
| 3029 | |||
| 3030 | while (*p && g_ascii_isspace(*p)) p++; | ||
| 3031 | if (*p == '\0') break; | ||
| 3032 | if (strncmp(p, "NIL", 3) == 0) | ||
| 3033 | separator = NULL; | ||
| 3034 | else if (*p == '"') { | ||
| 3035 | p++; | ||
| 3036 | separator = p; | ||
| 3037 | while (*p && *p != '"') p++; | ||
| 3038 | if (*p == '\0') break; | ||
| 3039 | *p = '\0'; | ||
| 3040 | p++; | ||
| 3041 | } else break; | ||
| 3042 | |||
| 3043 | while (*p && *p != ')') p++; | ||
| 3044 | if (*p == '\0') break; | ||
| 3045 | p++; | ||
| 3046 | |||
| 3047 | namespace = g_new(IMAPNameSpace, 1); | ||
| 3048 | namespace->name = g_strdup(name); | ||
| 3049 | namespace->separator = separator ? separator[0] : '\0'; | ||
| 3050 | ns_list = g_list_append(ns_list, namespace); | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | return ns_list; | ||
| 3054 | } | ||
| 3055 | |||
| 3056 | static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder) | ||
| 3057 | { | ||
| 3058 | gchar *ns_str = NULL; | ||
| 3059 | gchar **str_array; | ||
| 3060 | |||
| 3061 | g_return_if_fail(session != NULL); | ||
| 3062 | g_return_if_fail(folder != NULL); | ||
| 3063 | |||
| 3064 | if (folder->ns_personal != NULL || | ||
| 3065 | folder->ns_others != NULL || | ||
| 3066 | folder->ns_shared != NULL) | ||
| 3067 | return; | ||
| 3068 | |||
| 3069 | if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) { | ||
| 3070 | log_warning(_("can't get namespace\n")); | ||
| 3071 | imap_get_namespace_by_list(session, folder); | ||
| 3072 | return; | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | str_array = strsplit_parenthesis(ns_str, '(', ')', 3); | ||
| 3076 | if (str_array[0]) | ||
| 3077 | folder->ns_personal = imap_parse_namespace_str(str_array[0]); | ||
| 3078 | if (str_array[0] && str_array[1]) | ||
| 3079 | folder->ns_others = imap_parse_namespace_str(str_array[1]); | ||
| 3080 | if (str_array[0] && str_array[1] && str_array[2]) | ||
| 3081 | folder->ns_shared = imap_parse_namespace_str(str_array[2]); | ||
| 3082 | g_strfreev(str_array); | ||
| 3083 | g_free(ns_str); | ||
| 3084 | } | ||
| 3085 | |||
| 3086 | static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder) | ||
| 3087 | { | ||
| 3088 | GSList *item_list, *cur; | ||
| 3089 | gchar separator = '\0'; | ||
| 3090 | IMAPNameSpace *namespace; | ||
| 3091 | |||
| 3092 | g_return_if_fail(session != NULL); | ||
| 3093 | g_return_if_fail(folder != NULL); | ||
| 3094 | |||
| 3095 | if (folder->ns_personal != NULL || | ||
| 3096 | folder->ns_others != NULL || | ||
| 3097 | folder->ns_shared != NULL) | ||
| 3098 | return; | ||
| 3099 | |||
| 3100 | if (imap_cmd_gen_send(session, "LIST \"\" \"\"") != IMAP_SUCCESS) | ||
| 3101 | return; | ||
| 3102 | item_list = imap_parse_list(session, "", &separator); | ||
| 3103 | for (cur = item_list; cur != NULL; cur = cur->next) | ||
| 3104 | folder_item_destroy(FOLDER_ITEM(cur->data)); | ||
| 3105 | g_slist_free(item_list); | ||
| 3106 | |||
| 3107 | namespace = g_new(IMAPNameSpace, 1); | ||
| 3108 | namespace->name = g_strdup(""); | ||
| 3109 | namespace->separator = separator; | ||
| 3110 | folder->ns_personal = g_list_append(NULL, namespace); | ||
| 3111 | } | ||
| 3112 | |||
| 3113 | static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list, | ||
| 3114 | const gchar *path) | ||
| 3115 | { | ||
| 3116 | IMAPNameSpace *namespace = NULL; | ||
| 3117 | gchar *tmp_path, *name; | ||
| 3118 | |||
| 3119 | if (!path) path = ""; | ||
| 3120 | |||
| 3121 | for (; ns_list != NULL; ns_list = ns_list->next) { | ||
| 3122 | IMAPNameSpace *tmp_ns = ns_list->data; | ||
| 3123 | |||
| 3124 | Xstrcat_a(tmp_path, path, "/", return namespace); | ||
| 3125 | Xstrdup_a(name, tmp_ns->name, return namespace); | ||
| 3126 | if (tmp_ns->separator && tmp_ns->separator != '/') { | ||
| 3127 | subst_char(tmp_path, tmp_ns->separator, '/'); | ||
| 3128 | subst_char(name, tmp_ns->separator, '/'); | ||
| 3129 | } | ||
| 3130 | if (strncmp(tmp_path, name, strlen(name)) == 0) | ||
| 3131 | namespace = tmp_ns; | ||
| 3132 | } | ||
| 3133 | |||
| 3134 | return namespace; | ||
| 3135 | } | ||
| 3136 | |||
| 3137 | static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder, | ||
| 3138 | const gchar *path) | ||
| 3139 | { | ||
| 3140 | IMAPNameSpace *namespace; | ||
| 3141 | |||
| 3142 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 3143 | |||
| 3144 | namespace = imap_find_namespace_from_list(folder->ns_personal, path); | ||
| 3145 | if (namespace) return namespace; | ||
| 3146 | namespace = imap_find_namespace_from_list(folder->ns_others, path); | ||
| 3147 | if (namespace) return namespace; | ||
| 3148 | namespace = imap_find_namespace_from_list(folder->ns_shared, path); | ||
| 3149 | if (namespace) return namespace; | ||
| 3150 | |||
| 3151 | return NULL; | ||
| 3152 | } | ||
| 3153 | |||
| 3154 | static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path) | ||
| 3155 | { | ||
| 3156 | IMAPNameSpace *namespace; | ||
| 3157 | gchar separator = '/'; | ||
| 3158 | |||
| 3159 | namespace = imap_find_namespace(folder, path); | ||
| 3160 | if (namespace && namespace->separator) | ||
| 3161 | separator = namespace->separator; | ||
| 3162 | |||
| 3163 | return separator; | ||
| 3164 | } | ||
| 3165 | |||
| 3166 | static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path) | ||
| 3167 | { | ||
| 3168 | gchar *real_path; | ||
| 3169 | gchar separator; | ||
| 3170 | |||
| 3171 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 3172 | g_return_val_if_fail(path != NULL, NULL); | ||
| 3173 | |||
| 3174 | real_path = imap_utf8_to_modified_utf7(path); | ||
| 3175 | separator = imap_get_path_separator(folder, path); | ||
| 3176 | imap_path_separator_subst(real_path, separator); | ||
| 3177 | |||
| 3178 | return real_path; | ||
| 3179 | } | ||
| 3180 | |||
| 3181 | static gchar *imap_parse_atom(IMAPSession *session, gchar *src, | ||
| 3182 | gchar *dest, gint dest_len, GString *str) | ||
| 3183 | { | ||
| 3184 | gchar *cur_pos = src; | ||
| 3185 | gchar *nextline; | ||
| 3186 | |||
| 3187 | g_return_val_if_fail(str != NULL, cur_pos); | ||
| 3188 | |||
| 3189 | /* read the next line if the current response buffer is empty */ | ||
| 3190 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3191 | while (*cur_pos == '\0') { | ||
| 3192 | if (sock_getline(SESSION(session)->sock, &nextline) < 0) | ||
| 3193 | return cur_pos; | ||
| 3194 | g_string_assign(str, nextline); | ||
| 3195 | cur_pos = str->str; | ||
| 3196 | strretchomp(nextline); | ||
| 3197 | /* log_print("IMAP4< %s\n", nextline); */ | ||
| 3198 | debug_print("IMAP4< %s\n", nextline); | ||
| 3199 | g_free(nextline); | ||
| 3200 | |||
| 3201 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3202 | } | ||
| 3203 | |||
| 3204 | if (*cur_pos == '~' && *(cur_pos + 1) == '{') | ||
| 3205 | cur_pos++; | ||
| 3206 | |||
| 3207 | if (!strncmp(cur_pos, "NIL", 3)) { | ||
| 3208 | *dest = '\0'; | ||
| 3209 | cur_pos += 3; | ||
| 3210 | } else if (*cur_pos == '\"') { | ||
| 3211 | gchar *p; | ||
| 3212 | |||
| 3213 | p = get_quoted(cur_pos, '\"', dest, dest_len); | ||
| 3214 | cur_pos = p ? p : cur_pos + 2; | ||
| 3215 | } else if (*cur_pos == '{') { | ||
| 3216 | gchar buf[32]; | ||
| 3217 | gint len; | ||
| 3218 | gint block_len = 0; | ||
| 3219 | |||
| 3220 | cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf)); | ||
| 3221 | len = atoi(buf); | ||
| 3222 | g_return_val_if_fail(len >= 0, cur_pos); | ||
| 3223 | |||
| 3224 | g_string_truncate(str, 0); | ||
| 3225 | cur_pos = str->str; | ||
| 3226 | |||
| 3227 | do { | ||
| 3228 | gint cur_len; | ||
| 3229 | |||
| 3230 | cur_len = sock_getline(SESSION(session)->sock, | ||
| 3231 | &nextline); | ||
| 3232 | if (cur_len < 0) | ||
| 3233 | return cur_pos; | ||
| 3234 | block_len += cur_len; | ||
| 3235 | subst_null(nextline, cur_len, ' '); | ||
| 3236 | g_string_append(str, nextline); | ||
| 3237 | cur_pos = str->str; | ||
| 3238 | strretchomp(nextline); | ||
| 3239 | /* log_print("IMAP4< %s\n", nextline); */ | ||
| 3240 | debug_print("IMAP4< %s\n", nextline); | ||
| 3241 | g_free(nextline); | ||
| 3242 | } while (block_len < len); | ||
| 3243 | |||
| 3244 | memcpy(dest, cur_pos, MIN(len, dest_len - 1)); | ||
| 3245 | dest[MIN(len, dest_len - 1)] = '\0'; | ||
| 3246 | cur_pos += len; | ||
| 3247 | } | ||
| 3248 | |||
| 3249 | return cur_pos; | ||
| 3250 | } | ||
| 3251 | |||
| 3252 | static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos, | ||
| 3253 | gchar **headers, GString *str) | ||
| 3254 | { | ||
| 3255 | gchar *nextline; | ||
| 3256 | gchar buf[IMAPBUFSIZE]; | ||
| 3257 | gint len; | ||
| 3258 | gint block_len = 0; | ||
| 3259 | |||
| 3260 | *headers = NULL; | ||
| 3261 | |||
| 3262 | g_return_val_if_fail(str != NULL, cur_pos); | ||
| 3263 | |||
| 3264 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3265 | if (*cur_pos == '~' && *(cur_pos + 1) == '{') | ||
| 3266 | cur_pos++; | ||
| 3267 | |||
| 3268 | if (*cur_pos == '"') { | ||
| 3269 | cur_pos = strchr_cpy(cur_pos + 1, '"', buf, sizeof(buf)); | ||
| 3270 | if (!cur_pos) | ||
| 3271 | return NULL; | ||
| 3272 | len = strlen(buf); | ||
| 3273 | *headers = g_strdup(buf); | ||
| 3274 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3275 | return cur_pos; | ||
| 3276 | } | ||
| 3277 | |||
| 3278 | g_return_val_if_fail(*cur_pos == '{', cur_pos); | ||
| 3279 | |||
| 3280 | cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf)); | ||
| 3281 | len = atoi(buf); | ||
| 3282 | g_return_val_if_fail(len >= 0, cur_pos); | ||
| 3283 | |||
| 3284 | g_string_truncate(str, 0); | ||
| 3285 | cur_pos = str->str; | ||
| 3286 | |||
| 3287 | do { | ||
| 3288 | gint cur_len; | ||
| 3289 | |||
| 3290 | cur_len = sock_getline(SESSION(session)->sock, &nextline); | ||
| 3291 | if (cur_len < 0) | ||
| 3292 | return cur_pos; | ||
| 3293 | block_len += cur_len; | ||
| 3294 | subst_null(nextline, cur_len, ' '); | ||
| 3295 | g_string_append(str, nextline); | ||
| 3296 | cur_pos = str->str; | ||
| 3297 | /* strretchomp(nextline); */ | ||
| 3298 | /* debug_print("IMAP4< %s\n", nextline); */ | ||
| 3299 | g_free(nextline); | ||
| 3300 | } while (block_len < len); | ||
| 3301 | |||
| 3302 | debug_print("IMAP4< [contents of RFC822.HEADER]\n"); | ||
| 3303 | |||
| 3304 | *headers = g_strndup(cur_pos, len); | ||
| 3305 | cur_pos += len; | ||
| 3306 | |||
| 3307 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3308 | while (*cur_pos == '\0') { | ||
| 3309 | if (sock_getline(SESSION(session)->sock, &nextline) < 0) | ||
| 3310 | return cur_pos; | ||
| 3311 | g_string_assign(str, nextline); | ||
| 3312 | cur_pos = str->str; | ||
| 3313 | strretchomp(nextline); | ||
| 3314 | debug_print("IMAP4< %s\n", nextline); | ||
| 3315 | g_free(nextline); | ||
| 3316 | |||
| 3317 | while (g_ascii_isspace(*cur_pos)) cur_pos++; | ||
| 3318 | } | ||
| 3319 | |||
| 3320 | return cur_pos; | ||
| 3321 | } | ||
| 3322 | |||
| 3323 | static MsgFlags imap_parse_flags(const gchar *flag_str) | ||
| 3324 | { | ||
| 3325 | const gchar *p = flag_str; | ||
| 3326 | MsgFlags flags = {0, 0}; | ||
| 3327 | |||
| 3328 | flags.perm_flags = MSG_UNREAD; | ||
| 3329 | |||
| 3330 | while (*p != '\0') { | ||
| 3331 | if (g_ascii_strncasecmp(p, "\\Recent", 7) == 0 && | ||
| 3332 | MSG_IS_UNREAD(flags)) { | ||
| 3333 | MSG_SET_PERM_FLAGS(flags, MSG_NEW); | ||
| 3334 | } else if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) { | ||
| 3335 | MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD); | ||
| 3336 | } else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) { | ||
| 3337 | MSG_SET_PERM_FLAGS(flags, MSG_DELETED); | ||
| 3338 | } else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) { | ||
| 3339 | MSG_SET_PERM_FLAGS(flags, MSG_MARKED); | ||
| 3340 | } else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) { | ||
| 3341 | MSG_SET_PERM_FLAGS(flags, MSG_REPLIED); | ||
| 3342 | } else if (g_ascii_strncasecmp(p, "$label", 6) == 0) { | ||
| 3343 | /* color labels */ | ||
| 3344 | if (*(p + 6) >= '1' && *(p + 6) <= '7') { | ||
| 3345 | guint color = *(p + 6) - '1' + 1; | ||
| 3346 | MSG_UNSET_PERM_FLAGS(flags, | ||
| 3347 | MSG_CLABEL_FLAG_MASK); | ||
| 3348 | MSG_SET_COLORLABEL_VALUE(flags, color); | ||
| 3349 | } | ||
| 3350 | } | ||
| 3351 | |||
| 3352 | while (*p && !g_ascii_isspace(*p)) p++; | ||
| 3353 | while (g_ascii_isspace(*p)) p++; | ||
| 3354 | } | ||
| 3355 | |||
| 3356 | return flags; | ||
| 3357 | } | ||
| 3358 | |||
| 3359 | static IMAPFlags imap_parse_imap_flags(const gchar *flag_str) | ||
| 3360 | { | ||
| 3361 | const gchar *p = flag_str; | ||
| 3362 | IMAPFlags flags = 0; | ||
| 3363 | |||
| 3364 | while (*p != '\0') { | ||
| 3365 | if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) { | ||
| 3366 | flags |= IMAP_FLAG_SEEN; | ||
| 3367 | } else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) { | ||
| 3368 | flags |= IMAP_FLAG_DELETED; | ||
| 3369 | } else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) { | ||
| 3370 | flags |= IMAP_FLAG_FLAGGED; | ||
| 3371 | } else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) { | ||
| 3372 | flags |= IMAP_FLAG_ANSWERED; | ||
| 3373 | } else if (g_ascii_strncasecmp(p, "$label", 6) == 0) { | ||
| 3374 | /* color labels */ | ||
| 3375 | if (*(p + 6) >= '1' && *(p + 6) <= '7') { | ||
| 3376 | guint color = *(p + 6) - '1' + 1; | ||
| 3377 | MSG_UNSET_FLAGS(flags, MSG_CLABEL_FLAG_MASK); | ||
| 3378 | IMAP_SET_COLORLABEL_VALUE(flags, color); | ||
| 3379 | } | ||
| 3380 | } | ||
| 3381 | |||
| 3382 | while (*p && !g_ascii_isspace(*p)) p++; | ||
| 3383 | while (g_ascii_isspace(*p)) p++; | ||
| 3384 | } | ||
| 3385 | |||
| 3386 | return flags; | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item, | ||
| 3390 | GString *line_str) | ||
| 3391 | { | ||
| 3392 | gchar buf[IMAPBUFSIZE]; | ||
| 3393 | MsgInfo *msginfo = NULL; | ||
| 3394 | gchar *cur_pos; | ||
| 3395 | gint msgnum; | ||
| 3396 | guint32 uid = 0; | ||
| 3397 | size_t size = 0; | ||
| 3398 | MsgFlags flags = {0, 0}, imap_flags = {0, 0}; | ||
| 3399 | |||
| 3400 | g_return_val_if_fail(line_str != NULL, NULL); | ||
| 3401 | g_return_val_if_fail(line_str->str[0] == '*' && | ||
| 3402 | line_str->str[1] == ' ', NULL); | ||
| 3403 | |||
| 3404 | MSG_SET_TMP_FLAGS(flags, MSG_IMAP); | ||
| 3405 | if (item->stype == F_QUEUE) { | ||
| 3406 | MSG_SET_TMP_FLAGS(flags, MSG_QUEUED); | ||
| 3407 | } else if (item->stype == F_DRAFT) { | ||
| 3408 | MSG_SET_TMP_FLAGS(flags, MSG_DRAFT); | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | cur_pos = line_str->str + 2; | ||
| 3412 | |||
| 3413 | #define PARSE_ONE_ELEMENT(ch) \ | ||
| 3414 | { \ | ||
| 3415 | cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \ | ||
| 3416 | if (cur_pos == NULL) { \ | ||
| 3417 | g_warning("cur_pos == NULL\n"); \ | ||
| 3418 | procmsg_msginfo_free(msginfo); \ | ||
| 3419 | return NULL; \ | ||
| 3420 | } \ | ||
| 3421 | } | ||
| 3422 | |||
| 3423 | PARSE_ONE_ELEMENT(' '); | ||
| 3424 | msgnum = atoi(buf); | ||
| 3425 | |||
| 3426 | PARSE_ONE_ELEMENT(' '); | ||
| 3427 | g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL); | ||
| 3428 | |||
| 3429 | g_return_val_if_fail(*cur_pos == '(', NULL); | ||
| 3430 | cur_pos++; | ||
| 3431 | |||
| 3432 | while (*cur_pos != '\0' && *cur_pos != ')') { | ||
| 3433 | while (*cur_pos == ' ') cur_pos++; | ||
| 3434 | |||
| 3435 | if (!strncmp(cur_pos, "UID ", 4)) { | ||
| 3436 | cur_pos += 4; | ||
| 3437 | uid = strtoul(cur_pos, &cur_pos, 10); | ||
| 3438 | } else if (!strncmp(cur_pos, "FLAGS ", 6)) { | ||
| 3439 | cur_pos += 6; | ||
| 3440 | if (*cur_pos != '(') { | ||
| 3441 | g_warning("FLAGS: *cur_pos != '('\n"); | ||
| 3442 | procmsg_msginfo_free(msginfo); | ||
| 3443 | return NULL; | ||
| 3444 | } | ||
| 3445 | cur_pos++; | ||
| 3446 | PARSE_ONE_ELEMENT(')'); | ||
| 3447 | imap_flags = imap_parse_flags(buf); | ||
| 3448 | } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) { | ||
| 3449 | cur_pos += 12; | ||
| 3450 | size = strtol(cur_pos, &cur_pos, 10); | ||
| 3451 | } else if (!strncmp(cur_pos, "RFC822.HEADER", 13)) { | ||
| 3452 | gchar *headers; | ||
| 3453 | |||
| 3454 | cur_pos += 13; | ||
| 3455 | cur_pos = imap_get_header(session, cur_pos, &headers, | ||
| 3456 | line_str); | ||
| 3457 | if (cur_pos == NULL) { | ||
| 3458 | g_warning("RFC822.HEADER: cur_pos == NULL\n"); | ||
| 3459 | procmsg_msginfo_free(msginfo); | ||
| 3460 | return NULL; | ||
| 3461 | } | ||
| 3462 | if (!msginfo) | ||
| 3463 | msginfo = procheader_parse_str(headers, flags, FALSE); | ||
| 3464 | g_free(headers); | ||
| 3465 | } else { | ||
| 3466 | g_warning("invalid FETCH response: %s\n", cur_pos); | ||
| 3467 | break; | ||
| 3468 | } | ||
| 3469 | } | ||
| 3470 | |||
| 3471 | #undef PARSE_ONE_ELEMENT | ||
| 3472 | |||
| 3473 | if (msginfo) { | ||
| 3474 | msginfo->msgnum = uid; | ||
| 3475 | msginfo->size = size; | ||
| 3476 | msginfo->flags.tmp_flags |= imap_flags.tmp_flags; | ||
| 3477 | msginfo->flags.perm_flags = imap_flags.perm_flags; | ||
| 3478 | } | ||
| 3479 | |||
| 3480 | return msginfo; | ||
| 3481 | } | ||
| 3482 | |||
| 3483 | static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags, | ||
| 3484 | gboolean is_set) | ||
| 3485 | { | ||
| 3486 | Folder *folder; | ||
| 3487 | IMAPSession *session; | ||
| 3488 | IMAPFlags iflags = 0; | ||
| 3489 | MsgInfo *msginfo; | ||
| 3490 | GSList *seq_list, *cur; | ||
| 3491 | gint ok = IMAP_SUCCESS; | ||
| 3492 | |||
| 3493 | if (msglist == NULL) return IMAP_SUCCESS; | ||
| 3494 | |||
| 3495 | msginfo = (MsgInfo *)msglist->data; | ||
| 3496 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 3497 | |||
| 3498 | g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1); | ||
| 3499 | g_return_val_if_fail(msginfo->folder != NULL, -1); | ||
| 3500 | g_return_val_if_fail(msginfo->folder->folder != NULL, -1); | ||
| 3501 | |||
| 3502 | folder = msginfo->folder->folder; | ||
| 3503 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1); | ||
| 3504 | |||
| 3505 | session = imap_session_get(folder); | ||
| 3506 | if (!session) return -1; | ||
| 3507 | |||
| 3508 | ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path, | ||
| 3509 | NULL, NULL, NULL, NULL); | ||
| 3510 | if (ok != IMAP_SUCCESS) | ||
| 3511 | return ok; | ||
| 3512 | |||
| 3513 | seq_list = imap_get_seq_set_from_msglist(msglist, 0); | ||
| 3514 | |||
| 3515 | if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED; | ||
| 3516 | if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED; | ||
| 3517 | |||
| 3518 | for (cur = seq_list; cur != NULL; cur = cur->next) { | ||
| 3519 | gchar *seq_set = (gchar *)cur->data; | ||
| 3520 | |||
| 3521 | if (iflags) { | ||
| 3522 | ok = imap_set_message_flags(session, seq_set, iflags, | ||
| 3523 | is_set); | ||
| 3524 | if (ok != IMAP_SUCCESS) break; | ||
| 3525 | } | ||
| 3526 | |||
| 3527 | if (flags & MSG_UNREAD) { | ||
| 3528 | ok = imap_set_message_flags(session, seq_set, | ||
| 3529 | IMAP_FLAG_SEEN, !is_set); | ||
| 3530 | if (ok != IMAP_SUCCESS) break; | ||
| 3531 | } | ||
| 3532 | } | ||
| 3533 | |||
| 3534 | imap_seq_set_free(seq_list); | ||
| 3535 | |||
| 3536 | return ok; | ||
| 3537 | } | ||
| 3538 | |||
| 3539 | gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags) | ||
| 3540 | { | ||
| 3541 | GSList msglist; | ||
| 3542 | |||
| 3543 | msglist.data = msginfo; | ||
| 3544 | msglist.next = NULL; | ||
| 3545 | |||
| 3546 | return imap_msg_list_change_perm_flags(&msglist, flags, TRUE); | ||
| 3547 | } | ||
| 3548 | |||
| 3549 | gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags) | ||
| 3550 | { | ||
| 3551 | GSList msglist; | ||
| 3552 | |||
| 3553 | msglist.data = msginfo; | ||
| 3554 | msglist.next = NULL; | ||
| 3555 | |||
| 3556 | return imap_msg_list_change_perm_flags(&msglist, flags, FALSE); | ||
| 3557 | } | ||
| 3558 | |||
| 3559 | gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags) | ||
| 3560 | { | ||
| 3561 | return imap_msg_list_change_perm_flags(msglist, flags, TRUE); | ||
| 3562 | } | ||
| 3563 | |||
| 3564 | gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags) | ||
| 3565 | { | ||
| 3566 | return imap_msg_list_change_perm_flags(msglist, flags, FALSE); | ||
| 3567 | } | ||
| 3568 | |||
| 3569 | gint imap_msg_list_set_colorlabel_flags(GSList *msglist, guint color) | ||
| 3570 | { | ||
| 3571 | Folder *folder; | ||
| 3572 | IMAPSession *session; | ||
| 3573 | IMAPFlags iflags = 0; | ||
| 3574 | MsgInfo *msginfo; | ||
| 3575 | GSList *seq_list, *cur; | ||
| 3576 | gint ok = IMAP_SUCCESS; | ||
| 3577 | |||
| 3578 | if (msglist == NULL) return IMAP_SUCCESS; | ||
| 3579 | |||
| 3580 | msginfo = (MsgInfo *)msglist->data; | ||
| 3581 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 3582 | |||
| 3583 | g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1); | ||
| 3584 | g_return_val_if_fail(msginfo->folder != NULL, -1); | ||
| 3585 | g_return_val_if_fail(msginfo->folder->folder != NULL, -1); | ||
| 3586 | |||
| 3587 | folder = msginfo->folder->folder; | ||
| 3588 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1); | ||
| 3589 | |||
| 3590 | session = imap_session_get(folder); | ||
| 3591 | if (!session) return -1; | ||
| 3592 | |||
| 3593 | ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path, | ||
| 3594 | NULL, NULL, NULL, NULL); | ||
| 3595 | if (ok != IMAP_SUCCESS) | ||
| 3596 | return ok; | ||
| 3597 | |||
| 3598 | seq_list = imap_get_seq_set_from_msglist(msglist, 0); | ||
| 3599 | |||
| 3600 | IMAP_SET_COLORLABEL_VALUE(iflags, color); | ||
| 3601 | |||
| 3602 | for (cur = seq_list; cur != NULL; cur = cur->next) { | ||
| 3603 | gchar *seq_set = (gchar *)cur->data; | ||
| 3604 | |||
| 3605 | ok = imap_cmd_store(session, seq_set, | ||
| 3606 | "-FLAGS.SILENT ($label1 $label2 $label3 $label4 $label5 $label6 $label7)"); | ||
| 3607 | if (ok != IMAP_SUCCESS) break; | ||
| 3608 | |||
| 3609 | if (iflags) { | ||
| 3610 | ok = imap_set_message_flags(session, seq_set, iflags, | ||
| 3611 | TRUE); | ||
| 3612 | if (ok != IMAP_SUCCESS) break; | ||
| 3613 | } | ||
| 3614 | } | ||
| 3615 | |||
| 3616 | imap_seq_set_free(seq_list); | ||
| 3617 | |||
| 3618 | return ok; | ||
| 3619 | } | ||
| 3620 | |||
| 3621 | static gchar *imap_get_flag_str(IMAPFlags flags) | ||
| 3622 | { | ||
| 3623 | GString *str; | ||
| 3624 | gchar *ret; | ||
| 3625 | guint color; | ||
| 3626 | |||
| 3627 | str = g_string_new(NULL); | ||
| 3628 | |||
| 3629 | if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen "); | ||
| 3630 | if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered "); | ||
| 3631 | if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged "); | ||
| 3632 | if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted "); | ||
| 3633 | if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft "); | ||
| 3634 | |||
| 3635 | if ((color = IMAP_GET_COLORLABEL_VALUE(flags)) != 0) { | ||
| 3636 | g_string_append_printf(str, "$label%u", color); | ||
| 3637 | } | ||
| 3638 | |||
| 3639 | if (str->len > 0 && str->str[str->len - 1] == ' ') | ||
| 3640 | g_string_truncate(str, str->len - 1); | ||
| 3641 | |||
| 3642 | ret = str->str; | ||
| 3643 | g_string_free(str, FALSE); | ||
| 3644 | |||
| 3645 | return ret; | ||
| 3646 | } | ||
| 3647 | |||
| 3648 | static gint imap_set_message_flags(IMAPSession *session, | ||
| 3649 | const gchar *seq_set, | ||
| 3650 | IMAPFlags flags, | ||
| 3651 | gboolean is_set) | ||
| 3652 | { | ||
| 3653 | gchar *cmd; | ||
| 3654 | gchar *flag_str; | ||
| 3655 | gint ok; | ||
| 3656 | |||
| 3657 | flag_str = imap_get_flag_str(flags); | ||
| 3658 | cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (", | ||
| 3659 | flag_str, ")", NULL); | ||
| 3660 | g_free(flag_str); | ||
| 3661 | |||
| 3662 | ok = imap_cmd_store(session, seq_set, cmd); | ||
| 3663 | g_free(cmd); | ||
| 3664 | |||
| 3665 | return ok; | ||
| 3666 | } | ||
| 3667 | |||
| 3668 | static gint imap_select(IMAPSession *session, IMAPFolder *folder, | ||
| 3669 | const gchar *path, | ||
| 3670 | gint *exists, gint *recent, gint *unseen, | ||
| 3671 | guint32 *uid_validity) | ||
| 3672 | { | ||
| 3673 | gchar *real_path; | ||
| 3674 | gint ok; | ||
| 3675 | gint exists_, recent_, unseen_; | ||
| 3676 | guint32 uid_validity_; | ||
| 3677 | |||
| 3678 | if (!exists || !recent || !unseen || !uid_validity) { | ||
| 3679 | if (session->mbox && strcmp(session->mbox, path) == 0) | ||
| 3680 | return IMAP_SUCCESS; | ||
| 3681 | exists = &exists_; | ||
| 3682 | recent = &recent_; | ||
| 3683 | unseen = &unseen_; | ||
| 3684 | uid_validity = &uid_validity_; | ||
| 3685 | } | ||
| 3686 | |||
| 3687 | g_free(session->mbox); | ||
| 3688 | session->mbox = NULL; | ||
| 3689 | |||
| 3690 | real_path = imap_get_real_path(folder, path); | ||
| 3691 | ok = imap_cmd_select(session, real_path, | ||
| 3692 | exists, recent, unseen, uid_validity); | ||
| 3693 | if (ok != IMAP_SUCCESS) | ||
| 3694 | log_warning(_("can't select folder: %s\n"), real_path); | ||
| 3695 | else | ||
| 3696 | session->mbox = g_strdup(path); | ||
| 3697 | g_free(real_path); | ||
| 3698 | |||
| 3699 | return ok; | ||
| 3700 | } | ||
| 3701 | |||
| 3702 | #define THROW(err) { ok = err; goto catch; } | ||
| 3703 | |||
| 3704 | static gint imap_status(IMAPSession *session, IMAPFolder *folder, | ||
| 3705 | const gchar *path, | ||
| 3706 | gint *messages, gint *recent, | ||
| 3707 | guint32 *uid_next, guint32 *uid_validity, | ||
| 3708 | gint *unseen) | ||
| 3709 | { | ||
| 3710 | gchar *real_path; | ||
| 3711 | gchar *real_path_; | ||
| 3712 | gint ok; | ||
| 3713 | GPtrArray *argbuf = NULL; | ||
| 3714 | gchar *str; | ||
| 3715 | |||
| 3716 | if (messages && recent && uid_next && uid_validity && unseen) { | ||
| 3717 | *messages = *recent = *uid_next = *uid_validity = *unseen = 0; | ||
| 3718 | argbuf = g_ptr_array_new(); | ||
| 3719 | } | ||
| 3720 | |||
| 3721 | real_path = imap_get_real_path(folder, path); | ||
| 3722 | QUOTE_IF_REQUIRED(real_path_, real_path); | ||
| 3723 | ok = imap_cmd_gen_send(session, "STATUS %s " | ||
| 3724 | "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)", | ||
| 3725 | real_path_); | ||
| 3726 | if (ok != IMAP_SUCCESS) { | ||
| 3727 | log_warning("error on sending imap command: STATUS\n"); | ||
| 3728 | THROW(ok); | ||
| 3729 | } | ||
| 3730 | ok = imap_cmd_ok(session, argbuf); | ||
| 3731 | if (ok != IMAP_SUCCESS) | ||
| 3732 | log_warning(_("error on imap command: STATUS\n")); | ||
| 3733 | if (ok != IMAP_SUCCESS || !argbuf) THROW(ok); | ||
| 3734 | |||
| 3735 | str = search_array_str(argbuf, "STATUS"); | ||
| 3736 | if (!str) THROW(IMAP_ERROR); | ||
| 3737 | |||
| 3738 | str = strrchr_with_skip_quote(str, '"', '('); | ||
| 3739 | if (!str) THROW(IMAP_ERROR); | ||
| 3740 | str++; | ||
| 3741 | while (*str != '\0' && *str != ')') { | ||
| 3742 | while (*str == ' ') str++; | ||
| 3743 | |||
| 3744 | if (!strncmp(str, "MESSAGES ", 9)) { | ||
| 3745 | str += 9; | ||
| 3746 | *messages = strtol(str, &str, 10); | ||
| 3747 | } else if (!strncmp(str, "RECENT ", 7)) { | ||
| 3748 | str += 7; | ||
| 3749 | *recent = strtol(str, &str, 10); | ||
| 3750 | } else if (!strncmp(str, "UIDNEXT ", 8)) { | ||
| 3751 | str += 8; | ||
| 3752 | *uid_next = strtoul(str, &str, 10); | ||
| 3753 | } else if (!strncmp(str, "UIDVALIDITY ", 12)) { | ||
| 3754 | str += 12; | ||
| 3755 | *uid_validity = strtoul(str, &str, 10); | ||
| 3756 | } else if (!strncmp(str, "UNSEEN ", 7)) { | ||
| 3757 | str += 7; | ||
| 3758 | *unseen = strtol(str, &str, 10); | ||
| 3759 | } else { | ||
| 3760 | g_warning("invalid STATUS response: %s\n", str); | ||
| 3761 | break; | ||
| 3762 | } | ||
| 3763 | } | ||
| 3764 | |||
| 3765 | catch: | ||
| 3766 | g_free(real_path); | ||
| 3767 | if (argbuf) { | ||
| 3768 | ptr_array_free_strings(argbuf); | ||
| 3769 | g_ptr_array_free(argbuf, TRUE); | ||
| 3770 | } | ||
| 3771 | |||
| 3772 | return ok; | ||
| 3773 | } | ||
| 3774 | |||
| 3775 | #undef THROW | ||
| 3776 | |||
| 3777 | static gboolean imap_has_capability(IMAPSession *session, | ||
| 3778 | const gchar *capability) | ||
| 3779 | { | ||
| 3780 | gchar **p; | ||
| 3781 | |||
| 3782 | for (p = session->capability; *p != NULL; ++p) { | ||
| 3783 | if (!g_ascii_strcasecmp(*p, capability)) | ||
| 3784 | return TRUE; | ||
| 3785 | } | ||
| 3786 | |||
| 3787 | return FALSE; | ||
| 3788 | } | ||
| 3789 | |||
| 3790 | static void imap_capability_free(IMAPSession *session) | ||
| 3791 | { | ||
| 3792 | if (session->capability) { | ||
| 3793 | g_strfreev(session->capability); | ||
| 3794 | session->capability = NULL; | ||
| 3795 | } | ||
| 3796 | } | ||
| 3797 | |||
| 3798 | |||
| 3799 | /* low-level IMAP4rev1 commands */ | ||
| 3800 | |||
| 3801 | #define THROW(err) { ok = err; goto catch; } | ||
| 3802 | |||
| 3803 | static gint imap_cmd_capability(IMAPSession *session) | ||
| 3804 | { | ||
| 3805 | gint ok; | ||
| 3806 | GPtrArray *argbuf; | ||
| 3807 | gchar *capability; | ||
| 3808 | |||
| 3809 | argbuf = g_ptr_array_new(); | ||
| 3810 | |||
| 3811 | if ((ok = imap_cmd_gen_send(session, "CAPABILITY")) != IMAP_SUCCESS) | ||
| 3812 | THROW(ok); | ||
| 3813 | if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok); | ||
| 3814 | |||
| 3815 | capability = search_array_str(argbuf, "CAPABILITY "); | ||
| 3816 | if (!capability) THROW(IMAP_ERROR); | ||
| 3817 | |||
| 3818 | capability += strlen("CAPABILITY "); | ||
| 3819 | |||
| 3820 | imap_capability_free(session); | ||
| 3821 | session->capability = g_strsplit(capability, " ", -1); | ||
| 3822 | |||
| 3823 | catch: | ||
| 3824 | ptr_array_free_strings(argbuf); | ||
| 3825 | g_ptr_array_free(argbuf, TRUE); | ||
| 3826 | |||
| 3827 | return ok; | ||
| 3828 | } | ||
| 3829 | |||
| 3830 | #undef THROW | ||
| 3831 | |||
| 3832 | static gint imap_cmd_auth_plain(IMAPSession *session, const gchar *user, | ||
| 3833 | const gchar *pass) | ||
| 3834 | { | ||
| 3835 | gchar *p; | ||
| 3836 | gchar *response; | ||
| 3837 | gchar *response64; | ||
| 3838 | gint ok; | ||
| 3839 | |||
| 3840 | p = response = g_malloc(strlen(user) * 2 + 2 + strlen(pass) + 1); | ||
| 3841 | strcpy(p, user); | ||
| 3842 | p += strlen(user) + 1; | ||
| 3843 | strcpy(p, user); | ||
| 3844 | p += strlen(user) + 1; | ||
| 3845 | strcpy(p, pass); | ||
| 3846 | p += strlen(pass); | ||
| 3847 | |||
| 3848 | response64 = g_malloc((p - response) * 2 + 1); | ||
| 3849 | base64_encode(response64, (guchar *)response, p - response); | ||
| 3850 | g_free(response); | ||
| 3851 | |||
| 3852 | log_print("IMAP4> ****************\n"); | ||
| 3853 | sock_puts(SESSION(session)->sock, response64); | ||
| 3854 | ok = imap_cmd_ok(session, NULL); | ||
| 3855 | if (ok != IMAP_SUCCESS) | ||
| 3856 | log_warning(_("IMAP4 authentication failed.\n")); | ||
| 3857 | g_free(response64); | ||
| 3858 | |||
| 3859 | return ok; | ||
| 3860 | } | ||
| 3861 | |||
| 3862 | static gint imap_cmd_auth_cram_md5(IMAPSession *session, const gchar *user, | ||
| 3863 | const gchar *pass, const gchar *challenge64) | ||
| 3864 | { | ||
| 3865 | gchar *challenge; | ||
| 3866 | gint challenge_len; | ||
| 3867 | gchar hexdigest[33]; | ||
| 3868 | gchar *response; | ||
| 3869 | gchar *response64; | ||
| 3870 | gint ok; | ||
| 3871 | |||
| 3872 | challenge = g_malloc(strlen(challenge64 + 2) + 1); | ||
| 3873 | challenge_len = base64_decode((guchar *)challenge, challenge64 + 2, -1); | ||
| 3874 | challenge[challenge_len] = '\0'; | ||
| 3875 | log_print("IMAP< [Decoded: %s]\n", challenge); | ||
| 3876 | |||
| 3877 | md5_hex_hmac(hexdigest, (guchar *)challenge, challenge_len, | ||
| 3878 | (guchar *)pass, strlen(pass)); | ||
| 3879 | g_free(challenge); | ||
| 3880 | |||
| 3881 | response = g_strdup_printf("%s %s", user, hexdigest); | ||
| 3882 | log_print("IMAP> [Encoded: %s]\n", response); | ||
| 3883 | response64 = g_malloc((strlen(response) + 3) * 2 + 1); | ||
| 3884 | base64_encode(response64, (guchar *)response, strlen(response)); | ||
| 3885 | g_free(response); | ||
| 3886 | |||
| 3887 | log_print("IMAP> %s\n", response64); | ||
| 3888 | sock_puts(SESSION(session)->sock, response64); | ||
| 3889 | ok = imap_cmd_ok(session, NULL); | ||
| 3890 | if (ok != IMAP_SUCCESS) | ||
| 3891 | log_warning(_("IMAP4 authentication failed.\n")); | ||
| 3892 | |||
| 3893 | return ok; | ||
| 3894 | } | ||
| 3895 | |||
| 3896 | static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user, | ||
| 3897 | const gchar *pass, IMAPAuthType type) | ||
| 3898 | { | ||
| 3899 | gchar *auth_type; | ||
| 3900 | gint ok; | ||
| 3901 | gchar *buf = NULL; | ||
| 3902 | |||
| 3903 | g_return_val_if_fail((type == 0 || type == IMAP_AUTH_CRAM_MD5 || | ||
| 3904 | type == IMAP_AUTH_PLAIN), IMAP_ERROR); | ||
| 3905 | |||
| 3906 | if (type == IMAP_AUTH_PLAIN) | ||
| 3907 | auth_type = "PLAIN"; | ||
| 3908 | else | ||
| 3909 | auth_type = "CRAM-MD5"; | ||
| 3910 | |||
| 3911 | ok = imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type); | ||
| 3912 | if (ok != IMAP_SUCCESS) { | ||
| 3913 | g_free(buf); | ||
| 3914 | return ok; | ||
| 3915 | } | ||
| 3916 | ok = imap_cmd_gen_recv(session, &buf); | ||
| 3917 | if (ok != IMAP_SUCCESS || buf[0] != '+') { | ||
| 3918 | g_free(buf); | ||
| 3919 | return IMAP_ERROR; | ||
| 3920 | } | ||
| 3921 | |||
| 3922 | if (type == IMAP_AUTH_PLAIN) | ||
| 3923 | ok = imap_cmd_auth_plain(session, user, pass); | ||
| 3924 | else | ||
| 3925 | ok = imap_cmd_auth_cram_md5(session, user, pass, buf); | ||
| 3926 | |||
| 3927 | g_free(buf); | ||
| 3928 | |||
| 3929 | return ok; | ||
| 3930 | } | ||
| 3931 | |||
| 3932 | static gint imap_cmd_login(IMAPSession *session, | ||
| 3933 | const gchar *user, const gchar *pass) | ||
| 3934 | { | ||
| 3935 | gchar *user_, *pass_; | ||
| 3936 | gint ok; | ||
| 3937 | |||
| 3938 | QUOTE_IF_REQUIRED(user_, user); | ||
| 3939 | QUOTE_IF_REQUIRED(pass_, pass); | ||
| 3940 | ok = imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_); | ||
| 3941 | if (ok == IMAP_SUCCESS) | ||
| 3942 | ok = imap_cmd_ok(session, NULL); | ||
| 3943 | if (ok != IMAP_SUCCESS) | ||
| 3944 | log_warning(_("IMAP4 login failed.\n")); | ||
| 3945 | |||
| 3946 | return ok; | ||
| 3947 | } | ||
| 3948 | |||
| 3949 | static gint imap_cmd_logout(IMAPSession *session) | ||
| 3950 | { | ||
| 3951 | if (imap_cmd_gen_send(session, "LOGOUT") != IMAP_SUCCESS) | ||
| 3952 | return IMAP_ERROR; | ||
| 3953 | return imap_cmd_ok(session, NULL); | ||
| 3954 | } | ||
| 3955 | |||
| 3956 | static gint imap_cmd_noop(IMAPSession *session) | ||
| 3957 | { | ||
| 3958 | gint ret; | ||
| 3959 | |||
| 3960 | ret = imap_cmd_gen_send(session, "NOOP"); | ||
| 3961 | if (ret != IMAP_SUCCESS) | ||
| 3962 | return ret; | ||
| 3963 | return imap_cmd_ok(session, NULL); | ||
| 3964 | } | ||
| 3965 | |||
| 3966 | #if USE_SSL | ||
| 3967 | static gint imap_cmd_starttls(IMAPSession *session) | ||
| 3968 | { | ||
| 3969 | if (imap_cmd_gen_send(session, "STARTTLS") != IMAP_SUCCESS) | ||
| 3970 | return IMAP_ERROR; | ||
| 3971 | return imap_cmd_ok(session, NULL); | ||
| 3972 | } | ||
| 3973 | #endif | ||
| 3974 | |||
| 3975 | #define THROW(err) { ok = err; goto catch; } | ||
| 3976 | |||
| 3977 | static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str) | ||
| 3978 | { | ||
| 3979 | gint ok; | ||
| 3980 | GPtrArray *argbuf; | ||
| 3981 | gchar *str; | ||
| 3982 | |||
| 3983 | argbuf = g_ptr_array_new(); | ||
| 3984 | |||
| 3985 | if ((ok = imap_cmd_gen_send(session, "NAMESPACE")) != IMAP_SUCCESS) | ||
| 3986 | THROW(ok); | ||
| 3987 | if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok); | ||
| 3988 | |||
| 3989 | str = search_array_str(argbuf, "NAMESPACE"); | ||
| 3990 | if (!str) THROW(IMAP_ERROR); | ||
| 3991 | |||
| 3992 | *ns_str = g_strdup(str); | ||
| 3993 | |||
| 3994 | catch: | ||
| 3995 | ptr_array_free_strings(argbuf); | ||
| 3996 | g_ptr_array_free(argbuf, TRUE); | ||
| 3997 | |||
| 3998 | return ok; | ||
| 3999 | } | ||
| 4000 | |||
| 4001 | #undef THROW | ||
| 4002 | |||
| 4003 | static gint imap_cmd_list(IMAPSession *session, const gchar *ref, | ||
| 4004 | const gchar *mailbox, GPtrArray *argbuf) | ||
| 4005 | { | ||
| 4006 | gchar *ref_, *mailbox_; | ||
| 4007 | |||
| 4008 | if (!ref) ref = ""; | ||
| 4009 | if (!mailbox) mailbox = ""; | ||
| 4010 | |||
| 4011 | QUOTE_IF_REQUIRED(ref_, ref); | ||
| 4012 | QUOTE_IF_REQUIRED(mailbox_, mailbox); | ||
| 4013 | if (imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_) != IMAP_SUCCESS) | ||
| 4014 | return IMAP_ERROR; | ||
| 4015 | |||
| 4016 | return imap_cmd_ok(session, argbuf); | ||
| 4017 | } | ||
| 4018 | |||
| 4019 | #define THROW goto catch | ||
| 4020 | |||
| 4021 | static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder, | ||
| 4022 | gboolean examine, | ||
| 4023 | gint *exists, gint *recent, gint *unseen, | ||
| 4024 | guint32 *uid_validity) | ||
| 4025 | { | ||
| 4026 | gint ok; | ||
| 4027 | gchar *resp_str; | ||
| 4028 | GPtrArray *argbuf; | ||
| 4029 | gchar *select_cmd; | ||
| 4030 | gchar *folder_; | ||
| 4031 | guint uid_validity_; | ||
| 4032 | |||
| 4033 | *exists = *recent = *unseen = *uid_validity = 0; | ||
| 4034 | argbuf = g_ptr_array_new(); | ||
| 4035 | |||
| 4036 | if (examine) | ||
| 4037 | select_cmd = "EXAMINE"; | ||
| 4038 | else | ||
| 4039 | select_cmd = "SELECT"; | ||
| 4040 | |||
| 4041 | QUOTE_IF_REQUIRED(folder_, folder); | ||
| 4042 | if ((ok = imap_cmd_gen_send(session, "%s %s", select_cmd, folder_)) != IMAP_SUCCESS) | ||
| 4043 | THROW; | ||
| 4044 | |||
| 4045 | if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW; | ||
| 4046 | |||
| 4047 | resp_str = search_array_contain_str(argbuf, "EXISTS"); | ||
| 4048 | if (resp_str) { | ||
| 4049 | if (sscanf(resp_str,"%d EXISTS", exists) != 1) { | ||
| 4050 | g_warning("imap_cmd_select(): invalid EXISTS line.\n"); | ||
| 4051 | THROW; | ||
| 4052 | } | ||
| 4053 | } | ||
| 4054 | |||
| 4055 | resp_str = search_array_contain_str(argbuf, "RECENT"); | ||
| 4056 | if (resp_str) { | ||
| 4057 | if (sscanf(resp_str, "%d RECENT", recent) != 1) { | ||
| 4058 | g_warning("imap_cmd_select(): invalid RECENT line.\n"); | ||
| 4059 | THROW; | ||
| 4060 | } | ||
| 4061 | } | ||
| 4062 | |||
| 4063 | resp_str = search_array_contain_str(argbuf, "UIDVALIDITY"); | ||
| 4064 | if (resp_str) { | ||
| 4065 | if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_) | ||
| 4066 | != 1) { | ||
| 4067 | g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n"); | ||
| 4068 | THROW; | ||
| 4069 | } | ||
| 4070 | *uid_validity = uid_validity_; | ||
| 4071 | } | ||
| 4072 | |||
| 4073 | resp_str = search_array_contain_str(argbuf, "UNSEEN"); | ||
| 4074 | if (resp_str) { | ||
| 4075 | if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) { | ||
| 4076 | g_warning("imap_cmd_select(): invalid UNSEEN line.\n"); | ||
| 4077 | THROW; | ||
| 4078 | } | ||
| 4079 | } | ||
| 4080 | |||
| 4081 | catch: | ||
| 4082 | ptr_array_free_strings(argbuf); | ||
| 4083 | g_ptr_array_free(argbuf, TRUE); | ||
| 4084 | |||
| 4085 | return ok; | ||
| 4086 | } | ||
| 4087 | |||
| 4088 | static gint imap_cmd_select(IMAPSession *session, const gchar *folder, | ||
| 4089 | gint *exists, gint *recent, gint *unseen, | ||
| 4090 | guint32 *uid_validity) | ||
| 4091 | { | ||
| 4092 | return imap_cmd_do_select(session, folder, FALSE, | ||
| 4093 | exists, recent, unseen, uid_validity); | ||
| 4094 | } | ||
| 4095 | |||
| 4096 | static gint imap_cmd_examine(IMAPSession *session, const gchar *folder, | ||
| 4097 | gint *exists, gint *recent, gint *unseen, | ||
| 4098 | guint32 *uid_validity) | ||
| 4099 | { | ||
| 4100 | return imap_cmd_do_select(session, folder, TRUE, | ||
| 4101 | exists, recent, unseen, uid_validity); | ||
| 4102 | } | ||
| 4103 | |||
| 4104 | #undef THROW | ||
| 4105 | |||
| 4106 | static gint imap_cmd_create(IMAPSession *session, const gchar *folder) | ||
| 4107 | { | ||
| 4108 | gchar *folder_; | ||
| 4109 | |||
| 4110 | QUOTE_IF_REQUIRED(folder_, folder); | ||
| 4111 | if (imap_cmd_gen_send(session, "CREATE %s", folder_) != IMAP_SUCCESS) | ||
| 4112 | return IMAP_ERROR; | ||
| 4113 | |||
| 4114 | return imap_cmd_ok(session, NULL); | ||
| 4115 | } | ||
| 4116 | |||
| 4117 | static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder, | ||
| 4118 | const gchar *new_folder) | ||
| 4119 | { | ||
| 4120 | gchar *old_folder_, *new_folder_; | ||
| 4121 | |||
| 4122 | QUOTE_IF_REQUIRED(old_folder_, old_folder); | ||
| 4123 | QUOTE_IF_REQUIRED(new_folder_, new_folder); | ||
| 4124 | if (imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_) != IMAP_SUCCESS) | ||
| 4125 | return IMAP_ERROR; | ||
| 4126 | |||
| 4127 | return imap_cmd_ok(session, NULL); | ||
| 4128 | } | ||
| 4129 | |||
| 4130 | static gint imap_cmd_delete(IMAPSession *session, const gchar *folder) | ||
| 4131 | { | ||
| 4132 | gchar *folder_; | ||
| 4133 | |||
| 4134 | QUOTE_IF_REQUIRED(folder_, folder); | ||
| 4135 | if (imap_cmd_gen_send(session, "DELETE %s", folder_) != IMAP_SUCCESS) | ||
| 4136 | return IMAP_ERROR; | ||
| 4137 | |||
| 4138 | return imap_cmd_ok(session, NULL); | ||
| 4139 | } | ||
| 4140 | |||
| 4141 | static gint imap_cmd_subscribe(IMAPSession *session, const gchar *folder) | ||
| 4142 | { | ||
| 4143 | gchar *folder_; | ||
| 4144 | |||
| 4145 | QUOTE_IF_REQUIRED(folder_, folder); | ||
| 4146 | if (imap_cmd_gen_send(session, "SUBSCRIBE %s", folder_) != IMAP_SUCCESS) | ||
| 4147 | return IMAP_ERROR; | ||
| 4148 | |||
| 4149 | return imap_cmd_ok(session, NULL); | ||
| 4150 | } | ||
| 4151 | |||
| 4152 | #define THROW(err) { ok = err; goto catch; } | ||
| 4153 | |||
| 4154 | static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, | ||
| 4155 | GArray **result) | ||
| 4156 | { | ||
| 4157 | gint ok; | ||
| 4158 | GPtrArray *argbuf; | ||
| 4159 | GArray *array; | ||
| 4160 | gchar *str; | ||
| 4161 | gchar *p, *ep; | ||
| 4162 | gint i; | ||
| 4163 | guint32 uid; | ||
| 4164 | |||
| 4165 | g_return_val_if_fail(criteria != NULL, IMAP_ERROR); | ||
| 4166 | g_return_val_if_fail(result != NULL, IMAP_ERROR); | ||
| 4167 | |||
| 4168 | argbuf = g_ptr_array_new(); | ||
| 4169 | |||
| 4170 | if ((ok = imap_cmd_gen_send(session, "UID SEARCH %s", criteria)) != IMAP_SUCCESS) | ||
| 4171 | THROW(ok); | ||
| 4172 | if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok); | ||
| 4173 | |||
| 4174 | array = g_array_new(FALSE, FALSE, sizeof(guint32)); | ||
| 4175 | |||
| 4176 | for (i = 0; i < argbuf->len; i++) { | ||
| 4177 | str = g_ptr_array_index(argbuf, i); | ||
| 4178 | if (strncmp(str, "SEARCH", 6) != 0) | ||
| 4179 | continue; | ||
| 4180 | |||
| 4181 | p = str + 6; | ||
| 4182 | while (*p != '\0') { | ||
| 4183 | uid = strtoul(p, &ep, 10); | ||
| 4184 | if (p < ep && uid > 0) { | ||
| 4185 | g_array_append_val(array, uid); | ||
| 4186 | p = ep; | ||
| 4187 | } else | ||
| 4188 | break; | ||
| 4189 | } | ||
| 4190 | } | ||
| 4191 | |||
| 4192 | *result = array; | ||
| 4193 | |||
| 4194 | catch: | ||
| 4195 | ptr_array_free_strings(argbuf); | ||
| 4196 | g_ptr_array_free(argbuf, TRUE); | ||
| 4197 | |||
| 4198 | return ok; | ||
| 4199 | } | ||
| 4200 | |||
| 4201 | typedef struct _IMAPCmdFetchData | ||
| 4202 | { | ||
| 4203 | guint32 uid; | ||
| 4204 | const gchar *filename; | ||
| 4205 | } IMAPCmdFetchData; | ||
| 4206 | |||
| 4207 | #define THROW(err) { ok = err; goto catch; } | ||
| 4208 | |||
| 4209 | static gint imap_cmd_fetch_func(IMAPSession *session, gpointer data) | ||
| 4210 | { | ||
| 4211 | const gchar *filename = ((IMAPCmdFetchData *)data)->filename; | ||
| 4212 | gint ok; | ||
| 4213 | gchar *buf; | ||
| 4214 | gchar *cur_pos; | ||
| 4215 | gchar size_str[32]; | ||
| 4216 | glong size_num; | ||
| 4217 | gint ret; | ||
| 4218 | |||
| 4219 | while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) { | ||
| 4220 | if (buf[0] != '*' || buf[1] != ' ') { | ||
| 4221 | g_free(buf); | ||
| 4222 | return IMAP_ERROR; | ||
| 4223 | } | ||
| 4224 | if (strstr(buf, "FETCH") != NULL && strstr(buf, "BODY") != NULL) | ||
| 4225 | break; | ||
| 4226 | g_free(buf); | ||
| 4227 | } | ||
| 4228 | if (ok != IMAP_SUCCESS) | ||
| 4229 | THROW(ok); | ||
| 4230 | |||
| 4231 | #define RETURN_ERROR_IF_FAIL(cond) \ | ||
| 4232 | if (!(cond)) { \ | ||
| 4233 | g_free(buf); \ | ||
| 4234 | ok = imap_cmd_ok_real(session, NULL); \ | ||
| 4235 | THROW(IMAP_ERROR); \ | ||
| 4236 | } | ||
| 4237 | |||
| 4238 | cur_pos = strchr(buf, '{'); | ||
| 4239 | RETURN_ERROR_IF_FAIL(cur_pos != NULL); | ||
| 4240 | cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str)); | ||
| 4241 | RETURN_ERROR_IF_FAIL(cur_pos != NULL); | ||
| 4242 | size_num = atol(size_str); | ||
| 4243 | RETURN_ERROR_IF_FAIL(size_num >= 0); | ||
| 4244 | |||
| 4245 | RETURN_ERROR_IF_FAIL(*cur_pos == '\0'); | ||
| 4246 | |||
| 4247 | #undef RETURN_ERROR_IF_FAIL | ||
| 4248 | |||
| 4249 | g_free(buf); | ||
| 4250 | |||
| 4251 | if ((ret = recv_bytes_write_to_file(SESSION(session)->sock, | ||
| 4252 | size_num, filename)) != 0) { | ||
| 4253 | if (ret == -2) | ||
| 4254 | THROW(IMAP_SOCKET); | ||
| 4255 | } | ||
| 4256 | |||
| 4257 | if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS) | ||
| 4258 | THROW(IMAP_ERROR); | ||
| 4259 | |||
| 4260 | if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') { | ||
| 4261 | g_free(buf); | ||
| 4262 | THROW(IMAP_ERROR); | ||
| 4263 | } | ||
| 4264 | g_free(buf); | ||
| 4265 | |||
| 4266 | ok = imap_cmd_ok_real(session, NULL); | ||
| 4267 | |||
| 4268 | if (ret != 0) | ||
| 4269 | THROW(IMAP_ERROR); | ||
| 4270 | |||
| 4271 | catch: | ||
| 4272 | return ok; | ||
| 4273 | } | ||
| 4274 | |||
| 4275 | #undef THROW | ||
| 4276 | |||
| 4277 | static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, | ||
| 4278 | const gchar *filename) | ||
| 4279 | { | ||
| 4280 | gint ok; | ||
| 4281 | IMAPCmdFetchData fetch_data = {uid, filename}; | ||
| 4282 | |||
| 4283 | g_return_val_if_fail(filename != NULL, IMAP_ERROR); | ||
| 4284 | |||
| 4285 | ok = imap_cmd_gen_send(session, "UID FETCH %u BODY.PEEK[]", uid); | ||
| 4286 | if (ok != IMAP_SUCCESS) | ||
| 4287 | return ok; | ||
| 4288 | |||
| 4289 | #if USE_THREADS | ||
| 4290 | ok = imap_thread_run(session, imap_cmd_fetch_func, &fetch_data); | ||
| 4291 | #else | ||
| 4292 | ok = imap_cmd_fetch_func(session, &fetch_data); | ||
| 4293 | #endif | ||
| 4294 | |||
| 4295 | return ok; | ||
| 4296 | } | ||
| 4297 | |||
| 4298 | static void imap_get_date_time(gchar *buf, size_t len, stime_t timer) | ||
| 4299 | { | ||
| 4300 | static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; | ||
| 4301 | struct tm *lt; | ||
| 4302 | gchar date_time[64]; | ||
| 4303 | gchar tz[6]; | ||
| 4304 | time_t timer_ = timer; | ||
| 4305 | |||
| 4306 | lt = localtime(&timer_); | ||
| 4307 | if (lt && lt->tm_mon >= 0 && lt->tm_mon < 12) { | ||
| 4308 | strftime(date_time, sizeof(date_time), "%d-___-%Y %H:%M:%S", | ||
| 4309 | lt); | ||
| 4310 | tzoffset_buf(tz, &timer); | ||
| 4311 | memcpy(date_time + 3, monthstr + lt->tm_mon * 3, 3); | ||
| 4312 | g_snprintf(buf, len, "%s %s", date_time, tz); | ||
| 4313 | } | ||
| 4314 | } | ||
| 4315 | |||
| 4316 | static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder, | ||
| 4317 | const gchar *file, IMAPFlags flags, | ||
| 4318 | guint32 *new_uid) | ||
| 4319 | { | ||
| 4320 | gint ok; | ||
| 4321 | MsgInfo *msginfo; | ||
| 4322 | MsgFlags flags_ = {0, 0}; | ||
| 4323 | gchar date_time[64] = ""; | ||
| 4324 | gint size; | ||
| 4325 | gchar *destfolder_; | ||
| 4326 | gchar *flag_str; | ||
| 4327 | guint new_uid_; | ||
| 4328 | gchar *ret = NULL; | ||
| 4329 | gchar buf[BUFFSIZE]; | ||
| 4330 | FILE *fp; | ||
| 4331 | FILE *tmp; | ||
| 4332 | size_t read_len; | ||
| 4333 | GPtrArray *argbuf; | ||
| 4334 | gchar *resp_str; | ||
| 4335 | |||
| 4336 | g_return_val_if_fail(file != NULL, IMAP_ERROR); | ||
| 4337 | |||
| 4338 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 4339 | FILE_OP_ERROR(file, "fopen"); | ||
| 4340 | return -1; | ||
| 4341 | } | ||
| 4342 | |||
| 4343 | /* use Date: header as received date */ | ||
| 4344 | msginfo = procheader_parse_stream(fp, flags_, FALSE); | ||
| 4345 | imap_get_date_time(date_time, sizeof(date_time), msginfo->date_t); | ||
| 4346 | procmsg_msginfo_free(msginfo); | ||
| 4347 | |||
| 4348 | rewind(fp); | ||
| 4349 | tmp = canonicalize_file_stream(fp, &size); | ||
| 4350 | fclose(fp); | ||
| 4351 | if (!tmp) | ||
| 4352 | return -1; | ||
| 4353 | |||
| 4354 | QUOTE_IF_REQUIRED(destfolder_, destfolder); | ||
| 4355 | flag_str = imap_get_flag_str(flags); | ||
| 4356 | if (date_time[0]) | ||
| 4357 | ok = imap_cmd_gen_send(session, "APPEND %s (%s) \"%s\" {%d}", | ||
| 4358 | destfolder_, flag_str, date_time, size); | ||
| 4359 | else | ||
| 4360 | ok = imap_cmd_gen_send(session, "APPEND %s (%s) {%d}", | ||
| 4361 | destfolder_, flag_str, size); | ||
| 4362 | g_free(flag_str); | ||
| 4363 | if (ok != IMAP_SUCCESS) { | ||
| 4364 | log_warning(_("can't append %s to %s\n"), file, destfolder_); | ||
| 4365 | fclose(tmp); | ||
| 4366 | return ok; | ||
| 4367 | } | ||
| 4368 | |||
| 4369 | ok = imap_cmd_gen_recv(session, &ret); | ||
| 4370 | if (ok != IMAP_SUCCESS || ret[0] != '+') { | ||
| 4371 | log_warning(_("can't append %s to %s\n"), file, destfolder_); | ||
| 4372 | g_free(ret); | ||
| 4373 | fclose(tmp); | ||
| 4374 | return IMAP_ERROR; | ||
| 4375 | } | ||
| 4376 | g_free(ret); | ||
| 4377 | |||
| 4378 | log_print("IMAP4> %s\n", _("(sending file...)")); | ||
| 4379 | |||
| 4380 | while ((read_len = fread(buf, 1, sizeof(buf), tmp)) > 0) { | ||
| 4381 | if (read_len < sizeof(buf) && ferror(tmp)) | ||
| 4382 | break; | ||
| 4383 | if (sock_write_all(SESSION(session)->sock, buf, read_len) < 0) { | ||
| 4384 | fclose(tmp); | ||
| 4385 | return -1; | ||
| 4386 | } | ||
| 4387 | } | ||
| 4388 | |||
| 4389 | if (ferror(tmp)) { | ||
| 4390 | FILE_OP_ERROR(file, "fread"); | ||
| 4391 | fclose(tmp); | ||
| 4392 | return -1; | ||
| 4393 | } | ||
| 4394 | |||
| 4395 | sock_puts(SESSION(session)->sock, ""); | ||
| 4396 | |||
| 4397 | fclose(tmp); | ||
| 4398 | |||
| 4399 | if (new_uid != NULL) | ||
| 4400 | *new_uid = 0; | ||
| 4401 | |||
| 4402 | if (new_uid != NULL && session->uidplus) { | ||
| 4403 | argbuf = g_ptr_array_new(); | ||
| 4404 | |||
| 4405 | ok = imap_cmd_ok(session, argbuf); | ||
| 4406 | if (ok != IMAP_SUCCESS) | ||
| 4407 | log_warning(_("can't append message to %s\n"), | ||
| 4408 | destfolder_); | ||
| 4409 | else if (argbuf->len > 0) { | ||
| 4410 | resp_str = g_ptr_array_index(argbuf, argbuf->len - 1); | ||
| 4411 | if (resp_str && | ||
| 4412 | sscanf(resp_str, "%*u OK [APPENDUID %*u %u]", | ||
| 4413 | &new_uid_) == 1) { | ||
| 4414 | *new_uid = new_uid_; | ||
| 4415 | } | ||
| 4416 | } | ||
| 4417 | |||
| 4418 | ptr_array_free_strings(argbuf); | ||
| 4419 | g_ptr_array_free(argbuf, TRUE); | ||
| 4420 | } else | ||
| 4421 | ok = imap_cmd_ok(session, NULL); | ||
| 4422 | |||
| 4423 | return ok; | ||
| 4424 | } | ||
| 4425 | |||
| 4426 | static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set, | ||
| 4427 | const gchar *destfolder) | ||
| 4428 | { | ||
| 4429 | gint ok; | ||
| 4430 | gchar *destfolder_; | ||
| 4431 | |||
| 4432 | g_return_val_if_fail(destfolder != NULL, IMAP_ERROR); | ||
| 4433 | |||
| 4434 | QUOTE_IF_REQUIRED(destfolder_, destfolder); | ||
| 4435 | ok = imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_); | ||
| 4436 | if (ok == IMAP_SUCCESS) | ||
| 4437 | ok = imap_cmd_ok(session, NULL); | ||
| 4438 | if (ok != IMAP_SUCCESS) { | ||
| 4439 | log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_); | ||
| 4440 | return -1; | ||
| 4441 | } | ||
| 4442 | |||
| 4443 | return ok; | ||
| 4444 | } | ||
| 4445 | |||
| 4446 | gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set) | ||
| 4447 | { | ||
| 4448 | return imap_cmd_gen_send | ||
| 4449 | (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)", | ||
| 4450 | seq_set); | ||
| 4451 | } | ||
| 4452 | |||
| 4453 | static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set, | ||
| 4454 | const gchar *sub_cmd) | ||
| 4455 | { | ||
| 4456 | gint ok; | ||
| 4457 | |||
| 4458 | ok = imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd); | ||
| 4459 | if (ok == IMAP_SUCCESS) | ||
| 4460 | ok = imap_cmd_ok(session, NULL); | ||
| 4461 | if (ok != IMAP_SUCCESS) { | ||
| 4462 | log_warning(_("error while imap command: STORE %s %s\n"), | ||
| 4463 | seq_set, sub_cmd); | ||
| 4464 | return ok; | ||
| 4465 | } | ||
| 4466 | |||
| 4467 | return IMAP_SUCCESS; | ||
| 4468 | } | ||
| 4469 | |||
| 4470 | static gint imap_cmd_expunge(IMAPSession *session) | ||
| 4471 | { | ||
| 4472 | gint ok; | ||
| 4473 | |||
| 4474 | ok = imap_cmd_gen_send(session, "EXPUNGE"); | ||
| 4475 | if (ok == IMAP_SUCCESS) | ||
| 4476 | ok = imap_cmd_ok(session, NULL); | ||
| 4477 | if (ok != IMAP_SUCCESS) { | ||
| 4478 | log_warning(_("error while imap command: EXPUNGE\n")); | ||
| 4479 | return ok; | ||
| 4480 | } | ||
| 4481 | |||
| 4482 | return IMAP_SUCCESS; | ||
| 4483 | } | ||
| 4484 | |||
| 4485 | static gint imap_cmd_close(IMAPSession *session) | ||
| 4486 | { | ||
| 4487 | gint ok; | ||
| 4488 | |||
| 4489 | ok = imap_cmd_gen_send(session, "CLOSE"); | ||
| 4490 | if (ok == IMAP_SUCCESS) | ||
| 4491 | ok = imap_cmd_ok(session, NULL); | ||
| 4492 | if (ok != IMAP_SUCCESS) | ||
| 4493 | log_warning(_("error while imap command: CLOSE\n")); | ||
| 4494 | |||
| 4495 | return ok; | ||
| 4496 | } | ||
| 4497 | |||
| 4498 | static gint imap_cmd_ok_real(IMAPSession *session, GPtrArray *argbuf) | ||
| 4499 | { | ||
| 4500 | gint ok; | ||
| 4501 | gchar *buf; | ||
| 4502 | gint cmd_num; | ||
| 4503 | gchar cmd_status[IMAPBUFSIZE + 1]; | ||
| 4504 | GString *str; | ||
| 4505 | gchar *p; | ||
| 4506 | gchar obuf[32]; | ||
| 4507 | gint len; | ||
| 4508 | gchar *literal; | ||
| 4509 | |||
| 4510 | str = g_string_sized_new(256); | ||
| 4511 | |||
| 4512 | //g_usleep(800000); | ||
| 4513 | while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) { | ||
| 4514 | g_string_append(str, buf); | ||
| 4515 | |||
| 4516 | if ((p = strrchr_with_skip_quote(buf, '"', '{'))) { | ||
| 4517 | /* literal */ | ||
| 4518 | p = strchr_cpy(p + 1, '}', obuf, sizeof(obuf)); | ||
| 4519 | len = atoi(obuf); | ||
| 4520 | if (len < 0 || p == NULL || *p != '\0') { | ||
| 4521 | g_free(buf); | ||
| 4522 | ok = IMAP_ERROR; | ||
| 4523 | break; | ||
| 4524 | } | ||
| 4525 | |||
| 4526 | literal = recv_bytes(SESSION(session)->sock, len); | ||
| 4527 | if (!literal) { | ||
| 4528 | g_free(buf); | ||
| 4529 | ok = IMAP_SOCKET; | ||
| 4530 | break; | ||
| 4531 | } | ||
| 4532 | if (memchr(literal, '\n', len)) | ||
| 4533 | log_print("IMAP4< (literal: %d bytes)\n", len); | ||
| 4534 | else | ||
| 4535 | log_print("IMAP4< %s\n", literal); | ||
| 4536 | |||
| 4537 | g_string_append(str, "\r\n"); | ||
| 4538 | g_string_append_len(str, literal, len); | ||
| 4539 | g_free(literal); | ||
| 4540 | g_free(buf); | ||
| 4541 | continue; | ||
| 4542 | } | ||
| 4543 | |||
| 4544 | g_free(buf); | ||
| 4545 | |||
| 4546 | if (str->str[0] == '*' && str->str[1] == ' ') { | ||
| 4547 | if (argbuf) | ||
| 4548 | g_ptr_array_add(argbuf, g_strdup(str->str + 2)); | ||
| 4549 | |||
| 4550 | g_string_truncate(str, 0); | ||
| 4551 | continue; | ||
| 4552 | } else if (sscanf(str->str, "%d %" Xstr(IMAPBUFSIZE) "s", | ||
| 4553 | &cmd_num, cmd_status) < 2) { | ||
| 4554 | ok = IMAP_ERROR; | ||
| 4555 | } else if (cmd_num == session->cmd_count && | ||
| 4556 | !strcmp(cmd_status, "OK")) { | ||
| 4557 | if (argbuf) | ||
| 4558 | g_ptr_array_add(argbuf, g_strdup(str->str)); | ||
| 4559 | } else { | ||
| 4560 | ok = IMAP_ERROR; | ||
| 4561 | } | ||
| 4562 | |||
| 4563 | break; | ||
| 4564 | } | ||
| 4565 | |||
| 4566 | g_string_free(str, TRUE); | ||
| 4567 | return ok; | ||
| 4568 | } | ||
| 4569 | |||
| 4570 | #if USE_THREADS | ||
| 4571 | static gint imap_cmd_ok_func(IMAPSession *session, gpointer data) | ||
| 4572 | { | ||
| 4573 | GPtrArray *argbuf = (GPtrArray *)data; | ||
| 4574 | gint ok; | ||
| 4575 | |||
| 4576 | ok = imap_cmd_ok_real(session, argbuf); | ||
| 4577 | return ok; | ||
| 4578 | } | ||
| 4579 | #endif | ||
| 4580 | |||
| 4581 | static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf) | ||
| 4582 | { | ||
| 4583 | #if USE_THREADS | ||
| 4584 | return imap_thread_run(session, imap_cmd_ok_func, argbuf); | ||
| 4585 | #else | ||
| 4586 | return imap_cmd_ok_real(session, argbuf); | ||
| 4587 | #endif | ||
| 4588 | } | ||
| 4589 | |||
| 4590 | static gint imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...) | ||
| 4591 | { | ||
| 4592 | IMAPRealSession *real = (IMAPRealSession *)session; | ||
| 4593 | gchar buf[IMAPBUFSIZE]; | ||
| 4594 | gchar tmp[IMAPBUFSIZE]; | ||
| 4595 | gchar *p; | ||
| 4596 | va_list args; | ||
| 4597 | |||
| 4598 | va_start(args, format); | ||
| 4599 | g_vsnprintf(tmp, sizeof(tmp), format, args); | ||
| 4600 | va_end(args); | ||
| 4601 | |||
| 4602 | #if USE_THREADS | ||
| 4603 | if (real->is_running) { | ||
| 4604 | g_warning("imap_cmd_gen_send: cannot send command because another command is already running."); | ||
| 4605 | return IMAP_EAGAIN; | ||
| 4606 | } | ||
| 4607 | #endif | ||
| 4608 | |||
| 4609 | session->cmd_count++; | ||
| 4610 | |||
| 4611 | g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp); | ||
| 4612 | if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && | ||
| 4613 | (p = strchr(tmp + 6, ' '))) { | ||
| 4614 | *p = '\0'; | ||
| 4615 | log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp); | ||
| 4616 | } else | ||
| 4617 | log_print("IMAP4> %d %s\n", session->cmd_count, tmp); | ||
| 4618 | |||
| 4619 | sock_write_all(SESSION(session)->sock, buf, strlen(buf)); | ||
| 4620 | |||
| 4621 | return IMAP_SUCCESS; | ||
| 4622 | } | ||
| 4623 | |||
| 4624 | static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret) | ||
| 4625 | { | ||
| 4626 | gint len; | ||
| 4627 | |||
| 4628 | if ((len = sock_getline(SESSION(session)->sock, ret)) < 0) | ||
| 4629 | return IMAP_SOCKET; | ||
| 4630 | |||
| 4631 | strretchomp(*ret); | ||
| 4632 | |||
| 4633 | if (len > 1000) { | ||
| 4634 | gchar *str; | ||
| 4635 | |||
| 4636 | str = trim_string(*ret, 1000); | ||
| 4637 | log_print("IMAP4< %s\n", str); | ||
| 4638 | g_free(str); | ||
| 4639 | } else | ||
| 4640 | log_print("IMAP4< %s\n", *ret); | ||
| 4641 | |||
| 4642 | session_set_access_time(SESSION(session)); | ||
| 4643 | |||
| 4644 | return IMAP_SUCCESS; | ||
| 4645 | } | ||
| 4646 | |||
| 4647 | static gint imap_cmd_gen_recv_silent(IMAPSession *session, gchar **ret) | ||
| 4648 | { | ||
| 4649 | gint len; | ||
| 4650 | |||
| 4651 | if ((len = sock_getline(SESSION(session)->sock, ret)) < 0) | ||
| 4652 | return IMAP_SOCKET; | ||
| 4653 | |||
| 4654 | strretchomp(*ret); | ||
| 4655 | |||
| 4656 | session_set_access_time(SESSION(session)); | ||
| 4657 | |||
| 4658 | return IMAP_SUCCESS; | ||
| 4659 | } | ||
| 4660 | |||
| 4661 | |||
| 4662 | /* misc utility functions */ | ||
| 4663 | |||
| 4664 | static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len) | ||
| 4665 | { | ||
| 4666 | gchar *tmp; | ||
| 4667 | |||
| 4668 | dest[0] = '\0'; | ||
| 4669 | tmp = strchr(src, ch); | ||
| 4670 | if (!tmp) | ||
| 4671 | return NULL; | ||
| 4672 | |||
| 4673 | memcpy(dest, src, MIN(tmp - src, len - 1)); | ||
| 4674 | dest[MIN(tmp - src, len - 1)] = '\0'; | ||
| 4675 | |||
| 4676 | return tmp + 1; | ||
| 4677 | } | ||
| 4678 | |||
| 4679 | static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len) | ||
| 4680 | { | ||
| 4681 | const gchar *p = src; | ||
| 4682 | gint n = 0; | ||
| 4683 | |||
| 4684 | g_return_val_if_fail(*p == ch, NULL); | ||
| 4685 | |||
| 4686 | *dest = '\0'; | ||
| 4687 | p++; | ||
| 4688 | |||
| 4689 | while (*p != '\0' && *p != ch) { | ||
| 4690 | if (n < len - 1) { | ||
| 4691 | if (*p == '\\' && *(p + 1) != '\0') | ||
| 4692 | p++; | ||
| 4693 | *dest++ = *p++; | ||
| 4694 | } else | ||
| 4695 | p++; | ||
| 4696 | n++; | ||
| 4697 | } | ||
| 4698 | |||
| 4699 | *dest = '\0'; | ||
| 4700 | return (gchar *)(*p == ch ? p + 1 : p); | ||
| 4701 | } | ||
| 4702 | |||
| 4703 | static gchar *search_array_contain_str(GPtrArray *array, gchar *str) | ||
| 4704 | { | ||
| 4705 | gint i; | ||
| 4706 | |||
| 4707 | for (i = 0; i < array->len; i++) { | ||
| 4708 | gchar *tmp; | ||
| 4709 | |||
| 4710 | tmp = g_ptr_array_index(array, i); | ||
| 4711 | if (strstr(tmp, str) != NULL) | ||
| 4712 | return tmp; | ||
| 4713 | } | ||
| 4714 | |||
| 4715 | return NULL; | ||
| 4716 | } | ||
| 4717 | |||
| 4718 | static gchar *search_array_str(GPtrArray *array, gchar *str) | ||
| 4719 | { | ||
| 4720 | gint i; | ||
| 4721 | gint len; | ||
| 4722 | |||
| 4723 | len = strlen(str); | ||
| 4724 | |||
| 4725 | for (i = 0; i < array->len; i++) { | ||
| 4726 | gchar *tmp; | ||
| 4727 | |||
| 4728 | tmp = g_ptr_array_index(array, i); | ||
| 4729 | if (!strncmp(tmp, str, len)) | ||
| 4730 | return tmp; | ||
| 4731 | } | ||
| 4732 | |||
| 4733 | return NULL; | ||
| 4734 | } | ||
| 4735 | |||
| 4736 | static void imap_path_separator_subst(gchar *str, gchar separator) | ||
| 4737 | { | ||
| 4738 | gchar *p; | ||
| 4739 | gboolean in_escape = FALSE; | ||
| 4740 | |||
| 4741 | if (!separator || separator == '/') return; | ||
| 4742 | |||
| 4743 | for (p = str; *p != '\0'; p++) { | ||
| 4744 | if (*p == '/' && !in_escape) | ||
| 4745 | *p = separator; | ||
| 4746 | else if (*p == '&' && *(p + 1) != '-' && !in_escape) | ||
| 4747 | in_escape = TRUE; | ||
| 4748 | else if (*p == '-' && in_escape) | ||
| 4749 | in_escape = FALSE; | ||
| 4750 | } | ||
| 4751 | } | ||
| 4752 | |||
| 4753 | static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str) | ||
| 4754 | { | ||
| 4755 | static iconv_t cd = (iconv_t)-1; | ||
| 4756 | static gboolean iconv_ok = TRUE; | ||
| 4757 | GString *norm_utf7; | ||
| 4758 | gchar *norm_utf7_p; | ||
| 4759 | size_t norm_utf7_len; | ||
| 4760 | const gchar *p; | ||
| 4761 | gchar *to_str, *to_p; | ||
| 4762 | size_t to_len; | ||
| 4763 | gboolean in_escape = FALSE; | ||
| 4764 | |||
| 4765 | if (!iconv_ok) return g_strdup(mutf7_str); | ||
| 4766 | |||
| 4767 | if (cd == (iconv_t)-1) { | ||
| 4768 | cd = iconv_open(CS_INTERNAL, CS_UTF_7); | ||
| 4769 | if (cd == (iconv_t)-1) { | ||
| 4770 | g_warning("iconv cannot convert UTF-7 to %s\n", | ||
| 4771 | CS_INTERNAL); | ||
| 4772 | iconv_ok = FALSE; | ||
| 4773 | return g_strdup(mutf7_str); | ||
| 4774 | } | ||
| 4775 | } | ||
| 4776 | |||
| 4777 | /* modified UTF-7 to normal UTF-7 conversion */ | ||
| 4778 | norm_utf7 = g_string_new(NULL); | ||
| 4779 | |||
| 4780 | for (p = mutf7_str; *p != '\0'; p++) { | ||
| 4781 | /* replace: '&' -> '+', | ||
| 4782 | "&-" -> '&', | ||
| 4783 | "+" -> "+-", | ||
| 4784 | escaped ',' -> '/' */ | ||
| 4785 | if (!in_escape && *p == '&') { | ||
| 4786 | if (*(p + 1) != '-') { | ||
| 4787 | g_string_append_c(norm_utf7, '+'); | ||
| 4788 | in_escape = TRUE; | ||
| 4789 | } else { | ||
| 4790 | g_string_append_c(norm_utf7, '&'); | ||
| 4791 | p++; | ||
| 4792 | } | ||
| 4793 | } else if (!in_escape && *p == '+') { | ||
| 4794 | g_string_append(norm_utf7, "+-"); | ||
| 4795 | } else if (in_escape && *p == ',') { | ||
| 4796 | g_string_append_c(norm_utf7, '/'); | ||
| 4797 | } else if (in_escape && *p == '-') { | ||
| 4798 | g_string_append_c(norm_utf7, '-'); | ||
| 4799 | in_escape = FALSE; | ||
| 4800 | } else { | ||
| 4801 | g_string_append_c(norm_utf7, *p); | ||
| 4802 | } | ||
| 4803 | } | ||
| 4804 | |||
| 4805 | /* somehow iconv() returns error when the last of the string is "+-" */ | ||
| 4806 | g_string_append_c(norm_utf7, '\n'); | ||
| 4807 | norm_utf7_p = norm_utf7->str; | ||
| 4808 | norm_utf7_len = norm_utf7->len; | ||
| 4809 | to_len = strlen(mutf7_str) * 5; | ||
| 4810 | to_p = to_str = g_malloc(to_len + 1); | ||
| 4811 | |||
| 4812 | if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len, | ||
| 4813 | &to_p, &to_len) == -1) { | ||
| 4814 | g_warning(_("iconv cannot convert UTF-7 to %s\n"), CS_INTERNAL); | ||
| 4815 | g_string_free(norm_utf7, TRUE); | ||
| 4816 | g_free(to_str); | ||
| 4817 | return g_strdup(mutf7_str); | ||
| 4818 | } | ||
| 4819 | |||
| 4820 | /* second iconv() call for flushing */ | ||
| 4821 | iconv(cd, NULL, NULL, &to_p, &to_len); | ||
| 4822 | g_string_free(norm_utf7, TRUE); | ||
| 4823 | *to_p = '\0'; | ||
| 4824 | strretchomp(to_str); | ||
| 4825 | |||
| 4826 | return to_str; | ||
| 4827 | } | ||
| 4828 | |||
| 4829 | static gchar *imap_utf8_to_modified_utf7(const gchar *from) | ||
| 4830 | { | ||
| 4831 | static iconv_t cd = (iconv_t)-1; | ||
| 4832 | static gboolean iconv_ok = TRUE; | ||
| 4833 | gchar *norm_utf7, *norm_utf7_p; | ||
| 4834 | size_t from_len, norm_utf7_len; | ||
| 4835 | GString *to_str; | ||
| 4836 | const gchar *from_tmp; | ||
| 4837 | const gchar *p; | ||
| 4838 | gchar *to; | ||
| 4839 | gboolean in_escape = FALSE; | ||
| 4840 | |||
| 4841 | if (!iconv_ok) return g_strdup(from); | ||
| 4842 | |||
| 4843 | if (cd == (iconv_t)-1) { | ||
| 4844 | cd = iconv_open(CS_UTF_7, CS_INTERNAL); | ||
| 4845 | if (cd == (iconv_t)-1) { | ||
| 4846 | g_warning(_("iconv cannot convert %s to UTF-7\n"), | ||
| 4847 | CS_INTERNAL); | ||
| 4848 | iconv_ok = FALSE; | ||
| 4849 | return g_strdup(from); | ||
| 4850 | } | ||
| 4851 | } | ||
| 4852 | |||
| 4853 | /* UTF-8 to normal UTF-7 conversion */ | ||
| 4854 | from_tmp = from; | ||
| 4855 | from_len = strlen(from); | ||
| 4856 | norm_utf7_len = from_len * 5; | ||
| 4857 | norm_utf7 = g_malloc(norm_utf7_len + 1); | ||
| 4858 | norm_utf7_p = norm_utf7; | ||
| 4859 | |||
| 4860 | while (from_len > 0) { | ||
| 4861 | if (*from_tmp == '+') { | ||
| 4862 | *norm_utf7_p++ = '+'; | ||
| 4863 | *norm_utf7_p++ = '-'; | ||
| 4864 | norm_utf7_len -= 2; | ||
| 4865 | from_tmp++; | ||
| 4866 | from_len--; | ||
| 4867 | } else if (g_ascii_isprint(*from_tmp)) { | ||
| 4868 | /* printable ascii char */ | ||
| 4869 | *norm_utf7_p = *from_tmp; | ||
| 4870 | norm_utf7_p++; | ||
| 4871 | norm_utf7_len--; | ||
| 4872 | from_tmp++; | ||
| 4873 | from_len--; | ||
| 4874 | } else { | ||
| 4875 | size_t conv_len = 0; | ||
| 4876 | |||
| 4877 | /* unprintable char: convert to UTF-7 */ | ||
| 4878 | p = from_tmp; | ||
| 4879 | while (!g_ascii_isprint(*p) && conv_len < from_len) { | ||
| 4880 | conv_len += g_utf8_skip[*(guchar *)p]; | ||
| 4881 | p += g_utf8_skip[*(guchar *)p]; | ||
| 4882 | } | ||
| 4883 | |||
| 4884 | from_len -= conv_len; | ||
| 4885 | if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, | ||
| 4886 | &conv_len, | ||
| 4887 | &norm_utf7_p, &norm_utf7_len) == -1) { | ||
| 4888 | g_warning("iconv cannot convert %s to UTF-7\n", | ||
| 4889 | CS_INTERNAL); | ||
| 4890 | g_free(norm_utf7); | ||
| 4891 | return g_strdup(from); | ||
| 4892 | } | ||
| 4893 | |||
| 4894 | /* second iconv() call for flushing */ | ||
| 4895 | iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len); | ||
| 4896 | } | ||
| 4897 | } | ||
| 4898 | |||
| 4899 | *norm_utf7_p = '\0'; | ||
| 4900 | to_str = g_string_new(NULL); | ||
| 4901 | for (p = norm_utf7; p < norm_utf7_p; p++) { | ||
| 4902 | /* replace: '&' -> "&-", | ||
| 4903 | '+' -> '&', | ||
| 4904 | "+-" -> '+', | ||
| 4905 | BASE64 '/' -> ',' */ | ||
| 4906 | if (!in_escape && *p == '&') { | ||
| 4907 | g_string_append(to_str, "&-"); | ||
| 4908 | } else if (!in_escape && *p == '+') { | ||
| 4909 | if (*(p + 1) == '-') { | ||
| 4910 | g_string_append_c(to_str, '+'); | ||
| 4911 | p++; | ||
| 4912 | } else { | ||
| 4913 | g_string_append_c(to_str, '&'); | ||
| 4914 | in_escape = TRUE; | ||
| 4915 | } | ||
| 4916 | } else if (in_escape && *p == '/') { | ||
| 4917 | g_string_append_c(to_str, ','); | ||
| 4918 | } else if (in_escape && *p == '-') { | ||
| 4919 | g_string_append_c(to_str, '-'); | ||
| 4920 | in_escape = FALSE; | ||
| 4921 | } else { | ||
| 4922 | g_string_append_c(to_str, *p); | ||
| 4923 | } | ||
| 4924 | } | ||
| 4925 | |||
| 4926 | if (in_escape) { | ||
| 4927 | in_escape = FALSE; | ||
| 4928 | g_string_append_c(to_str, '-'); | ||
| 4929 | } | ||
| 4930 | |||
| 4931 | to = g_string_free(to_str, FALSE); | ||
| 4932 | g_free(norm_utf7); | ||
| 4933 | |||
| 4934 | return to; | ||
| 4935 | } | ||
| 4936 | |||
| 4937 | static GSList *imap_get_seq_set_from_msglist(GSList *msglist, gint limit) | ||
| 4938 | { | ||
| 4939 | GString *str; | ||
| 4940 | GSList *sorted_list, *cur; | ||
| 4941 | guint first, last, next; | ||
| 4942 | gchar *ret_str; | ||
| 4943 | GSList *ret_list = NULL; | ||
| 4944 | gint count = 0; | ||
| 4945 | |||
| 4946 | if (msglist == NULL) | ||
| 4947 | return NULL; | ||
| 4948 | |||
| 4949 | str = g_string_sized_new(256); | ||
| 4950 | |||
| 4951 | sorted_list = g_slist_copy(msglist); | ||
| 4952 | sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER, | ||
| 4953 | SORT_ASCENDING); | ||
| 4954 | |||
| 4955 | first = ((MsgInfo *)sorted_list->data)->msgnum; | ||
| 4956 | |||
| 4957 | for (cur = sorted_list; cur != NULL; cur = cur->next) { | ||
| 4958 | ++count; | ||
| 4959 | last = ((MsgInfo *)cur->data)->msgnum; | ||
| 4960 | if (cur->next) | ||
| 4961 | next = ((MsgInfo *)cur->next->data)->msgnum; | ||
| 4962 | else | ||
| 4963 | next = 0; | ||
| 4964 | |||
| 4965 | if (limit > 0 && count >= limit) { | ||
| 4966 | if (str->len > 0) | ||
| 4967 | g_string_append_c(str, ','); | ||
| 4968 | if (first == last) | ||
| 4969 | g_string_sprintfa(str, "%u", first); | ||
| 4970 | else | ||
| 4971 | g_string_sprintfa(str, "%u:%u", first, last); | ||
| 4972 | |||
| 4973 | first = next; | ||
| 4974 | |||
| 4975 | ret_str = g_strdup(str->str); | ||
| 4976 | ret_list = g_slist_append(ret_list, ret_str); | ||
| 4977 | g_string_truncate(str, 0); | ||
| 4978 | count = 0; | ||
| 4979 | continue; | ||
| 4980 | } | ||
| 4981 | |||
| 4982 | if (last + 1 != next || next == 0) { | ||
| 4983 | if (str->len > 0) | ||
| 4984 | g_string_append_c(str, ','); | ||
| 4985 | if (first == last) | ||
| 4986 | g_string_sprintfa(str, "%u", first); | ||
| 4987 | else | ||
| 4988 | g_string_sprintfa(str, "%u:%u", first, last); | ||
| 4989 | |||
| 4990 | first = next; | ||
| 4991 | |||
| 4992 | if (str->len > IMAP_CMD_LIMIT) { | ||
| 4993 | ret_str = g_strdup(str->str); | ||
| 4994 | ret_list = g_slist_append(ret_list, ret_str); | ||
| 4995 | g_string_truncate(str, 0); | ||
| 4996 | } | ||
| 4997 | } | ||
| 4998 | } | ||
| 4999 | |||
| 5000 | if (str->len > 0) { | ||
| 5001 | ret_str = g_strdup(str->str); | ||
| 5002 | ret_list = g_slist_append(ret_list, ret_str); | ||
| 5003 | } | ||
| 5004 | |||
| 5005 | g_slist_free(sorted_list); | ||
| 5006 | g_string_free(str, TRUE); | ||
| 5007 | |||
| 5008 | return ret_list; | ||
| 5009 | } | ||
| 5010 | |||
| 5011 | static gint imap_seq_set_get_count(const gchar *seq_set) | ||
| 5012 | { | ||
| 5013 | gint count = 0; | ||
| 5014 | guint first, last; | ||
| 5015 | gchar *tmp, *p, *q; | ||
| 5016 | |||
| 5017 | p = q = tmp = g_strdup(seq_set); | ||
| 5018 | |||
| 5019 | while (*p) { | ||
| 5020 | if (*p == ',') { | ||
| 5021 | *p = '\0'; | ||
| 5022 | if (sscanf(q, "%u:%u", &first, &last) == 2) | ||
| 5023 | count += last - first + 1; | ||
| 5024 | else if (sscanf(q, "%u", &first) == 1) | ||
| 5025 | count++; | ||
| 5026 | q = ++p; | ||
| 5027 | } else | ||
| 5028 | ++p; | ||
| 5029 | } | ||
| 5030 | if (q != p) { | ||
| 5031 | if (sscanf(q, "%u:%u", &first, &last) == 2) | ||
| 5032 | count += last - first + 1; | ||
| 5033 | else if (sscanf(q, "%u", &first) == 1) | ||
| 5034 | count++; | ||
| 5035 | } | ||
| 5036 | |||
| 5037 | g_free(tmp); | ||
| 5038 | |||
| 5039 | return count; | ||
| 5040 | } | ||
| 5041 | |||
| 5042 | static void imap_seq_set_free(GSList *seq_list) | ||
| 5043 | { | ||
| 5044 | slist_free_strings(seq_list); | ||
| 5045 | g_slist_free(seq_list); | ||
| 5046 | } | ||
| 5047 | |||
| 5048 | static GHashTable *imap_get_uid_table(GArray *array) | ||
| 5049 | { | ||
| 5050 | GHashTable *table; | ||
| 5051 | gint i; | ||
| 5052 | guint32 uid; | ||
| 5053 | |||
| 5054 | g_return_val_if_fail(array != NULL, NULL); | ||
| 5055 | |||
| 5056 | table = g_hash_table_new(NULL, g_direct_equal); | ||
| 5057 | |||
| 5058 | for (i = 0; i < array->len; i++) { | ||
| 5059 | uid = g_array_index(array, guint32, i); | ||
| 5060 | g_hash_table_insert(table, GUINT_TO_POINTER(uid), | ||
| 5061 | GINT_TO_POINTER(i + 1)); | ||
| 5062 | } | ||
| 5063 | |||
| 5064 | return table; | ||
| 5065 | } | ||
| 5066 | |||
| 5067 | static gboolean imap_rename_folder_func(GNode *node, gpointer data) | ||
| 5068 | { | ||
| 5069 | FolderItem *item = node->data; | ||
| 5070 | gchar **paths = data; | ||
| 5071 | const gchar *oldpath = paths[0]; | ||
| 5072 | const gchar *newpath = paths[1]; | ||
| 5073 | gchar *base; | ||
| 5074 | gchar *new_itempath; | ||
| 5075 | gint oldpathlen; | ||
| 5076 | |||
| 5077 | oldpathlen = strlen(oldpath); | ||
| 5078 | if (strncmp(oldpath, item->path, oldpathlen) != 0) { | ||
| 5079 | g_warning("path doesn't match: %s, %s\n", oldpath, item->path); | ||
| 5080 | return TRUE; | ||
| 5081 | } | ||
| 5082 | |||
| 5083 | base = item->path + oldpathlen; | ||
| 5084 | while (*base == '/') base++; | ||
| 5085 | if (*base == '\0') | ||
| 5086 | new_itempath = g_strdup(newpath); | ||
| 5087 | else | ||
| 5088 | new_itempath = g_strconcat(newpath, "/", base, NULL); | ||
| 5089 | g_free(item->path); | ||
| 5090 | item->path = new_itempath; | ||
| 5091 | |||
| 5092 | return FALSE; | ||
| 5093 | } | ||
| 5094 | |||
| 5095 | #if USE_THREADS | ||
| 5096 | static void imap_thread_run_proxy(gpointer push_data, gpointer data) | ||
| 5097 | { | ||
| 5098 | IMAPRealSession *real = (IMAPRealSession *)data; | ||
| 5099 | |||
| 5100 | debug_print("imap_thread_run_proxy (%p): calling thread_func\n", g_thread_self()); | ||
| 5101 | real->retval = real->thread_func(IMAP_SESSION(real), real->thread_data); | ||
| 5102 | g_atomic_int_set(&real->flag, 1); | ||
| 5103 | g_main_context_wakeup(NULL); | ||
| 5104 | debug_print("imap_thread_run_proxy (%p): thread_func done\n", g_thread_self()); | ||
| 5105 | } | ||
| 5106 | |||
| 5107 | static gint imap_thread_run(IMAPSession *session, IMAPThreadFunc func, | ||
| 5108 | gpointer data) | ||
| 5109 | { | ||
| 5110 | IMAPRealSession *real = (IMAPRealSession *)session; | ||
| 5111 | gint ret; | ||
| 5112 | |||
| 5113 | if (real->is_running) { | ||
| 5114 | g_warning("imap_thread_run: thread is already running"); | ||
| 5115 | return IMAP_ERROR; | ||
| 5116 | } | ||
| 5117 | |||
| 5118 | if (!real->pool) { | ||
| 5119 | real->pool = g_thread_pool_new(imap_thread_run_proxy, real, | ||
| 5120 | -1, FALSE, NULL); | ||
| 5121 | if (!real->pool) | ||
| 5122 | return IMAP_ERROR; | ||
| 5123 | } | ||
| 5124 | |||
| 5125 | real->is_running = TRUE; | ||
| 5126 | real->thread_func = func; | ||
| 5127 | real->thread_data = data; | ||
| 5128 | real->flag = 0; | ||
| 5129 | real->retval = 0; | ||
| 5130 | |||
| 5131 | g_thread_pool_push(real->pool, real, NULL); | ||
| 5132 | |||
| 5133 | while (g_atomic_int_get(&real->flag) == 0) | ||
| 5134 | event_loop_iterate(); | ||
| 5135 | |||
| 5136 | real->is_running = FALSE; | ||
| 5137 | real->thread_func = NULL; | ||
| 5138 | real->thread_data = NULL; | ||
| 5139 | real->flag = 0; | ||
| 5140 | ret = real->retval; | ||
| 5141 | real->retval = 0; | ||
| 5142 | log_flush(); | ||
| 5143 | |||
| 5144 | return ret; | ||
| 5145 | } | ||
| 5146 | |||
| 5147 | static gint imap_thread_run_progress(IMAPSession *session, IMAPThreadFunc func, | ||
| 5148 | IMAPProgressFunc progress_func, | ||
| 5149 | gpointer data) | ||
| 5150 | { | ||
| 5151 | IMAPRealSession *real = (IMAPRealSession *)session; | ||
| 5152 | gint prev_count = 0; | ||
| 5153 | gint ret; | ||
| 5154 | |||
| 5155 | if (real->is_running) { | ||
| 5156 | g_warning("imap_thread_run: thread is already running"); | ||
| 5157 | return IMAP_ERROR; | ||
| 5158 | } | ||
| 5159 | |||
| 5160 | if (!real->pool) { | ||
| 5161 | real->pool = g_thread_pool_new(imap_thread_run_proxy, real, | ||
| 5162 | -1, FALSE, NULL); | ||
| 5163 | if (!real->pool) | ||
| 5164 | return IMAP_ERROR; | ||
| 5165 | } | ||
| 5166 | |||
| 5167 | real->is_running = TRUE; | ||
| 5168 | real->thread_func = func; | ||
| 5169 | real->thread_data = data; | ||
| 5170 | real->flag = 0; | ||
| 5171 | real->retval = 0; | ||
| 5172 | real->prog_count = 0; | ||
| 5173 | real->prog_total = 0; | ||
| 5174 | |||
| 5175 | g_thread_pool_push(real->pool, real, NULL); | ||
| 5176 | |||
| 5177 | while (g_atomic_int_get(&real->flag) == 0) { | ||
| 5178 | event_loop_iterate(); | ||
| 5179 | if (prev_count != real->prog_count && real->prog_total > 0) { | ||
| 5180 | progress_func(session, real->prog_count, | ||
| 5181 | real->prog_total, data); | ||
| 5182 | prev_count = real->prog_count; | ||
| 5183 | } | ||
| 5184 | } | ||
| 5185 | |||
| 5186 | real->is_running = FALSE; | ||
| 5187 | real->thread_func = NULL; | ||
| 5188 | real->thread_data = NULL; | ||
| 5189 | real->flag = 0; | ||
| 5190 | ret = real->retval; | ||
| 5191 | real->retval = 0; | ||
| 5192 | real->prog_count = 0; | ||
| 5193 | real->prog_total = 0; | ||
| 5194 | log_flush(); | ||
| 5195 | |||
| 5196 | return ret; | ||
| 5197 | } | ||
| 5198 | #endif /* USE_THREADS */ | ||
| 5199 | |||
| 5200 | gboolean imap_is_session_active(IMAPFolder *folder) | ||
| 5201 | { | ||
| 5202 | #if USE_THREADS | ||
| 5203 | IMAPRealSession *real; | ||
| 5204 | |||
| 5205 | g_return_val_if_fail(folder != NULL, FALSE); | ||
| 5206 | |||
| 5207 | real = (IMAPRealSession *)(REMOTE_FOLDER(folder)->session); | ||
| 5208 | if (!real) | ||
| 5209 | return FALSE; | ||
| 5210 | |||
| 5211 | return real->is_running; | ||
| 5212 | #else | ||
| 5213 | return FALSE; | ||
| 5214 | #endif | ||
| 5215 | } | ||
diff --git a/libsylph/imap.h b/libsylph/imap.h new file mode 100644 index 0000000..ac928b4 --- /dev/null +++ b/libsylph/imap.h | |||
| @@ -0,0 +1,130 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2011 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __IMAP_H__ | ||
| 21 | #define __IMAP_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <time.h> | ||
| 29 | |||
| 30 | #include "folder.h" | ||
| 31 | #include "session.h" | ||
| 32 | #include "procmsg.h" | ||
| 33 | |||
| 34 | typedef struct _IMAPFolder IMAPFolder; | ||
| 35 | typedef struct _IMAPSession IMAPSession; | ||
| 36 | typedef struct _IMAPNameSpace IMAPNameSpace; | ||
| 37 | |||
| 38 | #define IMAP_FOLDER(obj) ((IMAPFolder *)obj) | ||
| 39 | #define IMAP_SESSION(obj) ((IMAPSession *)obj) | ||
| 40 | |||
| 41 | #include "prefs_account.h" | ||
| 42 | |||
| 43 | typedef enum | ||
| 44 | { | ||
| 45 | IMAP_AUTH_LOGIN = 1 << 0, | ||
| 46 | IMAP_AUTH_CRAM_MD5 = 1 << 1, | ||
| 47 | IMAP_AUTH_PLAIN = 1 << 2 | ||
| 48 | } IMAPAuthType; | ||
| 49 | |||
| 50 | struct _IMAPFolder | ||
| 51 | { | ||
| 52 | RemoteFolder rfolder; | ||
| 53 | |||
| 54 | /* list of IMAPNameSpace */ | ||
| 55 | GList *ns_personal; | ||
| 56 | GList *ns_others; | ||
| 57 | GList *ns_shared; | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct _IMAPSession | ||
| 61 | { | ||
| 62 | Session session; | ||
| 63 | |||
| 64 | gboolean authenticated; | ||
| 65 | |||
| 66 | gchar **capability; | ||
| 67 | gboolean uidplus; | ||
| 68 | |||
| 69 | gchar *mbox; | ||
| 70 | guint cmd_count; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct _IMAPNameSpace | ||
| 74 | { | ||
| 75 | gchar *name; | ||
| 76 | gchar separator; | ||
| 77 | }; | ||
| 78 | |||
| 79 | #define IMAP_SUCCESS 0 | ||
| 80 | #define IMAP_SOCKET 2 | ||
| 81 | #define IMAP_AUTHFAIL 3 | ||
| 82 | #define IMAP_PROTOCOL 4 | ||
| 83 | #define IMAP_SYNTAX 5 | ||
| 84 | #define IMAP_IOERR 6 | ||
| 85 | #define IMAP_ERROR 7 | ||
| 86 | #define IMAP_EAGAIN 8 | ||
| 87 | |||
| 88 | #define IMAPBUFSIZE 8192 | ||
| 89 | |||
| 90 | typedef enum | ||
| 91 | { | ||
| 92 | IMAP_FLAG_SEEN = 1 << 0, | ||
| 93 | IMAP_FLAG_ANSWERED = 1 << 1, | ||
| 94 | IMAP_FLAG_FLAGGED = 1 << 2, | ||
| 95 | IMAP_FLAG_DELETED = 1 << 3, | ||
| 96 | IMAP_FLAG_DRAFT = 1 << 4, | ||
| 97 | |||
| 98 | /* color label keywords : 1 << 7 ... 1 << 9 | ||
| 99 | compatible with procmsg.h: MSG_CLABEL* macros */ | ||
| 100 | } IMAPFlags; | ||
| 101 | |||
| 102 | #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0) | ||
| 103 | #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0) | ||
| 104 | #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0) | ||
| 105 | #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0) | ||
| 106 | #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0) | ||
| 107 | |||
| 108 | #define IMAP_GET_COLORLABEL(flags) (flags & (7 << MSG_CLABEL_SBIT)) | ||
| 109 | #define IMAP_GET_COLORLABEL_VALUE(flags) \ | ||
| 110 | (IMAP_GET_COLORLABEL(flags) >> MSG_CLABEL_SBIT) | ||
| 111 | #define IMAP_SET_COLORLABEL_VALUE(flags, v) \ | ||
| 112 | ((flags) |= ((v & 7) << MSG_CLABEL_SBIT)) | ||
| 113 | |||
| 114 | FolderClass *imap_get_class (void); | ||
| 115 | |||
| 116 | gint imap_msg_set_perm_flags (MsgInfo *msginfo, | ||
| 117 | MsgPermFlags flags); | ||
| 118 | gint imap_msg_unset_perm_flags (MsgInfo *msginfo, | ||
| 119 | MsgPermFlags flags); | ||
| 120 | gint imap_msg_list_set_perm_flags (GSList *msglist, | ||
| 121 | MsgPermFlags flags); | ||
| 122 | gint imap_msg_list_unset_perm_flags (GSList *msglist, | ||
| 123 | MsgPermFlags flags); | ||
| 124 | |||
| 125 | gint imap_msg_list_set_colorlabel_flags (GSList *msglist, | ||
| 126 | guint color); | ||
| 127 | |||
| 128 | gboolean imap_is_session_active (IMAPFolder *folder); | ||
| 129 | |||
| 130 | #endif /* __IMAP_H__ */ | ||
diff --git a/libsylph/libsylph-0.def b/libsylph/libsylph-0.def new file mode 100644 index 0000000..f625f1f --- /dev/null +++ b/libsylph/libsylph-0.def | |||
| @@ -0,0 +1,714 @@ | |||
| 1 | EXPORTS | ||
| 2 | account_address_exist @ 1 | ||
| 3 | account_append @ 2 | ||
| 4 | account_destroy @ 3 | ||
| 5 | account_find_from_address @ 4 | ||
| 6 | account_find_from_id @ 5 | ||
| 7 | account_find_from_item @ 6 | ||
| 8 | account_find_from_item_property @ 7 | ||
| 9 | account_find_from_message_file @ 8 | ||
| 10 | account_find_from_msginfo @ 9 | ||
| 11 | account_find_from_smtp_server @ 10 | ||
| 12 | account_foreach @ 11 | ||
| 13 | account_get_current_account @ 12 | ||
| 14 | account_get_default @ 13 | ||
| 15 | account_get_list @ 14 | ||
| 16 | account_get_special_folder @ 15 | ||
| 17 | account_list_free @ 16 | ||
| 18 | account_read_config_all @ 17 | ||
| 19 | account_set_as_default @ 18 | ||
| 20 | account_update_lock @ 19 | ||
| 21 | account_update_unlock @ 20 | ||
| 22 | account_updated @ 21 | ||
| 23 | account_write_config_all @ 22 | ||
| 24 | add_history @ 23 | ||
| 25 | address_equal @ 24 | ||
| 26 | address_list_append @ 25 | ||
| 27 | address_list_append_orig @ 26 | ||
| 28 | address_table @ 27 DATA | ||
| 29 | base64_decode @ 28 | ||
| 30 | base64_decoder_decode @ 29 | ||
| 31 | base64_decoder_free @ 30 | ||
| 32 | base64_decoder_new @ 31 | ||
| 33 | base64_encode @ 32 | ||
| 34 | canonicalize_file @ 33 | ||
| 35 | canonicalize_file_replace @ 34 | ||
| 36 | canonicalize_file_stream @ 35 | ||
| 37 | canonicalize_str @ 36 | ||
| 38 | change_dir @ 37 | ||
| 39 | change_file_mode_rw @ 38 | ||
| 40 | check_line_length @ 39 | ||
| 41 | close_log_file @ 40 | ||
| 42 | conv_check_file_encoding @ 41 | ||
| 43 | conv_code_converter_destroy @ 42 | ||
| 44 | conv_code_converter_new @ 43 | ||
| 45 | conv_codeset_strdup_full @ 44 | ||
| 46 | conv_convert @ 45 | ||
| 47 | conv_copy_dir @ 46 | ||
| 48 | conv_copy_file @ 47 | ||
| 49 | conv_encode_filename @ 48 | ||
| 50 | conv_encode_header @ 49 | ||
| 51 | conv_filename_from_utf8 @ 50 | ||
| 52 | conv_filename_to_utf8 @ 51 | ||
| 53 | conv_get_autodetect_type @ 52 | ||
| 54 | conv_get_charset_from_str @ 53 | ||
| 55 | conv_get_charset_str @ 54 | ||
| 56 | conv_get_code_conv_func @ 55 | ||
| 57 | conv_get_current_locale @ 56 | ||
| 58 | conv_get_internal_charset @ 57 | ||
| 59 | conv_get_internal_charset_str @ 58 | ||
| 60 | conv_get_locale_charset @ 59 | ||
| 61 | conv_get_locale_charset_str @ 60 | ||
| 62 | conv_get_outgoing_charset @ 61 | ||
| 63 | conv_get_outgoing_charset_str @ 62 | ||
| 64 | conv_guess_ja_encoding @ 63 | ||
| 65 | conv_iconv_strdup @ 64 | ||
| 66 | conv_iconv_strdup_with_cd @ 65 | ||
| 67 | conv_is_ja_locale @ 66 | ||
| 68 | conv_is_multibyte_encoding @ 67 | ||
| 69 | conv_localetodisp @ 68 | ||
| 70 | conv_mb_alnum @ 69 | ||
| 71 | conv_set_autodetect_type @ 70 | ||
| 72 | conv_unmime_header @ 71 | ||
| 73 | conv_utf8todisp @ 72 | ||
| 74 | copy_dir @ 73 | ||
| 75 | copy_file @ 74 | ||
| 76 | copy_file_part @ 75 | ||
| 77 | copy_mbox @ 76 | ||
| 78 | cur_account @ 77 DATA | ||
| 79 | custom_header_find @ 78 | ||
| 80 | custom_header_free @ 79 | ||
| 81 | custom_header_get_str @ 80 | ||
| 82 | custom_header_read_config @ 81 | ||
| 83 | custom_header_read_str @ 82 | ||
| 84 | custom_header_write_config @ 83 | ||
| 85 | debug_print @ 84 | ||
| 86 | decode_uri @ 85 | ||
| 87 | decode_xdigit_encoded_str @ 86 | ||
| 88 | dirent_is_directory @ 87 | ||
| 89 | dirent_is_regular_file @ 88 | ||
| 90 | display_header_prop_free @ 89 | ||
| 91 | display_header_prop_get_str @ 90 | ||
| 92 | display_header_prop_read_str @ 91 | ||
| 93 | eliminate_address_comment @ 92 | ||
| 94 | eliminate_parenthesis @ 93 | ||
| 95 | eliminate_quote @ 94 | ||
| 96 | empty_mbox @ 95 | ||
| 97 | encode_uri @ 96 | ||
| 98 | event_loop_iterate @ 97 | ||
| 99 | execute_async @ 98 | ||
| 100 | execute_command_line @ 99 | ||
| 101 | execute_command_line_async_wait @ 100 | ||
| 102 | execute_open_file @ 101 | ||
| 103 | execute_print_file @ 102 | ||
| 104 | execute_sync @ 103 | ||
| 105 | export_to_mbox @ 104 | ||
| 106 | extract_address @ 105 | ||
| 107 | extract_list_id_str @ 106 | ||
| 108 | extract_parenthesis @ 107 | ||
| 109 | extract_parenthesis_with_escape @ 108 | ||
| 110 | extract_parenthesis_with_skip_quote @ 109 | ||
| 111 | extract_quote @ 110 | ||
| 112 | extract_quote_with_escape @ 111 | ||
| 113 | fd_accept @ 112 | ||
| 114 | fd_close @ 113 | ||
| 115 | fd_connect_inet @ 114 | ||
| 116 | fd_connect_unix @ 115 | ||
| 117 | fd_getline @ 116 | ||
| 118 | fd_gets @ 117 | ||
| 119 | fd_open_inet @ 118 | ||
| 120 | fd_open_unix @ 119 | ||
| 121 | fd_read @ 120 | ||
| 122 | fd_recv @ 121 | ||
| 123 | fd_write @ 122 | ||
| 124 | fd_write_all @ 123 | ||
| 125 | file_exist @ 124 | ||
| 126 | file_read_stream_to_str @ 125 | ||
| 127 | file_read_to_str @ 126 | ||
| 128 | filter_action_exec @ 127 | ||
| 129 | filter_action_list_free @ 128 | ||
| 130 | filter_action_new @ 129 | ||
| 131 | filter_apply @ 130 | ||
| 132 | filter_apply_msginfo @ 131 | ||
| 133 | filter_cond_list_free @ 132 | ||
| 134 | filter_cond_new @ 133 | ||
| 135 | filter_get_keyword_from_msg @ 134 | ||
| 136 | filter_get_str @ 135 | ||
| 137 | filter_info_free @ 136 | ||
| 138 | filter_info_new @ 137 | ||
| 139 | filter_list_delete_path @ 138 | ||
| 140 | filter_list_rename_path @ 139 | ||
| 141 | filter_match_rule @ 140 | ||
| 142 | filter_read_config @ 141 | ||
| 143 | filter_read_file @ 142 | ||
| 144 | filter_read_str @ 143 | ||
| 145 | filter_rule_delete_action_by_dest_path @ 144 | ||
| 146 | filter_rule_free @ 145 | ||
| 147 | filter_rule_list_free @ 146 | ||
| 148 | filter_rule_match_type_str_to_enum @ 147 | ||
| 149 | filter_rule_new @ 148 | ||
| 150 | filter_rule_rename_dest_path @ 149 | ||
| 151 | filter_rule_requires_full_headers @ 150 | ||
| 152 | filter_write_config @ 151 | ||
| 153 | filter_write_file @ 152 | ||
| 154 | filter_xml_node_to_filter_list @ 153 | ||
| 155 | folder_add @ 154 | ||
| 156 | folder_create_tree @ 155 | ||
| 157 | folder_destroy @ 156 | ||
| 158 | folder_find_child_item_by_name @ 157 | ||
| 159 | folder_find_from_name @ 158 | ||
| 160 | folder_find_from_path @ 159 | ||
| 161 | folder_find_item_and_num_from_id @ 160 | ||
| 162 | folder_find_item_from_identifier @ 161 | ||
| 163 | folder_find_item_from_path @ 162 | ||
| 164 | folder_get_default_draft @ 163 | ||
| 165 | folder_get_default_folder @ 164 | ||
| 166 | folder_get_default_inbox @ 165 | ||
| 167 | folder_get_default_outbox @ 166 | ||
| 168 | folder_get_default_queue @ 167 | ||
| 169 | folder_get_default_trash @ 168 | ||
| 170 | folder_get_identifier @ 169 | ||
| 171 | folder_get_list @ 170 | ||
| 172 | folder_get_path @ 171 | ||
| 173 | folder_get_status @ 172 | ||
| 174 | folder_item_add_msg @ 173 | ||
| 175 | folder_item_add_msg_msginfo @ 174 | ||
| 176 | folder_item_add_msgs @ 175 | ||
| 177 | folder_item_add_msgs_msginfo @ 176 | ||
| 178 | folder_item_append @ 177 | ||
| 179 | folder_item_close @ 178 | ||
| 180 | folder_item_compare @ 179 | ||
| 181 | folder_item_copy @ 180 | ||
| 182 | folder_item_copy_msg @ 181 | ||
| 183 | folder_item_copy_msgs @ 182 | ||
| 184 | folder_item_destroy @ 183 | ||
| 185 | folder_item_fetch_all_msg @ 184 | ||
| 186 | folder_item_fetch_msg @ 185 | ||
| 187 | folder_item_get_cache_file @ 186 | ||
| 188 | folder_item_get_identifier @ 187 | ||
| 189 | folder_item_get_mark_file @ 188 | ||
| 190 | folder_item_get_msg_list @ 189 | ||
| 191 | folder_item_get_msginfo @ 190 | ||
| 192 | folder_item_get_path @ 191 | ||
| 193 | folder_item_get_uncached_msg_list @ 192 | ||
| 194 | folder_item_is_msg_changed @ 193 | ||
| 195 | folder_item_move_msg @ 194 | ||
| 196 | folder_item_move_msgs @ 195 | ||
| 197 | folder_item_new @ 196 | ||
| 198 | folder_item_remove @ 197 | ||
| 199 | folder_item_remove_all_msg @ 198 | ||
| 200 | folder_item_remove_children @ 199 | ||
| 201 | folder_item_remove_msg @ 200 | ||
| 202 | folder_item_remove_msgs @ 201 | ||
| 203 | folder_item_scan @ 202 | ||
| 204 | folder_item_scan_foreach @ 203 | ||
| 205 | folder_local_folder_destroy @ 204 | ||
| 206 | folder_local_folder_init @ 205 | ||
| 207 | folder_new @ 206 | ||
| 208 | folder_read_list @ 207 | ||
| 209 | folder_remote_folder_destroy @ 208 | ||
| 210 | folder_remote_folder_init @ 209 | ||
| 211 | folder_scan_tree @ 210 | ||
| 212 | folder_set_missing_folders @ 211 | ||
| 213 | folder_set_name @ 212 | ||
| 214 | folder_set_ui_func @ 213 | ||
| 215 | folder_tree_destroy @ 214 | ||
| 216 | folder_unref_account_all @ 215 | ||
| 217 | folder_write_list @ 216 | ||
| 218 | fromuutobits @ 217 | ||
| 219 | generate_mime_boundary @ 218 | ||
| 220 | get_abbrev_newsgroup_name @ 219 | ||
| 221 | get_alt_filename @ 220 | ||
| 222 | get_command_output @ 221 | ||
| 223 | get_debug_mode @ 222 | ||
| 224 | get_document_dir @ 223 | ||
| 225 | get_domain_name @ 224 | ||
| 226 | get_file_size @ 225 | ||
| 227 | get_file_size_as_crlf @ 226 | ||
| 228 | get_home_dir @ 227 | ||
| 229 | get_imap_cache_dir @ 228 | ||
| 230 | get_left_file_size @ 229 | ||
| 231 | get_mail_base_dir @ 230 | ||
| 232 | get_mime_tmp_dir @ 231 | ||
| 233 | get_news_cache_dir @ 232 | ||
| 234 | get_next_word_len @ 233 | ||
| 235 | get_old_rc_dir @ 234 | ||
| 236 | get_outgoing_rfc2822_file @ 235 | ||
| 237 | get_outgoing_rfc2822_str @ 236 | ||
| 238 | get_quote_level @ 237 | ||
| 239 | get_rc_dir @ 238 | ||
| 240 | get_rfc822_date @ 239 | ||
| 241 | get_startup_dir @ 240 | ||
| 242 | get_template_dir @ 241 | ||
| 243 | get_tmp_dir @ 242 | ||
| 244 | get_tmp_file @ 243 | ||
| 245 | get_uri_len @ 244 | ||
| 246 | get_uri_path @ 245 | ||
| 247 | hash_free_strings @ 246 | ||
| 248 | hash_free_value_mem @ 247 | ||
| 249 | html_parse @ 248 | ||
| 250 | html_parser_destroy @ 249 | ||
| 251 | html_parser_new @ 250 | ||
| 252 | imap_get_class @ 251 | ||
| 253 | imap_msg_list_set_perm_flags @ 252 | ||
| 254 | imap_msg_list_unset_perm_flags @ 253 | ||
| 255 | imap_msg_set_perm_flags @ 254 | ||
| 256 | imap_msg_unset_perm_flags @ 255 | ||
| 257 | input_query_password @ 256 | ||
| 258 | is_ascii_str @ 257 | ||
| 259 | is_dir_exist @ 258 | ||
| 260 | is_file_entry_exist @ 259 | ||
| 261 | is_header_line @ 260 | ||
| 262 | is_next_nonascii @ 261 | ||
| 263 | is_uri_string @ 262 | ||
| 264 | itos @ 263 | ||
| 265 | itos_buf @ 264 | ||
| 266 | list_free_strings @ 265 | ||
| 267 | lock_mbox @ 266 | ||
| 268 | log_error @ 267 | ||
| 269 | log_flush @ 268 | ||
| 270 | log_message @ 269 | ||
| 271 | log_print @ 270 | ||
| 272 | log_warning @ 271 | ||
| 273 | log_write @ 272 | ||
| 274 | make_dir @ 273 | ||
| 275 | make_dir_hier @ 274 | ||
| 276 | md5_hex_hmac @ 275 | ||
| 277 | md5_hmac @ 276 | ||
| 278 | mh_get_class @ 277 | ||
| 279 | move_file @ 278 | ||
| 280 | my_gethostbyname @ 279 | ||
| 281 | my_memmem @ 280 | ||
| 282 | my_strftime @ 281 | ||
| 283 | my_tmpfile @ 282 | ||
| 284 | news_get_class @ 283 | ||
| 285 | news_get_group_list @ 284 | ||
| 286 | news_group_list_free @ 285 | ||
| 287 | news_post @ 286 | ||
| 288 | news_post_stream @ 287 | ||
| 289 | news_remove_group_list_cache @ 288 | ||
| 290 | newsgroup_list_append @ 289 | ||
| 291 | nntp_article @ 290 | ||
| 292 | nntp_body @ 291 | ||
| 293 | nntp_get_article @ 292 | ||
| 294 | nntp_group @ 293 | ||
| 295 | nntp_head @ 294 | ||
| 296 | nntp_list @ 295 | ||
| 297 | nntp_mode @ 296 | ||
| 298 | nntp_newgroups @ 297 | ||
| 299 | nntp_newnews @ 298 | ||
| 300 | nntp_next @ 299 | ||
| 301 | nntp_post @ 300 | ||
| 302 | nntp_session_new @ 301 | ||
| 303 | nntp_stat @ 302 | ||
| 304 | nntp_xhdr @ 303 | ||
| 305 | nntp_xover @ 304 | ||
| 306 | normalize_address_field @ 305 | ||
| 307 | normalize_newlines @ 306 | ||
| 308 | open_uri @ 307 | ||
| 309 | path_cmp @ 308 | ||
| 310 | pop3_delete_recv @ 309 | ||
| 311 | pop3_delete_send @ 310 | ||
| 312 | pop3_gen_send @ 311 | ||
| 313 | pop3_get_uidl_table @ 312 | ||
| 314 | pop3_getauth_apop_send @ 313 | ||
| 315 | pop3_getauth_pass_send @ 314 | ||
| 316 | pop3_getauth_user_send @ 315 | ||
| 317 | pop3_getrange_last_recv @ 316 | ||
| 318 | pop3_getrange_last_send @ 317 | ||
| 319 | pop3_getrange_stat_recv @ 318 | ||
| 320 | pop3_getrange_stat_send @ 319 | ||
| 321 | pop3_getrange_uidl_recv @ 320 | ||
| 322 | pop3_getrange_uidl_send @ 321 | ||
| 323 | pop3_getsize_list_recv @ 322 | ||
| 324 | pop3_getsize_list_send @ 323 | ||
| 325 | pop3_greeting_recv @ 324 | ||
| 326 | pop3_logout_send @ 325 | ||
| 327 | pop3_ok @ 326 | ||
| 328 | pop3_retr_recv @ 327 | ||
| 329 | pop3_retr_send @ 328 | ||
| 330 | pop3_session_new @ 329 | ||
| 331 | pop3_stls_recv @ 330 | ||
| 332 | pop3_stls_send @ 331 | ||
| 333 | pop3_write_msg_to_file @ 332 | ||
| 334 | pop3_write_uidl_list @ 333 | ||
| 335 | prefs_account_apply_tmp_prefs @ 334 | ||
| 336 | prefs_account_free @ 335 | ||
| 337 | prefs_account_get_params @ 336 | ||
| 338 | prefs_account_get_tmp_prefs @ 337 | ||
| 339 | prefs_account_new @ 338 | ||
| 340 | prefs_account_read_config @ 339 | ||
| 341 | prefs_account_set_tmp_prefs @ 340 | ||
| 342 | prefs_account_write_config_all @ 341 | ||
| 343 | prefs_common @ 342 DATA | ||
| 344 | prefs_common_get @ 343 | ||
| 345 | prefs_common_get_params @ 344 | ||
| 346 | prefs_common_junk_filter_list_set @ 345 | ||
| 347 | prefs_common_junk_folder_rename_path @ 346 | ||
| 348 | prefs_common_read_config @ 347 | ||
| 349 | prefs_common_write_config @ 348 | ||
| 350 | prefs_file_close @ 349 | ||
| 351 | prefs_file_close_revert @ 350 | ||
| 352 | prefs_file_get_backup_generation @ 351 | ||
| 353 | prefs_file_open @ 352 | ||
| 354 | prefs_file_set_backup_generation @ 353 | ||
| 355 | prefs_file_write_param @ 354 | ||
| 356 | prefs_free @ 355 | ||
| 357 | prefs_param_table_destroy @ 356 | ||
| 358 | prefs_param_table_get @ 357 | ||
| 359 | prefs_read_config @ 358 | ||
| 360 | prefs_set_default @ 359 | ||
| 361 | prefs_write_config @ 360 | ||
| 362 | proc_mbox @ 361 | ||
| 363 | proc_mbox_full @ 362 | ||
| 364 | procheader_add_header_list @ 363 | ||
| 365 | procheader_copy_header_list @ 364 | ||
| 366 | procheader_date_get_localtime @ 365 | ||
| 367 | procheader_date_parse @ 366 | ||
| 368 | procheader_find_header_list @ 367 | ||
| 369 | procheader_get_fromname @ 368 | ||
| 370 | procheader_get_header_array @ 369 | ||
| 371 | procheader_get_header_array_asis @ 370 | ||
| 372 | procheader_get_header_array_for_display @ 371 | ||
| 373 | procheader_get_header_fields @ 372 | ||
| 374 | procheader_get_header_list @ 373 | ||
| 375 | procheader_get_header_list_from_file @ 374 | ||
| 376 | procheader_get_header_list_from_msginfo @ 375 | ||
| 377 | procheader_get_one_field @ 376 | ||
| 378 | procheader_get_toname @ 377 | ||
| 379 | procheader_get_unfolded_line @ 378 | ||
| 380 | procheader_header_array_destroy @ 379 | ||
| 381 | procheader_header_free @ 380 | ||
| 382 | procheader_header_list_destroy @ 381 | ||
| 383 | procheader_merge_header_list @ 382 | ||
| 384 | procheader_merge_header_list_dup @ 383 | ||
| 385 | procheader_parse_file @ 384 | ||
| 386 | procheader_parse_str @ 385 | ||
| 387 | procheader_parse_stream @ 386 | ||
| 388 | procmime_decode_content @ 387 | ||
| 389 | procmime_execute_open_file @ 388 | ||
| 390 | procmime_find_string @ 389 | ||
| 391 | procmime_find_string_part @ 390 | ||
| 392 | procmime_get_all_parts @ 391 | ||
| 393 | procmime_get_encoding_for_charset @ 392 | ||
| 394 | procmime_get_encoding_for_str @ 393 | ||
| 395 | procmime_get_encoding_for_text_file @ 394 | ||
| 396 | procmime_get_encoding_str @ 395 | ||
| 397 | procmime_get_first_text_content @ 396 | ||
| 398 | procmime_get_mime_type @ 397 | ||
| 399 | procmime_get_part @ 398 | ||
| 400 | procmime_get_part_file_name @ 399 | ||
| 401 | procmime_get_part_fp @ 400 | ||
| 402 | procmime_get_text_content @ 401 | ||
| 403 | procmime_get_tmp_file_name @ 402 | ||
| 404 | procmime_mimeinfo_free_all @ 403 | ||
| 405 | procmime_mimeinfo_insert @ 404 | ||
| 406 | procmime_mimeinfo_new @ 405 | ||
| 407 | procmime_mimeinfo_next @ 406 | ||
| 408 | procmime_scan_content_disposition @ 407 | ||
| 409 | procmime_scan_content_type @ 408 | ||
| 410 | procmime_scan_content_type_str @ 409 | ||
| 411 | procmime_scan_encoding @ 410 | ||
| 412 | procmime_scan_message @ 411 | ||
| 413 | procmime_scan_mime_header @ 412 | ||
| 414 | procmime_scan_mime_type @ 413 | ||
| 415 | procmime_scan_multipart_message @ 414 | ||
| 416 | procmsg_add_cache_queue @ 415 | ||
| 417 | procmsg_add_flags @ 416 | ||
| 418 | procmsg_add_mark_queue @ 417 | ||
| 419 | procmsg_clear_cache @ 418 | ||
| 420 | procmsg_clear_mark @ 419 | ||
| 421 | procmsg_cmp_msgnum_for_sort @ 420 | ||
| 422 | procmsg_copy_messages @ 421 | ||
| 423 | procmsg_empty_all_trash @ 422 | ||
| 424 | procmsg_empty_trash @ 423 | ||
| 425 | procmsg_flush_cache_queue @ 424 | ||
| 426 | procmsg_flush_folder @ 425 | ||
| 427 | procmsg_flush_folder_foreach @ 426 | ||
| 428 | procmsg_flush_mark_queue @ 427 | ||
| 429 | procmsg_get_last_num_in_msg_list @ 428 | ||
| 430 | procmsg_get_mark_sum @ 429 | ||
| 431 | procmsg_get_message_file @ 430 | ||
| 432 | procmsg_get_message_file_list @ 431 | ||
| 433 | procmsg_get_message_file_path @ 432 | ||
| 434 | procmsg_get_msginfo @ 433 | ||
| 435 | procmsg_get_thread_date @ 434 | ||
| 436 | procmsg_get_thread_tree @ 435 | ||
| 437 | procmsg_mark_all_read @ 436 | ||
| 438 | procmsg_message_file_list_free @ 437 | ||
| 439 | procmsg_move_messages @ 438 | ||
| 440 | procmsg_msg_exist @ 439 | ||
| 441 | procmsg_msg_hash_table_append @ 440 | ||
| 442 | procmsg_msg_hash_table_create @ 441 | ||
| 443 | procmsg_msg_list_free @ 442 | ||
| 444 | procmsg_msginfo_copy @ 443 | ||
| 445 | procmsg_msginfo_equal @ 444 | ||
| 446 | procmsg_msginfo_free @ 445 | ||
| 447 | procmsg_msginfo_get_full_info @ 446 | ||
| 448 | procmsg_open_cache_file @ 447 | ||
| 449 | procmsg_open_data_file @ 448 | ||
| 450 | procmsg_open_mark_file @ 449 | ||
| 451 | procmsg_open_message @ 450 | ||
| 452 | procmsg_open_message_decrypted @ 451 | ||
| 453 | procmsg_print_message @ 452 | ||
| 454 | procmsg_print_message_part @ 453 | ||
| 455 | procmsg_read_cache @ 454 | ||
| 456 | procmsg_read_cache_data_str @ 455 | ||
| 457 | procmsg_remove_all_cached_messages @ 456 | ||
| 458 | procmsg_save_to_outbox @ 457 | ||
| 459 | procmsg_set_auto_decrypt_message @ 458 | ||
| 460 | procmsg_set_decrypt_message_func @ 459 | ||
| 461 | procmsg_set_flags @ 460 | ||
| 462 | procmsg_sort_msg_list @ 461 | ||
| 463 | procmsg_to_folder_hash_table_create @ 462 | ||
| 464 | procmsg_trash_messages_exist @ 463 | ||
| 465 | procmsg_write_cache @ 464 | ||
| 466 | procmsg_write_cache_list @ 465 | ||
| 467 | procmsg_write_flags @ 466 | ||
| 468 | procmsg_write_flags_for_multiple_folders @ 467 | ||
| 469 | procmsg_write_flags_list @ 468 | ||
| 470 | progress_show @ 469 | ||
| 471 | ptr_array_free_strings @ 470 | ||
| 472 | qp_decode_line @ 471 | ||
| 473 | qp_decode_q_encoding @ 472 | ||
| 474 | qp_encode_line @ 473 | ||
| 475 | qp_get_q_encoding_len @ 474 | ||
| 476 | qp_q_encode @ 475 | ||
| 477 | recv_bytes @ 476 | ||
| 478 | recv_bytes_write @ 477 | ||
| 479 | recv_bytes_write_to_file @ 478 | ||
| 480 | recv_set_ui_func @ 479 | ||
| 481 | recv_write @ 480 | ||
| 482 | recv_write_to_file @ 481 | ||
| 483 | references_list_append @ 482 | ||
| 484 | references_list_prepend @ 483 | ||
| 485 | remote_tzoffset_sec @ 484 | ||
| 486 | remove_all_files @ 485 | ||
| 487 | remove_all_numbered_files @ 486 | ||
| 488 | remove_dir_recursive @ 487 | ||
| 489 | remove_expired_files @ 488 | ||
| 490 | remove_numbered_files @ 489 | ||
| 491 | remove_return @ 490 | ||
| 492 | remove_space @ 491 | ||
| 493 | rename_force @ 492 | ||
| 494 | s_gnet_md5_clone @ 493 | ||
| 495 | s_gnet_md5_copy_string @ 494 | ||
| 496 | s_gnet_md5_delete @ 495 | ||
| 497 | s_gnet_md5_equal @ 496 | ||
| 498 | s_gnet_md5_final @ 497 | ||
| 499 | s_gnet_md5_get_digest @ 498 | ||
| 500 | s_gnet_md5_get_string @ 499 | ||
| 501 | s_gnet_md5_hash @ 500 | ||
| 502 | s_gnet_md5_new @ 501 | ||
| 503 | s_gnet_md5_new_incremental @ 502 | ||
| 504 | s_gnet_md5_new_string @ 503 | ||
| 505 | s_gnet_md5_update @ 504 | ||
| 506 | scan_mailto_url @ 505 | ||
| 507 | session_connect @ 506 | ||
| 508 | session_destroy @ 507 | ||
| 509 | session_disconnect @ 508 | ||
| 510 | session_init @ 509 | ||
| 511 | session_is_connected @ 510 | ||
| 512 | session_recv_data @ 511 | ||
| 513 | session_recv_data_as_file @ 512 | ||
| 514 | session_recv_msg @ 513 | ||
| 515 | session_send_data @ 514 | ||
| 516 | session_send_msg @ 515 | ||
| 517 | session_set_access_time @ 516 | ||
| 518 | session_set_recv_data_notify @ 517 | ||
| 519 | session_set_recv_data_progressive_notify @ 518 | ||
| 520 | session_set_recv_message_notify @ 519 | ||
| 521 | session_set_send_data_notify @ 520 | ||
| 522 | session_set_send_data_progressive_notify @ 521 | ||
| 523 | session_set_timeout @ 522 | ||
| 524 | session_start_tls @ 523 | ||
| 525 | set_debug_mode @ 524 | ||
| 526 | set_event_loop_func @ 525 | ||
| 527 | set_input_query_password_func @ 526 | ||
| 528 | set_log_file @ 527 | ||
| 529 | set_log_show_status_func @ 528 | ||
| 530 | set_log_ui_func @ 529 | ||
| 531 | set_log_ui_func_full @ 530 | ||
| 532 | set_log_verbosity @ 531 | ||
| 533 | set_progress_func @ 532 | ||
| 534 | set_rc_dir @ 533 | ||
| 535 | set_startup_dir @ 534 | ||
| 536 | set_ui_update_func @ 535 | ||
| 537 | sinfo_equal @ 536 | ||
| 538 | sinfo_hash @ 537 | ||
| 539 | slist_free_strings @ 538 | ||
| 540 | smtp_session_new @ 539 | ||
| 541 | sock_add_watch @ 540 | ||
| 542 | sock_add_watch_poll @ 541 | ||
| 543 | sock_cleanup @ 542 | ||
| 544 | sock_close @ 543 | ||
| 545 | sock_connect @ 544 | ||
| 546 | sock_connect_async_thread @ 545 | ||
| 547 | sock_connect_async_thread_wait @ 546 | ||
| 548 | sock_getline @ 547 | ||
| 549 | sock_gets @ 548 | ||
| 550 | sock_has_read_data @ 549 | ||
| 551 | sock_init @ 550 | ||
| 552 | sock_is_nonblocking_mode @ 551 | ||
| 553 | sock_peek @ 552 | ||
| 554 | sock_printf @ 553 | ||
| 555 | sock_puts @ 554 | ||
| 556 | sock_read @ 555 | ||
| 557 | sock_set_io_timeout @ 556 | ||
| 558 | sock_set_nonblocking_mode @ 557 | ||
| 559 | sock_watch_funcs @ 558 DATA | ||
| 560 | sock_write @ 559 | ||
| 561 | sock_write_all @ 560 | ||
| 562 | ssl_done @ 561 | ||
| 563 | ssl_done_socket @ 562 | ||
| 564 | ssl_getline @ 563 | ||
| 565 | ssl_gets @ 564 | ||
| 566 | ssl_init @ 565 | ||
| 567 | ssl_init_socket @ 566 | ||
| 568 | ssl_init_socket_with_method @ 567 | ||
| 569 | ssl_peek @ 568 | ||
| 570 | ssl_read @ 569 | ||
| 571 | ssl_set_verify_func @ 570 | ||
| 572 | ssl_write @ 571 | ||
| 573 | ssl_write_all @ 572 | ||
| 574 | status_print @ 573 | ||
| 575 | str_case_equal @ 574 | ||
| 576 | str_case_find @ 575 | ||
| 577 | str_case_find_equal @ 576 | ||
| 578 | str_case_hash @ 577 | ||
| 579 | str_find @ 578 | ||
| 580 | str_find_equal @ 579 | ||
| 581 | str_find_format_times @ 580 | ||
| 582 | str_has_suffix_case @ 581 | ||
| 583 | str_open_as_stream @ 582 | ||
| 584 | str_write_to_file @ 583 | ||
| 585 | strcasestr @ 584 | ||
| 586 | strchomp_all @ 585 | ||
| 587 | strchr_parenthesis_close @ 586 | ||
| 588 | strchr_with_skip_quote @ 587 | ||
| 589 | strcmp2 @ 588 | ||
| 590 | strcrchomp @ 589 | ||
| 591 | string_table_free @ 590 | ||
| 592 | string_table_free_string @ 591 | ||
| 593 | string_table_get_stats @ 592 | ||
| 594 | string_table_insert_string @ 593 | ||
| 595 | string_table_lookup_string @ 594 | ||
| 596 | string_table_new @ 595 | ||
| 597 | strncpy2 @ 596 | ||
| 598 | strrchr_with_skip_quote @ 597 | ||
| 599 | strretchomp @ 598 | ||
| 600 | strsplit_csv @ 599 | ||
| 601 | strsplit_parenthesis @ 600 | ||
| 602 | strsplit_with_quote @ 601 | ||
| 603 | strstr_with_skip_quote @ 602 | ||
| 604 | strtailchomp @ 603 | ||
| 605 | subject_compare @ 604 | ||
| 606 | subject_compare_for_sort @ 605 | ||
| 607 | subst_char @ 606 | ||
| 608 | subst_chars @ 607 | ||
| 609 | subst_control @ 608 | ||
| 610 | subst_for_filename @ 609 | ||
| 611 | subst_null @ 610 | ||
| 612 | syl_app_create @ 611 | ||
| 613 | syl_app_get @ 612 | ||
| 614 | syl_app_get_type @ 613 | ||
| 615 | syl_cleanup @ 614 | ||
| 616 | syl_init @ 615 | ||
| 617 | syl_init_gettext @ 616 | ||
| 618 | syl_link @ 617 | ||
| 619 | syl_marshal_VOID__POINTER_STRING_STRING @ 618 | ||
| 620 | syl_marshal_VOID__POINTER_STRING_UINT @ 619 | ||
| 621 | syl_save_all_state @ 620 | ||
| 622 | syl_setup_rc_dir @ 621 | ||
| 623 | to_human_readable @ 622 | ||
| 624 | to_number @ 623 | ||
| 625 | touufrombits @ 624 | ||
| 626 | trim_string @ 625 | ||
| 627 | trim_string_before @ 626 | ||
| 628 | trim_subject @ 627 | ||
| 629 | trim_subject_for_compare @ 628 | ||
| 630 | trim_subject_for_sort @ 629 | ||
| 631 | tzoffset @ 630 | ||
| 632 | tzoffset_buf @ 631 | ||
| 633 | tzoffset_sec @ 632 | ||
| 634 | ui_update @ 633 | ||
| 635 | uncanonicalize_file @ 634 | ||
| 636 | uncanonicalize_file_replace @ 635 | ||
| 637 | unfold_line @ 636 | ||
| 638 | unlock_mbox @ 637 | ||
| 639 | unmime_header @ 638 | ||
| 640 | uri_list_extract_filenames @ 639 | ||
| 641 | uriencode_for_filename @ 640 | ||
| 642 | uriencode_for_mailto @ 641 | ||
| 643 | utos_buf @ 642 | ||
| 644 | uudigit @ 643 DATA | ||
| 645 | virtual_get_class @ 644 | ||
| 646 | xml_attr_new @ 645 | ||
| 647 | xml_close_file @ 646 | ||
| 648 | xml_compare_tag @ 647 | ||
| 649 | xml_copy_attr @ 648 | ||
| 650 | xml_copy_tag @ 649 | ||
| 651 | xml_file_put_escape_str @ 650 | ||
| 652 | xml_file_put_node @ 651 | ||
| 653 | xml_file_put_xml_decl @ 652 | ||
| 654 | xml_free_node @ 653 | ||
| 655 | xml_free_tree @ 654 | ||
| 656 | xml_get_current_tag @ 655 | ||
| 657 | xml_get_current_tag_attr @ 656 | ||
| 658 | xml_get_dtd @ 657 | ||
| 659 | xml_get_element @ 658 | ||
| 660 | xml_node_new @ 659 | ||
| 661 | xml_open_file @ 660 | ||
| 662 | xml_parse_file @ 661 | ||
| 663 | xml_parse_next_tag @ 662 | ||
| 664 | xml_pop_tag @ 663 | ||
| 665 | xml_push_tag @ 664 | ||
| 666 | xml_read_line @ 665 | ||
| 667 | xml_tag_add_attr @ 666 | ||
| 668 | xml_tag_new @ 667 | ||
| 669 | xml_truncate_buf @ 668 | ||
| 670 | xml_unescape_str @ 669 | ||
| 671 | strcasestr_with_skip_quote @ 670 | ||
| 672 | is_path_parent @ 671 | ||
| 673 | extract_addresses @ 672 | ||
| 674 | to_unumber @ 673 | ||
| 675 | imap_msg_list_set_colorlabel_flags @ 674 | ||
| 676 | filter_get_addressbook_func @ 675 | ||
| 677 | filter_set_addressbook_func @ 676 | ||
| 678 | procmsg_flaginfo_list_free @ 677 | ||
| 679 | procmsg_concat_partial_messages @ 678 | ||
| 680 | get_last_empty_line_size @ 679 | ||
| 681 | append_file_part @ 680 | ||
| 682 | procmime_scan_content_type_partial @ 681 | ||
| 683 | folder_get_default_junk @ 682 | ||
| 684 | folder_get_junk @ 683 | ||
| 685 | folder_set_junk @ 684 | ||
| 686 | procmime_get_part_fp_fp @ 685 | ||
| 687 | xml_escape_str @ 686 | ||
| 688 | session_connect_full @ 687 | ||
| 689 | socks_info_new @ 688 | ||
| 690 | socks_info_free @ 689 | ||
| 691 | socks_connect @ 690 | ||
| 692 | socks4_connect @ 691 | ||
| 693 | socks5_connect @ 692 | ||
| 694 | folder_remote_folder_destroy_all_sessions @ 693 | ||
| 695 | filter_junk_rule_create @ 694 | ||
| 696 | folder_remote_folder_active_session_exist @ 695 | ||
| 697 | procmsg_add_messages_from_queue @ 696 | ||
| 698 | to_human_readable_buf @ 697 | ||
| 699 | folder_item_is_trash @ 698 | ||
| 700 | play_sound @ 699 | ||
| 701 | session_get_error @ 700 | ||
| 702 | sock_new @ 701 | ||
| 703 | sock_info_connect @ 702 | ||
| 704 | sock_info_connect_async_thread @ 703 | ||
| 705 | sock_info_connect_async_thread_wait @ 704 | ||
| 706 | folder_set_ui_func2 @ 705 | ||
| 707 | folder_get_ui_func2 @ 706 | ||
| 708 | folder_call_ui_func2 @ 707 | ||
| 709 | export_msgs_to_mbox @ 708 | ||
| 710 | copy_file_stream @ 709 | ||
| 711 | procmsg_save_message_as_text @ 710 | ||
| 712 | procmime_get_tmp_file_name_for_user @ 711 | ||
| 713 | procmime_scan_message_stream @ 712 | ||
| 714 | strconcat_csv @ 713 | ||
diff --git a/libsylph/mbox.c b/libsylph/mbox.c new file mode 100644 index 0000000..4c0b2da --- /dev/null +++ b/libsylph/mbox.c | |||
| @@ -0,0 +1,572 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <string.h> | ||
| 31 | #include <fcntl.h> | ||
| 32 | #include <sys/file.h> | ||
| 33 | #include <ctype.h> | ||
| 34 | #include <time.h> | ||
| 35 | |||
| 36 | #ifdef HAVE_LOCKFILE_H | ||
| 37 | # include <lockfile.h> | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #include "mbox.h" | ||
| 41 | #include "procmsg.h" | ||
| 42 | #include "procheader.h" | ||
| 43 | #include "folder.h" | ||
| 44 | #include "filter.h" | ||
| 45 | #include "prefs_common.h" | ||
| 46 | #include "prefs_account.h" | ||
| 47 | #include "account.h" | ||
| 48 | #include "utils.h" | ||
| 49 | |||
| 50 | #define FPUTS_TO_TMP_ABORT_IF_FAIL(s) \ | ||
| 51 | { \ | ||
| 52 | if (fputs(s, tmp_fp) == EOF) { \ | ||
| 53 | g_warning(_("can't write to temporary file\n")); \ | ||
| 54 | fclose(tmp_fp); \ | ||
| 55 | fclose(mbox_fp); \ | ||
| 56 | g_unlink(tmp_file); \ | ||
| 57 | g_free(tmp_file); \ | ||
| 58 | return -1; \ | ||
| 59 | } \ | ||
| 60 | } | ||
| 61 | |||
| 62 | gint proc_mbox(FolderItem *dest, const gchar *mbox, GHashTable *folder_table) | ||
| 63 | { | ||
| 64 | return proc_mbox_full(dest, mbox, folder_table, | ||
| 65 | folder_table ? TRUE : FALSE, | ||
| 66 | folder_table && prefs_common.enable_junk && | ||
| 67 | prefs_common.filter_junk_on_recv ? TRUE : FALSE); | ||
| 68 | } | ||
| 69 | |||
| 70 | gint proc_mbox_full(FolderItem *dest, const gchar *mbox, | ||
| 71 | GHashTable *folder_table, gboolean apply_filter, | ||
| 72 | gboolean filter_junk) | ||
| 73 | { | ||
| 74 | FILE *mbox_fp; | ||
| 75 | gchar buf[BUFFSIZE], from_line[BUFFSIZE]; | ||
| 76 | gchar *tmp_file; | ||
| 77 | gint new_msgs = 0; | ||
| 78 | guint count = 0; | ||
| 79 | Folder *folder; | ||
| 80 | FilterRule *junk_rule = NULL; | ||
| 81 | GSList junk_fltlist = {NULL, NULL}; | ||
| 82 | FolderItem *junk; | ||
| 83 | |||
| 84 | g_return_val_if_fail(dest != NULL, -1); | ||
| 85 | g_return_val_if_fail(dest->folder != NULL, -1); | ||
| 86 | g_return_val_if_fail(mbox != NULL, -1); | ||
| 87 | |||
| 88 | debug_print(_("Getting messages from %s into %s...\n"), mbox, dest->path); | ||
| 89 | |||
| 90 | folder = dest->folder; | ||
| 91 | |||
| 92 | if ((mbox_fp = g_fopen(mbox, "rb")) == NULL) { | ||
| 93 | FILE_OP_ERROR(mbox, "fopen"); | ||
| 94 | return -1; | ||
| 95 | } | ||
| 96 | |||
| 97 | /* ignore empty lines on the head */ | ||
| 98 | do { | ||
| 99 | if (fgets(buf, sizeof(buf), mbox_fp) == NULL) { | ||
| 100 | g_warning(_("can't read mbox file.\n")); | ||
| 101 | fclose(mbox_fp); | ||
| 102 | return -1; | ||
| 103 | } | ||
| 104 | } while (buf[0] == '\n' || buf[0] == '\r'); | ||
| 105 | |||
| 106 | if (strncmp(buf, "From ", 5) != 0) { | ||
| 107 | g_warning(_("invalid mbox format: %s\n"), mbox); | ||
| 108 | fclose(mbox_fp); | ||
| 109 | return -1; | ||
| 110 | } | ||
| 111 | |||
| 112 | strcpy(from_line, buf); | ||
| 113 | if (fgets(buf, sizeof(buf), mbox_fp) == NULL) { | ||
| 114 | g_warning(_("malformed mbox: %s\n"), mbox); | ||
| 115 | fclose(mbox_fp); | ||
| 116 | return -1; | ||
| 117 | } | ||
| 118 | |||
| 119 | tmp_file = get_tmp_file(); | ||
| 120 | |||
| 121 | if (filter_junk) { | ||
| 122 | junk = folder_get_junk(folder); | ||
| 123 | junk_rule = filter_junk_rule_create(NULL, junk, FALSE); | ||
| 124 | junk_fltlist.data = junk_rule; | ||
| 125 | } | ||
| 126 | |||
| 127 | do { | ||
| 128 | FILE *tmp_fp; | ||
| 129 | GSList *cur; | ||
| 130 | gchar *startp, *endp, *rpath; | ||
| 131 | gint empty_line; | ||
| 132 | gboolean is_next_msg = FALSE; | ||
| 133 | gboolean is_junk = FALSE; | ||
| 134 | FilterInfo *fltinfo; | ||
| 135 | MsgInfo *msginfo; | ||
| 136 | |||
| 137 | count++; | ||
| 138 | if (folder->ui_func) | ||
| 139 | folder->ui_func(folder, dest, folder->ui_func_data ? dest->folder->ui_func_data : GUINT_TO_POINTER(count)); | ||
| 140 | if (folder_call_ui_func2(folder, dest, count, 0) == FALSE) { | ||
| 141 | debug_print("Import of mbox cancelled at %u\n", count); | ||
| 142 | break; | ||
| 143 | } | ||
| 144 | |||
| 145 | if ((tmp_fp = g_fopen(tmp_file, "wb")) == NULL) { | ||
| 146 | FILE_OP_ERROR(tmp_file, "fopen"); | ||
| 147 | g_warning(_("can't open temporary file\n")); | ||
| 148 | filter_rule_free(junk_rule); | ||
| 149 | g_free(tmp_file); | ||
| 150 | fclose(mbox_fp); | ||
| 151 | return -1; | ||
| 152 | } | ||
| 153 | if (change_file_mode_rw(tmp_fp, tmp_file) < 0) | ||
| 154 | FILE_OP_ERROR(tmp_file, "chmod"); | ||
| 155 | |||
| 156 | /* convert unix From into Return-Path */ | ||
| 157 | startp = from_line + 5; | ||
| 158 | endp = strchr(startp, ' '); | ||
| 159 | if (endp == NULL) | ||
| 160 | rpath = g_strdup(startp); | ||
| 161 | else | ||
| 162 | rpath = g_strndup(startp, endp - startp); | ||
| 163 | g_strstrip(rpath); | ||
| 164 | g_snprintf(from_line, sizeof(from_line), | ||
| 165 | "Return-Path: %s\n", rpath); | ||
| 166 | g_free(rpath); | ||
| 167 | |||
| 168 | FPUTS_TO_TMP_ABORT_IF_FAIL(from_line); | ||
| 169 | FPUTS_TO_TMP_ABORT_IF_FAIL(buf); | ||
| 170 | from_line[0] = '\0'; | ||
| 171 | |||
| 172 | empty_line = 0; | ||
| 173 | |||
| 174 | while (fgets(buf, sizeof(buf), mbox_fp) != NULL) { | ||
| 175 | if (buf[0] == '\n' || buf[0] == '\r') { | ||
| 176 | empty_line++; | ||
| 177 | buf[0] = '\0'; | ||
| 178 | continue; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* From separator */ | ||
| 182 | while (!strncmp(buf, "From ", 5)) { | ||
| 183 | strcpy(from_line, buf); | ||
| 184 | if (fgets(buf, sizeof(buf), mbox_fp) == NULL) { | ||
| 185 | buf[0] = '\0'; | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | |||
| 189 | if (is_header_line(buf)) { | ||
| 190 | is_next_msg = TRUE; | ||
| 191 | break; | ||
| 192 | } else if (!strncmp(buf, "From ", 5)) { | ||
| 193 | continue; | ||
| 194 | } else if (!strncmp(buf, ">From ", 6)) { | ||
| 195 | g_memmove(buf, buf + 1, strlen(buf)); | ||
| 196 | is_next_msg = TRUE; | ||
| 197 | break; | ||
| 198 | } else { | ||
| 199 | g_warning(_("unescaped From found:\n%s"), | ||
| 200 | from_line); | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | if (is_next_msg) break; | ||
| 205 | |||
| 206 | if (empty_line > 0) { | ||
| 207 | while (empty_line--) | ||
| 208 | FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); | ||
| 209 | empty_line = 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | if (from_line[0] != '\0') { | ||
| 213 | FPUTS_TO_TMP_ABORT_IF_FAIL(from_line); | ||
| 214 | from_line[0] = '\0'; | ||
| 215 | } | ||
| 216 | |||
| 217 | if (buf[0] != '\0') { | ||
| 218 | if (!strncmp(buf, ">From ", 6)) { | ||
| 219 | FPUTS_TO_TMP_ABORT_IF_FAIL(buf + 1); | ||
| 220 | } else | ||
| 221 | FPUTS_TO_TMP_ABORT_IF_FAIL(buf); | ||
| 222 | |||
| 223 | buf[0] = '\0'; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | if (empty_line > 0) { | ||
| 228 | while (--empty_line) | ||
| 229 | FPUTS_TO_TMP_ABORT_IF_FAIL("\n"); | ||
| 230 | } | ||
| 231 | |||
| 232 | if (fclose(tmp_fp) == EOF) { | ||
| 233 | FILE_OP_ERROR(tmp_file, "fclose"); | ||
| 234 | g_warning(_("can't write to temporary file\n")); | ||
| 235 | filter_rule_free(junk_rule); | ||
| 236 | g_unlink(tmp_file); | ||
| 237 | g_free(tmp_file); | ||
| 238 | fclose(mbox_fp); | ||
| 239 | return -1; | ||
| 240 | } | ||
| 241 | |||
| 242 | fltinfo = filter_info_new(); | ||
| 243 | fltinfo->flags.perm_flags = MSG_NEW|MSG_UNREAD; | ||
| 244 | fltinfo->flags.tmp_flags = MSG_RECEIVED; | ||
| 245 | |||
| 246 | msginfo = procheader_parse_file(tmp_file, fltinfo->flags, | ||
| 247 | FALSE); | ||
| 248 | if (!msginfo) { | ||
| 249 | g_warning("proc_mbox_full: procheader_parse_file failed"); | ||
| 250 | filter_info_free(fltinfo); | ||
| 251 | filter_rule_free(junk_rule); | ||
| 252 | g_unlink(tmp_file); | ||
| 253 | g_free(tmp_file); | ||
| 254 | fclose(mbox_fp); | ||
| 255 | return -1; | ||
| 256 | } | ||
| 257 | fltinfo->flags = msginfo->flags; | ||
| 258 | msginfo->file_path = g_strdup(tmp_file); | ||
| 259 | |||
| 260 | if (filter_junk && prefs_common.enable_junk && | ||
| 261 | prefs_common.filter_junk_before && junk_rule) { | ||
| 262 | filter_apply_msginfo(&junk_fltlist, msginfo, fltinfo); | ||
| 263 | if (fltinfo->drop_done) | ||
| 264 | is_junk = TRUE; | ||
| 265 | } | ||
| 266 | |||
| 267 | if (!fltinfo->drop_done && apply_filter) | ||
| 268 | filter_apply_msginfo(prefs_common.fltlist, msginfo, | ||
| 269 | fltinfo); | ||
| 270 | |||
| 271 | if (!fltinfo->drop_done && | ||
| 272 | filter_junk && prefs_common.enable_junk && | ||
| 273 | !prefs_common.filter_junk_before && junk_rule) { | ||
| 274 | filter_apply_msginfo(&junk_fltlist, msginfo, fltinfo); | ||
| 275 | if (fltinfo->drop_done) | ||
| 276 | is_junk = TRUE; | ||
| 277 | } | ||
| 278 | |||
| 279 | if (fltinfo->actions[FLT_ACTION_MOVE] == FALSE && | ||
| 280 | fltinfo->actions[FLT_ACTION_DELETE] == FALSE) { | ||
| 281 | msginfo->flags = fltinfo->flags; | ||
| 282 | if (folder_item_add_msg_msginfo(dest, msginfo, FALSE) < 0) { | ||
| 283 | procmsg_msginfo_free(msginfo); | ||
| 284 | filter_info_free(fltinfo); | ||
| 285 | filter_rule_free(junk_rule); | ||
| 286 | g_unlink(tmp_file); | ||
| 287 | g_free(tmp_file); | ||
| 288 | fclose(mbox_fp); | ||
| 289 | return -1; | ||
| 290 | } | ||
| 291 | fltinfo->dest_list = g_slist_append(fltinfo->dest_list, | ||
| 292 | dest); | ||
| 293 | } | ||
| 294 | |||
| 295 | for (cur = fltinfo->dest_list; cur != NULL; cur = cur->next) { | ||
| 296 | FolderItem *drop_folder = (FolderItem *)cur->data; | ||
| 297 | gint val = 0; | ||
| 298 | |||
| 299 | if (folder_table) { | ||
| 300 | val = GPOINTER_TO_INT(g_hash_table_lookup | ||
| 301 | (folder_table, | ||
| 302 | drop_folder)); | ||
| 303 | } | ||
| 304 | if (val == 0) { | ||
| 305 | if (folder_table) { | ||
| 306 | g_hash_table_insert(folder_table, | ||
| 307 | drop_folder, | ||
| 308 | GINT_TO_POINTER(1)); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | if (!is_junk && | ||
| 314 | fltinfo->actions[FLT_ACTION_DELETE] == FALSE && | ||
| 315 | fltinfo->actions[FLT_ACTION_MARK_READ] == FALSE) | ||
| 316 | new_msgs++; | ||
| 317 | |||
| 318 | procmsg_msginfo_free(msginfo); | ||
| 319 | filter_info_free(fltinfo); | ||
| 320 | g_unlink(tmp_file); | ||
| 321 | } while (from_line[0] != '\0'); | ||
| 322 | |||
| 323 | if (junk_rule) | ||
| 324 | filter_rule_free(junk_rule); | ||
| 325 | |||
| 326 | g_free(tmp_file); | ||
| 327 | fclose(mbox_fp); | ||
| 328 | debug_print("%d new messages found.\n", new_msgs); | ||
| 329 | |||
| 330 | return new_msgs; | ||
| 331 | } | ||
| 332 | |||
| 333 | gint lock_mbox(const gchar *base, LockType type) | ||
| 334 | { | ||
| 335 | #ifdef G_OS_UNIX | ||
| 336 | gint retval = 0; | ||
| 337 | |||
| 338 | if (type == LOCK_FILE) { | ||
| 339 | #if HAVE_LIBLOCKFILE | ||
| 340 | gchar *lockfile; | ||
| 341 | |||
| 342 | lockfile = g_strconcat(base, ".lock", NULL); | ||
| 343 | if (lockfile_create(lockfile, 0, L_PID) != L_SUCCESS) { | ||
| 344 | FILE_OP_ERROR(lockfile, "lockfile_create"); | ||
| 345 | g_free(lockfile); | ||
| 346 | return -1; | ||
| 347 | } | ||
| 348 | g_free(lockfile); | ||
| 349 | #else | ||
| 350 | gchar *lockfile, *locklink; | ||
| 351 | gint retry = 0; | ||
| 352 | FILE *lockfp; | ||
| 353 | |||
| 354 | lockfile = g_strdup_printf("%s.%d", base, getpid()); | ||
| 355 | if ((lockfp = g_fopen(lockfile, "wb")) == NULL) { | ||
| 356 | FILE_OP_ERROR(lockfile, "fopen"); | ||
| 357 | g_warning(_("can't create lock file %s\n"), lockfile); | ||
| 358 | g_warning(_("use 'flock' instead of 'file' if possible.\n")); | ||
| 359 | g_free(lockfile); | ||
| 360 | return -1; | ||
| 361 | } | ||
| 362 | |||
| 363 | fprintf(lockfp, "%d\n", getpid()); | ||
| 364 | fclose(lockfp); | ||
| 365 | |||
| 366 | locklink = g_strconcat(base, ".lock", NULL); | ||
| 367 | while (link(lockfile, locklink) < 0) { | ||
| 368 | FILE_OP_ERROR(lockfile, "link"); | ||
| 369 | if (retry >= 5) { | ||
| 370 | g_warning(_("can't create %s\n"), lockfile); | ||
| 371 | g_unlink(lockfile); | ||
| 372 | g_free(lockfile); | ||
| 373 | return -1; | ||
| 374 | } | ||
| 375 | if (retry == 0) | ||
| 376 | g_warning(_("mailbox is owned by another" | ||
| 377 | " process, waiting...\n")); | ||
| 378 | retry++; | ||
| 379 | sleep(5); | ||
| 380 | } | ||
| 381 | g_unlink(lockfile); | ||
| 382 | g_free(lockfile); | ||
| 383 | #endif /* HAVE_LIBLOCKFILE */ | ||
| 384 | } else if (type == LOCK_FLOCK) { | ||
| 385 | gint lockfd; | ||
| 386 | |||
| 387 | if ((lockfd = open(base, O_RDWR)) < 0) { | ||
| 388 | FILE_OP_ERROR(base, "open"); | ||
| 389 | return -1; | ||
| 390 | } | ||
| 391 | #if HAVE_LOCKF | ||
| 392 | if (lockf(lockfd, F_TLOCK, 0) < 0) { | ||
| 393 | perror("lockf"); | ||
| 394 | #elif HAVE_FLOCK | ||
| 395 | if (flock(lockfd, LOCK_EX|LOCK_NB) < 0) { | ||
| 396 | perror("flock"); | ||
| 397 | #else | ||
| 398 | { | ||
| 399 | #endif /* HAVE_LOCKF */ | ||
| 400 | g_warning(_("can't lock %s\n"), base); | ||
| 401 | if (close(lockfd) < 0) | ||
| 402 | perror("close"); | ||
| 403 | return -1; | ||
| 404 | } | ||
| 405 | retval = lockfd; | ||
| 406 | } else { | ||
| 407 | g_warning(_("invalid lock type\n")); | ||
| 408 | return -1; | ||
| 409 | } | ||
| 410 | |||
| 411 | return retval; | ||
| 412 | #else | ||
| 413 | return -1; | ||
| 414 | #endif /* G_OS_UNIX */ | ||
| 415 | } | ||
| 416 | |||
| 417 | gint unlock_mbox(const gchar *base, gint fd, LockType type) | ||
| 418 | { | ||
| 419 | if (type == LOCK_FILE) { | ||
| 420 | gchar *lockfile; | ||
| 421 | |||
| 422 | lockfile = g_strconcat(base, ".lock", NULL); | ||
| 423 | #if HAVE_LIBLOCKFILE | ||
| 424 | if (lockfile_remove(lockfile) != L_SUCCESS) { | ||
| 425 | FILE_OP_ERROR(lockfile, "lockfile_remove"); | ||
| 426 | #else | ||
| 427 | if (g_unlink(lockfile) < 0) { | ||
| 428 | FILE_OP_ERROR(lockfile, "unlink"); | ||
| 429 | #endif /* HAVE_LIBLOCKFILE */ | ||
| 430 | g_free(lockfile); | ||
| 431 | return -1; | ||
| 432 | } | ||
| 433 | g_free(lockfile); | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } else if (type == LOCK_FLOCK) { | ||
| 437 | #if HAVE_LOCKF | ||
| 438 | if (lockf(fd, F_ULOCK, 0) < 0) { | ||
| 439 | perror("lockf"); | ||
| 440 | #elif HAVE_FLOCK | ||
| 441 | if (flock(fd, LOCK_UN) < 0) { | ||
| 442 | perror("flock"); | ||
| 443 | #else | ||
| 444 | { | ||
| 445 | #endif /* HAVE_LOCKF */ | ||
| 446 | g_warning(_("can't unlock %s\n"), base); | ||
| 447 | if (close(fd) < 0) | ||
| 448 | perror("close"); | ||
| 449 | return -1; | ||
| 450 | } | ||
| 451 | |||
| 452 | if (close(fd) < 0) { | ||
| 453 | perror("close"); | ||
| 454 | return -1; | ||
| 455 | } | ||
| 456 | |||
| 457 | return 0; | ||
| 458 | } | ||
| 459 | |||
| 460 | g_warning(_("invalid lock type\n")); | ||
| 461 | return -1; | ||
| 462 | } | ||
| 463 | |||
| 464 | gint copy_mbox(const gchar *src, const gchar *dest) | ||
| 465 | { | ||
| 466 | return copy_file(src, dest, TRUE); | ||
| 467 | } | ||
| 468 | |||
| 469 | void empty_mbox(const gchar *mbox) | ||
| 470 | { | ||
| 471 | #if HAVE_TRUNCATE | ||
| 472 | if (truncate(mbox, 0) < 0) { | ||
| 473 | #endif | ||
| 474 | FILE *fp; | ||
| 475 | |||
| 476 | #if HAVE_TRUNCATE | ||
| 477 | FILE_OP_ERROR(mbox, "truncate"); | ||
| 478 | #endif | ||
| 479 | if ((fp = g_fopen(mbox, "wb")) == NULL) { | ||
| 480 | FILE_OP_ERROR(mbox, "fopen"); | ||
| 481 | g_warning(_("can't truncate mailbox to zero.\n")); | ||
| 482 | return; | ||
| 483 | } | ||
| 484 | fclose(fp); | ||
| 485 | #if HAVE_TRUNCATE | ||
| 486 | } | ||
| 487 | #endif | ||
| 488 | } | ||
| 489 | |||
| 490 | /* read all messages in SRC, and store them into one MBOX file. */ | ||
| 491 | gint export_to_mbox(FolderItem *src, const gchar *mbox) | ||
| 492 | { | ||
| 493 | GSList *mlist; | ||
| 494 | gint ret = 0; | ||
| 495 | |||
| 496 | mlist = folder_item_get_msg_list(src, TRUE); | ||
| 497 | if (mlist) { | ||
| 498 | ret = export_msgs_to_mbox(src, mlist, mbox); | ||
| 499 | procmsg_msg_list_free(mlist); | ||
| 500 | } | ||
| 501 | |||
| 502 | return ret; | ||
| 503 | } | ||
| 504 | |||
| 505 | /* store MLIST into one MBOX file. */ | ||
| 506 | gint export_msgs_to_mbox(FolderItem *src, GSList *mlist, const gchar *mbox) | ||
| 507 | { | ||
| 508 | GSList *cur; | ||
| 509 | MsgInfo *msginfo; | ||
| 510 | FILE *msg_fp; | ||
| 511 | FILE *mbox_fp; | ||
| 512 | gchar buf[BUFFSIZE]; | ||
| 513 | PrefsAccount *cur_ac; | ||
| 514 | guint count = 0, length; | ||
| 515 | time_t date_t_; | ||
| 516 | |||
| 517 | g_return_val_if_fail(src != NULL, -1); | ||
| 518 | g_return_val_if_fail(src->folder != NULL, -1); | ||
| 519 | g_return_val_if_fail(mlist != NULL, -1); | ||
| 520 | g_return_val_if_fail(mbox != NULL, -1); | ||
| 521 | |||
| 522 | debug_print(_("Exporting messages from %s into %s...\n"), | ||
| 523 | src->path, mbox); | ||
| 524 | |||
| 525 | if ((mbox_fp = g_fopen(mbox, "wb")) == NULL) { | ||
| 526 | FILE_OP_ERROR(mbox, "fopen"); | ||
| 527 | return -1; | ||
| 528 | } | ||
| 529 | |||
| 530 | cur_ac = account_get_current_account(); | ||
| 531 | |||
| 532 | length = g_slist_length(mlist); | ||
| 533 | |||
| 534 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 535 | msginfo = (MsgInfo *)cur->data; | ||
| 536 | |||
| 537 | count++; | ||
| 538 | if (src->folder->ui_func) | ||
| 539 | src->folder->ui_func(src->folder, src, src->folder->ui_func_data ? src->folder->ui_func_data : GUINT_TO_POINTER(count)); | ||
| 540 | if (folder_call_ui_func2(src->folder, src, count, length) == FALSE) { | ||
| 541 | debug_print("Export to mbox cancelled at %u/%u\n", count, length); | ||
| 542 | break; | ||
| 543 | } | ||
| 544 | |||
| 545 | msg_fp = procmsg_open_message(msginfo); | ||
| 546 | if (!msg_fp) | ||
| 547 | continue; | ||
| 548 | |||
| 549 | strncpy2(buf, | ||
| 550 | msginfo->from ? msginfo->from : | ||
| 551 | cur_ac && cur_ac->address ? | ||
| 552 | cur_ac->address : "unknown", | ||
| 553 | sizeof(buf)); | ||
| 554 | extract_address(buf); | ||
| 555 | |||
| 556 | date_t_ = msginfo->date_t; | ||
| 557 | fprintf(mbox_fp, "From %s %s", buf, ctime(&date_t_)); | ||
| 558 | |||
| 559 | while (fgets(buf, sizeof(buf), msg_fp) != NULL) { | ||
| 560 | if (!strncmp(buf, "From ", 5)) | ||
| 561 | fputc('>', mbox_fp); | ||
| 562 | fputs(buf, mbox_fp); | ||
| 563 | } | ||
| 564 | fputc('\n', mbox_fp); | ||
| 565 | |||
| 566 | fclose(msg_fp); | ||
| 567 | } | ||
| 568 | |||
| 569 | fclose(mbox_fp); | ||
| 570 | |||
| 571 | return 0; | ||
| 572 | } | ||
diff --git a/libsylph/mbox.h b/libsylph/mbox.h new file mode 100644 index 0000000..22300dd --- /dev/null +++ b/libsylph/mbox.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2007 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MBOX_H__ | ||
| 21 | #define __MBOX_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "folder.h" | ||
| 26 | |||
| 27 | typedef enum { | ||
| 28 | LOCK_FILE, | ||
| 29 | LOCK_FLOCK | ||
| 30 | } LockType; | ||
| 31 | |||
| 32 | gint proc_mbox (FolderItem *dest, | ||
| 33 | const gchar *mbox, | ||
| 34 | GHashTable *folder_table); | ||
| 35 | gint proc_mbox_full (FolderItem *dest, | ||
| 36 | const gchar *mbox, | ||
| 37 | GHashTable *folder_table, | ||
| 38 | gboolean apply_filter, | ||
| 39 | gboolean filter_junk); | ||
| 40 | |||
| 41 | gint lock_mbox (const gchar *base, | ||
| 42 | LockType type); | ||
| 43 | gint unlock_mbox (const gchar *base, | ||
| 44 | gint fd, | ||
| 45 | LockType type); | ||
| 46 | gint copy_mbox (const gchar *src, | ||
| 47 | const gchar *dest); | ||
| 48 | void empty_mbox (const gchar *mbox); | ||
| 49 | |||
| 50 | gint export_to_mbox (FolderItem *src, | ||
| 51 | const gchar *mbox); | ||
| 52 | gint export_msgs_to_mbox(FolderItem *src, | ||
| 53 | GSList *mlist, | ||
| 54 | const gchar *mbox); | ||
| 55 | |||
| 56 | #endif /* __MBOX_H__ */ | ||
diff --git a/libsylph/md5.c b/libsylph/md5.c new file mode 100644 index 0000000..a047471 --- /dev/null +++ b/libsylph/md5.c | |||
| @@ -0,0 +1,637 @@ | |||
| 1 | /* | ||
| 2 | |||
| 3 | GNet API added by David Helder <dhelder@umich.edu> 2000-6-11. All | ||
| 4 | additions and changes placed in the public domain. | ||
| 5 | |||
| 6 | Files originally from: http://www.gxsnmp.org/CVS/gxsnmp/ | ||
| 7 | |||
| 8 | Modified the prefix of functions to prevent conflict with original GNet. | ||
| 9 | |||
| 10 | */ | ||
| 11 | /* | ||
| 12 | * This code implements the MD5 message-digest algorithm. | ||
| 13 | * The algorithm is due to Ron Rivest. This code was | ||
| 14 | * written by Colin Plumb in 1993, no copyright is claimed. | ||
| 15 | * This code is in the public domain; do with it what you wish. | ||
| 16 | * | ||
| 17 | * Equivalent code is available from RSA Data Security, Inc. | ||
| 18 | * This code has been tested against that, and is equivalent, | ||
| 19 | * except that you don't need to include two pages of legalese | ||
| 20 | * with every copy. | ||
| 21 | * | ||
| 22 | * To compute the message digest of a chunk of bytes, declare an | ||
| 23 | * MD5Context structure, pass it to MD5Init, call MD5Update as | ||
| 24 | * needed on buffers full of bytes, and then call MD5Final, which | ||
| 25 | * will fill a supplied 16-byte array with the digest. | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include "md5.h" | ||
| 29 | #include <glib.h> | ||
| 30 | #include <string.h> | ||
| 31 | |||
| 32 | |||
| 33 | /* ************************************************************ */ | ||
| 34 | /* Code below is from Colin Plumb implementation */ | ||
| 35 | |||
| 36 | |||
| 37 | |||
| 38 | struct MD5Context { | ||
| 39 | guint32 buf[4]; | ||
| 40 | guint32 bits[2]; | ||
| 41 | guchar in[64]; | ||
| 42 | int doByteReverse; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static void MD5Init(struct MD5Context *context); | ||
| 46 | static void MD5Update(struct MD5Context *context, guchar const *buf, | ||
| 47 | guint len); | ||
| 48 | static void MD5Final(guchar digest[16], struct MD5Context *context); | ||
| 49 | static void MD5Transform(guint32 buf[4], guint32 const in[16]); | ||
| 50 | |||
| 51 | /* | ||
| 52 | * This is needed to make RSAREF happy on some MS-DOS compilers. | ||
| 53 | */ | ||
| 54 | typedef struct MD5Context MD5_CTX; | ||
| 55 | |||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | static void byteReverse(guint8 *buf, guint longs); | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Note: this code is harmless on little-endian machines. | ||
| 63 | */ | ||
| 64 | void | ||
| 65 | byteReverse(guint8 *buf, guint longs) | ||
| 66 | { | ||
| 67 | guint32 t; | ||
| 68 | do | ||
| 69 | { | ||
| 70 | t = (guint32) ((guint) buf[3] << 8 | buf[2]) << 16 | | ||
| 71 | ((guint) buf[1] << 8 | buf[0]); | ||
| 72 | *(guint32 *) buf = t; | ||
| 73 | buf += 4; | ||
| 74 | } | ||
| 75 | while (--longs); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* | ||
| 79 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious | ||
| 80 | * initialization constants. | ||
| 81 | */ | ||
| 82 | void | ||
| 83 | MD5Init(struct MD5Context *ctx) | ||
| 84 | { | ||
| 85 | ctx->buf[0] = 0x67452301; | ||
| 86 | ctx->buf[1] = 0xefcdab89; | ||
| 87 | ctx->buf[2] = 0x98badcfe; | ||
| 88 | ctx->buf[3] = 0x10325476; | ||
| 89 | |||
| 90 | ctx->bits[0] = 0; | ||
| 91 | ctx->bits[1] = 0; | ||
| 92 | |||
| 93 | #if (G_BYTE_ORDER == G_BIG_ENDIAN) | ||
| 94 | ctx->doByteReverse = 1; | ||
| 95 | #else | ||
| 96 | ctx->doByteReverse = 0; | ||
| 97 | #endif | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * Update context to reflect the concatenation of another buffer full | ||
| 102 | * of bytes. | ||
| 103 | */ | ||
| 104 | void | ||
| 105 | MD5Update(struct MD5Context *ctx, guint8 const *buf, guint len) | ||
| 106 | { | ||
| 107 | guint32 t; | ||
| 108 | |||
| 109 | /* Update bitcount */ | ||
| 110 | |||
| 111 | t = ctx->bits[0]; | ||
| 112 | if ((ctx->bits[0] = t + ((guint32) len << 3)) < t) | ||
| 113 | ctx->bits[1]++; /* Carry from low to high */ | ||
| 114 | ctx->bits[1] += len >> 29; | ||
| 115 | |||
| 116 | t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ | ||
| 117 | |||
| 118 | /* Handle any leading odd-sized chunks */ | ||
| 119 | |||
| 120 | if (t) | ||
| 121 | { | ||
| 122 | guint8 *p = (guint8 *) ctx->in + t; | ||
| 123 | |||
| 124 | t = 64 - t; | ||
| 125 | if (len < t) | ||
| 126 | { | ||
| 127 | g_memmove(p, buf, len); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | g_memmove(p, buf, t); | ||
| 131 | if (ctx->doByteReverse) | ||
| 132 | byteReverse(ctx->in, 16); | ||
| 133 | MD5Transform(ctx->buf, (guint32 *) ctx->in); | ||
| 134 | buf += t; | ||
| 135 | len -= t; | ||
| 136 | } | ||
| 137 | /* Process data in 64-byte chunks */ | ||
| 138 | |||
| 139 | while (len >= 64) | ||
| 140 | { | ||
| 141 | g_memmove(ctx->in, buf, 64); | ||
| 142 | if (ctx->doByteReverse) | ||
| 143 | byteReverse(ctx->in, 16); | ||
| 144 | MD5Transform(ctx->buf, (guint32 *) ctx->in); | ||
| 145 | buf += 64; | ||
| 146 | len -= 64; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Handle any remaining bytes of data. */ | ||
| 150 | |||
| 151 | g_memmove(ctx->in, buf, len); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* | ||
| 155 | * Final wrapup - pad to 64-byte boundary with the bit pattern | ||
| 156 | * 1 0* (64-bit count of bits processed, MSB-first) | ||
| 157 | */ | ||
| 158 | void | ||
| 159 | MD5Final(guint8 digest[16], struct MD5Context *ctx) | ||
| 160 | { | ||
| 161 | guint count; | ||
| 162 | guint8 *p; | ||
| 163 | |||
| 164 | /* Compute number of bytes mod 64 */ | ||
| 165 | count = (ctx->bits[0] >> 3) & 0x3F; | ||
| 166 | |||
| 167 | /* Set the first char of padding to 0x80. This is safe since there is | ||
| 168 | always at least one byte free */ | ||
| 169 | p = ctx->in + count; | ||
| 170 | *p++ = 0x80; | ||
| 171 | |||
| 172 | /* Bytes of padding needed to make 64 bytes */ | ||
| 173 | count = 64 - 1 - count; | ||
| 174 | |||
| 175 | /* Pad out to 56 mod 64 */ | ||
| 176 | if (count < 8) | ||
| 177 | { | ||
| 178 | /* Two lots of padding: Pad the first block to 64 bytes */ | ||
| 179 | memset(p, 0, count); | ||
| 180 | if (ctx->doByteReverse) | ||
| 181 | byteReverse(ctx->in, 16); | ||
| 182 | MD5Transform(ctx->buf, (guint32 *) ctx->in); | ||
| 183 | |||
| 184 | /* Now fill the next block with 56 bytes */ | ||
| 185 | memset(ctx->in, 0, 56); | ||
| 186 | } | ||
| 187 | else | ||
| 188 | { | ||
| 189 | /* Pad block to 56 bytes */ | ||
| 190 | memset(p, 0, count - 8); | ||
| 191 | } | ||
| 192 | if (ctx->doByteReverse) | ||
| 193 | byteReverse(ctx->in, 14); | ||
| 194 | |||
| 195 | /* Append length in bits and transform */ | ||
| 196 | ((guint32 *) ctx->in)[14] = ctx->bits[0]; | ||
| 197 | ((guint32 *) ctx->in)[15] = ctx->bits[1]; | ||
| 198 | |||
| 199 | MD5Transform(ctx->buf, (guint32 *) ctx->in); | ||
| 200 | if (ctx->doByteReverse) | ||
| 201 | byteReverse((guint8 *) ctx->buf, 4); | ||
| 202 | g_memmove(digest, ctx->buf, 16); | ||
| 203 | memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ | ||
| 204 | } | ||
| 205 | |||
| 206 | /* The four core functions - F1 is optimized somewhat */ | ||
| 207 | |||
| 208 | /* #define F1(x, y, z) (x & y | ~x & z) */ | ||
| 209 | #define F1(x, y, z) (z ^ (x & (y ^ z))) | ||
| 210 | #define F2(x, y, z) F1(z, x, y) | ||
| 211 | #define F3(x, y, z) (x ^ y ^ z) | ||
| 212 | #define F4(x, y, z) (y ^ (x | ~z)) | ||
| 213 | |||
| 214 | /* This is the central step in the MD5 algorithm. */ | ||
| 215 | #define MD5STEP(f, w, x, y, z, data, s) \ | ||
| 216 | ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) | ||
| 217 | |||
| 218 | /* | ||
| 219 | * The core of the MD5 algorithm, this alters an existing MD5 hash to | ||
| 220 | * reflect the addition of 16 longwords of new data. MD5Update blocks | ||
| 221 | * the data and converts bytes into longwords for this routine. | ||
| 222 | */ | ||
| 223 | void | ||
| 224 | MD5Transform(guint32 buf[4], guint32 const in[16]) | ||
| 225 | { | ||
| 226 | register guint32 a, b, c, d; | ||
| 227 | |||
| 228 | a = buf[0]; | ||
| 229 | b = buf[1]; | ||
| 230 | c = buf[2]; | ||
| 231 | d = buf[3]; | ||
| 232 | |||
| 233 | MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); | ||
| 234 | MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); | ||
| 235 | MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); | ||
| 236 | MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); | ||
| 237 | MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); | ||
| 238 | MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); | ||
| 239 | MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); | ||
| 240 | MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); | ||
| 241 | MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); | ||
| 242 | MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); | ||
| 243 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); | ||
| 244 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); | ||
| 245 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); | ||
| 246 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); | ||
| 247 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); | ||
| 248 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); | ||
| 249 | |||
| 250 | MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); | ||
| 251 | MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); | ||
| 252 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); | ||
| 253 | MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); | ||
| 254 | MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); | ||
| 255 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); | ||
| 256 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); | ||
| 257 | MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); | ||
| 258 | MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); | ||
| 259 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); | ||
| 260 | MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); | ||
| 261 | MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); | ||
| 262 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); | ||
| 263 | MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); | ||
| 264 | MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); | ||
| 265 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); | ||
| 266 | |||
| 267 | MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); | ||
| 268 | MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); | ||
| 269 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); | ||
| 270 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); | ||
| 271 | MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); | ||
| 272 | MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); | ||
| 273 | MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); | ||
| 274 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); | ||
| 275 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); | ||
| 276 | MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); | ||
| 277 | MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); | ||
| 278 | MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); | ||
| 279 | MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); | ||
| 280 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); | ||
| 281 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); | ||
| 282 | MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); | ||
| 283 | |||
| 284 | MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); | ||
| 285 | MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); | ||
| 286 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); | ||
| 287 | MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); | ||
| 288 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); | ||
| 289 | MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); | ||
| 290 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); | ||
| 291 | MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); | ||
| 292 | MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); | ||
| 293 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); | ||
| 294 | MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); | ||
| 295 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); | ||
| 296 | MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); | ||
| 297 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); | ||
| 298 | MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); | ||
| 299 | MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); | ||
| 300 | |||
| 301 | buf[0] += a; | ||
| 302 | buf[1] += b; | ||
| 303 | buf[2] += c; | ||
| 304 | buf[3] += d; | ||
| 305 | } | ||
| 306 | |||
| 307 | |||
| 308 | |||
| 309 | /* ************************************************************ */ | ||
| 310 | /* Code below is David Helder's API for GNet */ | ||
| 311 | |||
| 312 | struct _SMD5 | ||
| 313 | { | ||
| 314 | struct MD5Context ctx; | ||
| 315 | gchar digest[S_GNET_MD5_HASH_LENGTH]; | ||
| 316 | }; | ||
| 317 | |||
| 318 | |||
| 319 | /** | ||
| 320 | * s_gnet_md5_new: | ||
| 321 | * @buffer: buffer to hash | ||
| 322 | * @length: length of @buffer | ||
| 323 | * | ||
| 324 | * Creates a #SMD5 from @buffer. | ||
| 325 | * | ||
| 326 | * Returns: a new #SMD5. | ||
| 327 | * | ||
| 328 | **/ | ||
| 329 | SMD5* | ||
| 330 | s_gnet_md5_new (const guchar* buffer, guint length) | ||
| 331 | { | ||
| 332 | SMD5* md5; | ||
| 333 | |||
| 334 | md5 = g_new0 (SMD5, 1); | ||
| 335 | MD5Init (&md5->ctx); | ||
| 336 | MD5Update (&md5->ctx, buffer, length); | ||
| 337 | MD5Final ((gpointer) &md5->digest, &md5->ctx); | ||
| 338 | |||
| 339 | return md5; | ||
| 340 | } | ||
| 341 | |||
| 342 | |||
| 343 | |||
| 344 | /** | ||
| 345 | * s_gnet_md5_new_string: | ||
| 346 | * @str: hexidecimal string | ||
| 347 | * | ||
| 348 | * Creates a #SMD5 from @str. @str is a hexidecimal string | ||
| 349 | * representing the digest. | ||
| 350 | * | ||
| 351 | * Returns: a new #SMD5. | ||
| 352 | * | ||
| 353 | **/ | ||
| 354 | SMD5* | ||
| 355 | s_gnet_md5_new_string (const gchar* str) | ||
| 356 | { | ||
| 357 | SMD5* md5; | ||
| 358 | guint i; | ||
| 359 | |||
| 360 | g_return_val_if_fail (str, NULL); | ||
| 361 | g_return_val_if_fail (strlen(str) >= (S_GNET_MD5_HASH_LENGTH * 2), NULL); | ||
| 362 | |||
| 363 | md5 = g_new0 (SMD5, 1); | ||
| 364 | |||
| 365 | for (i = 0; i < (S_GNET_MD5_HASH_LENGTH * 2); ++i) | ||
| 366 | { | ||
| 367 | guint val = 0; | ||
| 368 | |||
| 369 | switch (str[i]) | ||
| 370 | { | ||
| 371 | case '0': val = 0; break; | ||
| 372 | case '1': val = 1; break; | ||
| 373 | case '2': val = 2; break; | ||
| 374 | case '3': val = 3; break; | ||
| 375 | case '4': val = 4; break; | ||
| 376 | case '5': val = 5; break; | ||
| 377 | case '6': val = 6; break; | ||
| 378 | case '7': val = 7; break; | ||
| 379 | case '8': val = 8; break; | ||
| 380 | case '9': val = 9; break; | ||
| 381 | case 'A': | ||
| 382 | case 'a': val = 10; break; | ||
| 383 | case 'B': | ||
| 384 | case 'b': val = 11; break; | ||
| 385 | case 'C': | ||
| 386 | case 'c': val = 12; break; | ||
| 387 | case 'D': | ||
| 388 | case 'd': val = 13; break; | ||
| 389 | case 'E': | ||
| 390 | case 'e': val = 14; break; | ||
| 391 | case 'F': | ||
| 392 | case 'f': val = 15; break; | ||
| 393 | default: | ||
| 394 | g_return_val_if_fail (FALSE, NULL); | ||
| 395 | } | ||
| 396 | |||
| 397 | if (i % 2) | ||
| 398 | md5->digest[i / 2] |= val; | ||
| 399 | else | ||
| 400 | md5->digest[i / 2] = val << 4; | ||
| 401 | } | ||
| 402 | |||
| 403 | return md5; | ||
| 404 | } | ||
| 405 | |||
| 406 | |||
| 407 | |||
| 408 | /** | ||
| 409 | * s_gnet_md5_clone | ||
| 410 | * @md5: a #SMD5 | ||
| 411 | * | ||
| 412 | * Copies a #SMD5. | ||
| 413 | * | ||
| 414 | * Returns: a copy of @md5. | ||
| 415 | * | ||
| 416 | **/ | ||
| 417 | SMD5* | ||
| 418 | s_gnet_md5_clone (const SMD5* md5) | ||
| 419 | { | ||
| 420 | SMD5* md52; | ||
| 421 | |||
| 422 | g_return_val_if_fail (md5, NULL); | ||
| 423 | |||
| 424 | md52 = g_new0 (SMD5, 1); | ||
| 425 | md52->ctx = md5->ctx; | ||
| 426 | memcpy (md52->digest, md5->digest, sizeof(md5->digest)); | ||
| 427 | |||
| 428 | return md52; | ||
| 429 | } | ||
| 430 | |||
| 431 | |||
| 432 | |||
| 433 | /** | ||
| 434 | * s_gnet_md5_delete | ||
| 435 | * @md5: a #SMD5 | ||
| 436 | * | ||
| 437 | * Deletes a #SMD5. | ||
| 438 | * | ||
| 439 | **/ | ||
| 440 | void | ||
| 441 | s_gnet_md5_delete (SMD5* md5) | ||
| 442 | { | ||
| 443 | if (md5) | ||
| 444 | g_free (md5); | ||
| 445 | } | ||
| 446 | |||
| 447 | |||
| 448 | |||
| 449 | /** | ||
| 450 | * s_gnet_md5_new_incremental | ||
| 451 | * | ||
| 452 | * Creates a #SMD5 incrementally. After creating a #SMD5, call | ||
| 453 | * s_gnet_md5_update() one or more times to hash data. Finally, call | ||
| 454 | * s_gnet_md5_final() to compute the final hash value. | ||
| 455 | * | ||
| 456 | * Returns: a new #SMD5. | ||
| 457 | * | ||
| 458 | **/ | ||
| 459 | SMD5* | ||
| 460 | s_gnet_md5_new_incremental (void) | ||
| 461 | { | ||
| 462 | SMD5* md5; | ||
| 463 | |||
| 464 | md5 = g_new0 (SMD5, 1); | ||
| 465 | MD5Init (&md5->ctx); | ||
| 466 | return md5; | ||
| 467 | } | ||
| 468 | |||
| 469 | |||
| 470 | /** | ||
| 471 | * s_gnet_md5_update | ||
| 472 | * @md5: a #SMD5 | ||
| 473 | * @buffer: buffer to add | ||
| 474 | * @length: length of @buffer | ||
| 475 | * | ||
| 476 | * Updates the hash with @buffer. This may be called several times | ||
| 477 | * on a hash created by s_gnet_md5_new_incremental() before being | ||
| 478 | * finalized by calling s_gnet_md5_final(). | ||
| 479 | * | ||
| 480 | **/ | ||
| 481 | void | ||
| 482 | s_gnet_md5_update (SMD5* md5, const guchar* buffer, guint length) | ||
| 483 | { | ||
| 484 | g_return_if_fail (md5); | ||
| 485 | |||
| 486 | MD5Update (&md5->ctx, buffer, length); | ||
| 487 | } | ||
| 488 | |||
| 489 | |||
| 490 | /** | ||
| 491 | * s_gnet_md5_final | ||
| 492 | * @md5: a #SMD5 | ||
| 493 | * | ||
| 494 | * Calcuates the final hash value of a #SMD5. This should only be | ||
| 495 | * called on an #SMD5 created by s_gnet_md5_new_incremental(). | ||
| 496 | * | ||
| 497 | **/ | ||
| 498 | void | ||
| 499 | s_gnet_md5_final (SMD5* md5) | ||
| 500 | { | ||
| 501 | g_return_if_fail (md5); | ||
| 502 | |||
| 503 | MD5Final ((gpointer) &md5->digest, &md5->ctx); | ||
| 504 | } | ||
| 505 | |||
| 506 | |||
| 507 | /* **************************************** */ | ||
| 508 | |||
| 509 | /** | ||
| 510 | * s_gnet_md5_equal | ||
| 511 | * @p1: first #SMD5. | ||
| 512 | * @p2: second #SMD5. | ||
| 513 | * | ||
| 514 | * Compares two #SMD5's for equality. | ||
| 515 | * | ||
| 516 | * Returns: TRUE if they are equal; FALSE otherwise. | ||
| 517 | * | ||
| 518 | **/ | ||
| 519 | gint | ||
| 520 | s_gnet_md5_equal (gconstpointer p1, gconstpointer p2) | ||
| 521 | { | ||
| 522 | SMD5* md5a = (SMD5*) p1; | ||
| 523 | SMD5* md5b = (SMD5*) p2; | ||
| 524 | guint i; | ||
| 525 | |||
| 526 | for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i) | ||
| 527 | if (md5a->digest[i] != md5b->digest[i]) | ||
| 528 | return FALSE; | ||
| 529 | |||
| 530 | return TRUE; | ||
| 531 | } | ||
| 532 | |||
| 533 | |||
| 534 | /** | ||
| 535 | * s_gnet_md5_hash | ||
| 536 | * @p: a #SMD5 | ||
| 537 | * | ||
| 538 | * Creates a hash code for a #SMD5 for use with GHashTable. This | ||
| 539 | * hash value is not the same as the MD5 digest. | ||
| 540 | * | ||
| 541 | * Returns: the hash code for @p. | ||
| 542 | * | ||
| 543 | **/ | ||
| 544 | guint | ||
| 545 | s_gnet_md5_hash (gconstpointer p) | ||
| 546 | { | ||
| 547 | const SMD5* md5 = (const SMD5*) p; | ||
| 548 | const guint* q; | ||
| 549 | |||
| 550 | g_return_val_if_fail (md5, 0); | ||
| 551 | |||
| 552 | q = (const guint*) md5->digest; | ||
| 553 | |||
| 554 | return (q[0] ^ q[1] ^ q[2] ^ q[3]); | ||
| 555 | } | ||
| 556 | |||
| 557 | |||
| 558 | /** | ||
| 559 | * s_gnet_md5_get_digest | ||
| 560 | * @md5: a #SMD5 | ||
| 561 | * | ||
| 562 | * Gets the raw MD5 digest. | ||
| 563 | * | ||
| 564 | * Returns: a callee-owned buffer containing the MD5 hash digest. | ||
| 565 | * The buffer is %S_GNET_MD5_HASH_LENGTH bytes long. | ||
| 566 | * | ||
| 567 | **/ | ||
| 568 | gchar* | ||
| 569 | s_gnet_md5_get_digest (const SMD5* md5) | ||
| 570 | { | ||
| 571 | g_return_val_if_fail (md5, NULL); | ||
| 572 | |||
| 573 | return (gchar*) md5->digest; | ||
| 574 | } | ||
| 575 | |||
| 576 | |||
| 577 | static gchar bits2hex[16] = { '0', '1', '2', '3', | ||
| 578 | '4', '5', '6', '7', | ||
| 579 | '8', '9', 'a', 'b', | ||
| 580 | 'c', 'd', 'e', 'f' }; | ||
| 581 | |||
| 582 | /** | ||
| 583 | * s_gnet_md5_get_string | ||
| 584 | * @md5: a #SMD5 | ||
| 585 | * | ||
| 586 | * Gets the digest represented a human-readable string. | ||
| 587 | * | ||
| 588 | * Returns: a hexadecimal string representing the digest. The string | ||
| 589 | * is 2 * %S_GNET_MD5_HASH_LENGTH bytes long and NULL terminated. The | ||
| 590 | * string is caller owned. | ||
| 591 | * | ||
| 592 | **/ | ||
| 593 | gchar* | ||
| 594 | s_gnet_md5_get_string (const SMD5* md5) | ||
| 595 | { | ||
| 596 | gchar* str; | ||
| 597 | guint i; | ||
| 598 | |||
| 599 | g_return_val_if_fail (md5, NULL); | ||
| 600 | |||
| 601 | str = g_new (gchar, S_GNET_MD5_HASH_LENGTH * 2 + 1); | ||
| 602 | str[S_GNET_MD5_HASH_LENGTH * 2] = '\0'; | ||
| 603 | |||
| 604 | for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i) | ||
| 605 | { | ||
| 606 | str[i * 2] = bits2hex[(md5->digest[i] & 0xF0) >> 4]; | ||
| 607 | str[(i * 2) + 1] = bits2hex[(md5->digest[i] & 0x0F) ]; | ||
| 608 | } | ||
| 609 | |||
| 610 | return str; | ||
| 611 | } | ||
| 612 | |||
| 613 | |||
| 614 | |||
| 615 | /** | ||
| 616 | * s_gnet_md5_copy_string | ||
| 617 | * @md5: a #SMD5 | ||
| 618 | * @buffer: buffer at least 2 * %S_GNET_MD5_HASH_LENGTH bytes long | ||
| 619 | * | ||
| 620 | * Copies the digest, represented as a string, into @buffer. The | ||
| 621 | * string is not NULL terminated. | ||
| 622 | * | ||
| 623 | **/ | ||
| 624 | void | ||
| 625 | s_gnet_md5_copy_string (const SMD5* md5, gchar* buffer) | ||
| 626 | { | ||
| 627 | guint i; | ||
| 628 | |||
| 629 | g_return_if_fail (md5); | ||
| 630 | g_return_if_fail (buffer); | ||
| 631 | |||
| 632 | for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i) | ||
| 633 | { | ||
| 634 | buffer[i * 2] = bits2hex[(md5->digest[i] & 0xF0) >> 4]; | ||
| 635 | buffer[(i * 2) + 1] = bits2hex[(md5->digest[i] & 0x0F) ]; | ||
| 636 | } | ||
| 637 | } | ||
diff --git a/libsylph/md5.h b/libsylph/md5.h new file mode 100644 index 0000000..e50b387 --- /dev/null +++ b/libsylph/md5.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | /** | ||
| 2 | |||
| 3 | This code is in the public domain. See md5.c for details. | ||
| 4 | |||
| 5 | Authors: | ||
| 6 | Colin Plumb [original author] | ||
| 7 | David Helder [GNet API] | ||
| 8 | |||
| 9 | Modified the prefix of functions to prevent conflict with original GNet. | ||
| 10 | |||
| 11 | */ | ||
| 12 | |||
| 13 | |||
| 14 | #ifndef S_GNET_MD5_H | ||
| 15 | #define S_GNET_MD5_H | ||
| 16 | |||
| 17 | #include <glib.h> | ||
| 18 | |||
| 19 | #ifdef __cplusplus | ||
| 20 | extern "C" { | ||
| 21 | #endif /* __cplusplus */ | ||
| 22 | |||
| 23 | /** | ||
| 24 | * SMD5 | ||
| 25 | * | ||
| 26 | * SMD5 is a MD5 hash. | ||
| 27 | * | ||
| 28 | **/ | ||
| 29 | typedef struct _SMD5 SMD5; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * S_GNET_MD5_HASH_LENGTH | ||
| 33 | * | ||
| 34 | * Length of the MD5 hash in bytes. | ||
| 35 | **/ | ||
| 36 | #define S_GNET_MD5_HASH_LENGTH 16 | ||
| 37 | |||
| 38 | |||
| 39 | SMD5* s_gnet_md5_new (const guchar* buffer, guint length); | ||
| 40 | SMD5* s_gnet_md5_new_string (const gchar* str); | ||
| 41 | SMD5* s_gnet_md5_clone (const SMD5* md5); | ||
| 42 | void s_gnet_md5_delete (SMD5* md5); | ||
| 43 | |||
| 44 | SMD5* s_gnet_md5_new_incremental (void); | ||
| 45 | void s_gnet_md5_update (SMD5* md5, const guchar* buffer, guint length); | ||
| 46 | void s_gnet_md5_final (SMD5* md5); | ||
| 47 | |||
| 48 | gboolean s_gnet_md5_equal (gconstpointer p1, gconstpointer p2); | ||
| 49 | guint s_gnet_md5_hash (gconstpointer p); | ||
| 50 | |||
| 51 | gchar* s_gnet_md5_get_digest (const SMD5* md5); | ||
| 52 | gchar* s_gnet_md5_get_string (const SMD5* md5); | ||
| 53 | |||
| 54 | void s_gnet_md5_copy_string (const SMD5* md5, gchar* buffer); | ||
| 55 | |||
| 56 | |||
| 57 | #ifdef __cplusplus | ||
| 58 | } | ||
| 59 | #endif /* __cplusplus */ | ||
| 60 | |||
| 61 | #endif /* S_GNET_MD5_H */ | ||
diff --git a/libsylph/md5_hmac.c b/libsylph/md5_hmac.c new file mode 100644 index 0000000..77ae39e --- /dev/null +++ b/libsylph/md5_hmac.c | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2006 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <string.h> | ||
| 26 | |||
| 27 | #include "md5.h" | ||
| 28 | #include "md5_hmac.h" | ||
| 29 | |||
| 30 | /* | ||
| 31 | ** Function: md5_hmac_get | ||
| 32 | ** taken from the file rfc2104.txt | ||
| 33 | ** originally written by Martin Schaaf <mascha@ma-scha.de> | ||
| 34 | ** rewritten by Hiroyuki Yamamoto <hiro-y@kcn.ne.jp> | ||
| 35 | */ | ||
| 36 | static SMD5* | ||
| 37 | md5_hmac_get(const guchar *text, gint text_len, | ||
| 38 | const guchar *key, gint key_len) | ||
| 39 | { | ||
| 40 | SMD5 *md5; | ||
| 41 | guchar k_ipad[64]; /* inner padding - | ||
| 42 | * key XORd with ipad | ||
| 43 | */ | ||
| 44 | guchar k_opad[64]; /* outer padding - | ||
| 45 | * key XORd with opad | ||
| 46 | */ | ||
| 47 | guchar digest[S_GNET_MD5_HASH_LENGTH]; | ||
| 48 | gint i; | ||
| 49 | |||
| 50 | /* start out by storing key in pads */ | ||
| 51 | memset(k_ipad, 0, sizeof k_ipad); | ||
| 52 | memset(k_opad, 0, sizeof k_opad); | ||
| 53 | |||
| 54 | if (key_len > 64) { | ||
| 55 | /* if key is longer than 64 bytes reset it to key=MD5(key) */ | ||
| 56 | SMD5 *tmd5; | ||
| 57 | |||
| 58 | tmd5 = s_gnet_md5_new(key, key_len); | ||
| 59 | memcpy(k_ipad, s_gnet_md5_get_digest(tmd5), | ||
| 60 | S_GNET_MD5_HASH_LENGTH); | ||
| 61 | memcpy(k_opad, s_gnet_md5_get_digest(tmd5), | ||
| 62 | S_GNET_MD5_HASH_LENGTH); | ||
| 63 | s_gnet_md5_delete(tmd5); | ||
| 64 | } else { | ||
| 65 | memcpy(k_ipad, key, key_len); | ||
| 66 | memcpy(k_opad, key, key_len); | ||
| 67 | } | ||
| 68 | |||
| 69 | /* | ||
| 70 | * the HMAC_MD5 transform looks like: | ||
| 71 | * | ||
| 72 | * MD5(K XOR opad, MD5(K XOR ipad, text)) | ||
| 73 | * | ||
| 74 | * where K is an n byte key | ||
| 75 | * ipad is the byte 0x36 repeated 64 times | ||
| 76 | * opad is the byte 0x5c repeated 64 times | ||
| 77 | * and text is the data being protected | ||
| 78 | */ | ||
| 79 | |||
| 80 | |||
| 81 | /* XOR key with ipad and opad values */ | ||
| 82 | for (i = 0; i < 64; i++) { | ||
| 83 | k_ipad[i] ^= 0x36; | ||
| 84 | k_opad[i] ^= 0x5c; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * perform inner MD5 | ||
| 89 | */ | ||
| 90 | md5 = s_gnet_md5_new_incremental(); /* init context for 1st | ||
| 91 | * pass */ | ||
| 92 | s_gnet_md5_update(md5, k_ipad, 64); /* start with inner pad */ | ||
| 93 | s_gnet_md5_update(md5, text, text_len); /* then text of datagram */ | ||
| 94 | s_gnet_md5_final(md5); /* finish up 1st pass */ | ||
| 95 | memcpy(digest, s_gnet_md5_get_digest(md5), S_GNET_MD5_HASH_LENGTH); | ||
| 96 | s_gnet_md5_delete(md5); | ||
| 97 | |||
| 98 | /* | ||
| 99 | * perform outer MD5 | ||
| 100 | */ | ||
| 101 | md5 = s_gnet_md5_new_incremental(); /* init context for 2nd | ||
| 102 | * pass */ | ||
| 103 | s_gnet_md5_update(md5, k_opad, 64); /* start with outer pad */ | ||
| 104 | s_gnet_md5_update(md5, digest, 16); /* then results of 1st | ||
| 105 | * hash */ | ||
| 106 | s_gnet_md5_final(md5); /* finish up 2nd pass */ | ||
| 107 | |||
| 108 | return md5; | ||
| 109 | } | ||
| 110 | |||
| 111 | void | ||
| 112 | md5_hmac(guchar *digest, | ||
| 113 | const guchar *text, gint text_len, | ||
| 114 | const guchar *key, gint key_len) | ||
| 115 | { | ||
| 116 | SMD5 *md5; | ||
| 117 | |||
| 118 | md5 = md5_hmac_get(text, text_len, key, key_len); | ||
| 119 | memcpy(digest, s_gnet_md5_get_digest(md5), S_GNET_MD5_HASH_LENGTH); | ||
| 120 | s_gnet_md5_delete(md5); | ||
| 121 | } | ||
| 122 | |||
| 123 | void | ||
| 124 | md5_hex_hmac(gchar *hexdigest, | ||
| 125 | const guchar *text, gint text_len, | ||
| 126 | const guchar *key, gint key_len) | ||
| 127 | { | ||
| 128 | SMD5 *md5; | ||
| 129 | |||
| 130 | md5 = md5_hmac_get(text, text_len, key, key_len); | ||
| 131 | s_gnet_md5_copy_string(md5, hexdigest); | ||
| 132 | hexdigest[S_GNET_MD5_HASH_LENGTH * 2] = '\0'; | ||
| 133 | s_gnet_md5_delete(md5); | ||
| 134 | } | ||
diff --git a/libsylph/md5_hmac.h b/libsylph/md5_hmac.h new file mode 100644 index 0000000..14c9318 --- /dev/null +++ b/libsylph/md5_hmac.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MD5_HMAC_H__ | ||
| 21 | #define __MD5_HMAC_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | void md5_hmac (guchar *digest, | ||
| 26 | const guchar* text, gint text_len, | ||
| 27 | const guchar* key, gint key_len); | ||
| 28 | void md5_hex_hmac (gchar *hexdigest, | ||
| 29 | const guchar* text, gint text_len, | ||
| 30 | const guchar* key, gint key_len); | ||
| 31 | |||
| 32 | #endif /* __MD5_HMAC_H__ */ | ||
| 33 | |||
diff --git a/libsylph/mh.c b/libsylph/mh.c new file mode 100644 index 0000000..c1f0679 --- /dev/null +++ b/libsylph/mh.c | |||
| @@ -0,0 +1,1898 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2009 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <dirent.h> | ||
| 29 | #include <sys/stat.h> | ||
| 30 | #include <time.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | #include <string.h> | ||
| 33 | #include <errno.h> | ||
| 34 | |||
| 35 | #ifdef G_OS_WIN32 | ||
| 36 | # include <windows.h> | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #undef MEASURE_TIME | ||
| 40 | |||
| 41 | #include "sylmain.h" | ||
| 42 | #include "folder.h" | ||
| 43 | #include "mh.h" | ||
| 44 | #include "procmsg.h" | ||
| 45 | #include "procheader.h" | ||
| 46 | #include "utils.h" | ||
| 47 | #include "prefs_common.h" | ||
| 48 | |||
| 49 | #if USE_THREADS | ||
| 50 | G_LOCK_DEFINE_STATIC(mh); | ||
| 51 | #define S_LOCK(name) G_LOCK(name) | ||
| 52 | #define S_UNLOCK(name) G_UNLOCK(name) | ||
| 53 | #else | ||
| 54 | #define S_LOCK(name) | ||
| 55 | #define S_UNLOCK(name) | ||
| 56 | #endif | ||
| 57 | |||
| 58 | static void mh_folder_init (Folder *folder, | ||
| 59 | const gchar *name, | ||
| 60 | const gchar *path); | ||
| 61 | |||
| 62 | static Folder *mh_folder_new (const gchar *name, | ||
| 63 | const gchar *path); | ||
| 64 | static void mh_folder_destroy (Folder *folder); | ||
| 65 | |||
| 66 | static GSList *mh_get_msg_list (Folder *folder, | ||
| 67 | FolderItem *item, | ||
| 68 | gboolean use_cache); | ||
| 69 | static GSList *mh_get_uncached_msg_list(Folder *folder, | ||
| 70 | FolderItem *item); | ||
| 71 | static gchar *mh_fetch_msg (Folder *folder, | ||
| 72 | FolderItem *item, | ||
| 73 | gint num); | ||
| 74 | static MsgInfo *mh_get_msginfo (Folder *folder, | ||
| 75 | FolderItem *item, | ||
| 76 | gint num); | ||
| 77 | static gint mh_add_msg (Folder *folder, | ||
| 78 | FolderItem *dest, | ||
| 79 | const gchar *file, | ||
| 80 | MsgFlags *flags, | ||
| 81 | gboolean remove_source); | ||
| 82 | static gint mh_add_msgs (Folder *folder, | ||
| 83 | FolderItem *dest, | ||
| 84 | GSList *file_list, | ||
| 85 | gboolean remove_source, | ||
| 86 | gint *first); | ||
| 87 | static gint mh_add_msg_msginfo (Folder *folder, | ||
| 88 | FolderItem *dest, | ||
| 89 | MsgInfo *msginfo, | ||
| 90 | gboolean remove_source); | ||
| 91 | static gint mh_add_msgs_msginfo (Folder *folder, | ||
| 92 | FolderItem *dest, | ||
| 93 | GSList *msglist, | ||
| 94 | gboolean remove_source, | ||
| 95 | gint *first); | ||
| 96 | static gint mh_move_msg (Folder *folder, | ||
| 97 | FolderItem *dest, | ||
| 98 | MsgInfo *msginfo); | ||
| 99 | static gint mh_move_msgs (Folder *folder, | ||
| 100 | FolderItem *dest, | ||
| 101 | GSList *msglist); | ||
| 102 | static gint mh_copy_msg (Folder *folder, | ||
| 103 | FolderItem *dest, | ||
| 104 | MsgInfo *msginfo); | ||
| 105 | static gint mh_copy_msgs (Folder *folder, | ||
| 106 | FolderItem *dest, | ||
| 107 | GSList *msglist); | ||
| 108 | static gint mh_remove_msg (Folder *folder, | ||
| 109 | FolderItem *item, | ||
| 110 | MsgInfo *msginfo); | ||
| 111 | static gint mh_remove_all_msg (Folder *folder, | ||
| 112 | FolderItem *item); | ||
| 113 | static gboolean mh_is_msg_changed (Folder *folder, | ||
| 114 | FolderItem *item, | ||
| 115 | MsgInfo *msginfo); | ||
| 116 | static gint mh_close (Folder *folder, | ||
| 117 | FolderItem *item); | ||
| 118 | |||
| 119 | static gint mh_scan_folder_full (Folder *folder, | ||
| 120 | FolderItem *item, | ||
| 121 | gboolean count_sum); | ||
| 122 | static gint mh_scan_folder (Folder *folder, | ||
| 123 | FolderItem *item); | ||
| 124 | static gint mh_scan_tree (Folder *folder); | ||
| 125 | |||
| 126 | static gint mh_create_tree (Folder *folder); | ||
| 127 | static FolderItem *mh_create_folder (Folder *folder, | ||
| 128 | FolderItem *parent, | ||
| 129 | const gchar *name); | ||
| 130 | static gint mh_rename_folder (Folder *folder, | ||
| 131 | FolderItem *item, | ||
| 132 | const gchar *name); | ||
| 133 | static gint mh_move_folder (Folder *folder, | ||
| 134 | FolderItem *item, | ||
| 135 | FolderItem *new_parent); | ||
| 136 | static gint mh_remove_folder (Folder *folder, | ||
| 137 | FolderItem *item); | ||
| 138 | |||
| 139 | static gchar *mh_get_new_msg_filename (FolderItem *dest); | ||
| 140 | |||
| 141 | static gint mh_do_move_msgs (Folder *folder, | ||
| 142 | FolderItem *dest, | ||
| 143 | GSList *msglist); | ||
| 144 | |||
| 145 | static time_t mh_get_mtime (FolderItem *item); | ||
| 146 | static GSList *mh_get_uncached_msgs (GHashTable *msg_table, | ||
| 147 | FolderItem *item); | ||
| 148 | static MsgInfo *mh_parse_msg (const gchar *file, | ||
| 149 | FolderItem *item); | ||
| 150 | static void mh_remove_missing_folder_items (Folder *folder); | ||
| 151 | static void mh_scan_tree_recursive (FolderItem *item); | ||
| 152 | |||
| 153 | static gboolean mh_rename_folder_func (GNode *node, | ||
| 154 | gpointer data); | ||
| 155 | |||
| 156 | static FolderClass mh_class = | ||
| 157 | { | ||
| 158 | F_MH, | ||
| 159 | |||
| 160 | mh_folder_new, | ||
| 161 | mh_folder_destroy, | ||
| 162 | |||
| 163 | mh_scan_tree, | ||
| 164 | mh_create_tree, | ||
| 165 | |||
| 166 | mh_get_msg_list, | ||
| 167 | mh_get_uncached_msg_list, | ||
| 168 | mh_fetch_msg, | ||
| 169 | mh_get_msginfo, | ||
| 170 | mh_add_msg, | ||
| 171 | mh_add_msgs, | ||
| 172 | mh_add_msg_msginfo, | ||
| 173 | mh_add_msgs_msginfo, | ||
| 174 | mh_move_msg, | ||
| 175 | mh_move_msgs, | ||
| 176 | mh_copy_msg, | ||
| 177 | mh_copy_msgs, | ||
| 178 | mh_remove_msg, | ||
| 179 | NULL, | ||
| 180 | mh_remove_all_msg, | ||
| 181 | mh_is_msg_changed, | ||
| 182 | mh_close, | ||
| 183 | mh_scan_folder, | ||
| 184 | |||
| 185 | mh_create_folder, | ||
| 186 | mh_rename_folder, | ||
| 187 | mh_move_folder, | ||
| 188 | mh_remove_folder, | ||
| 189 | }; | ||
| 190 | |||
| 191 | |||
| 192 | FolderClass *mh_get_class(void) | ||
| 193 | { | ||
| 194 | return &mh_class; | ||
| 195 | } | ||
| 196 | |||
| 197 | static Folder *mh_folder_new(const gchar *name, const gchar *path) | ||
| 198 | { | ||
| 199 | Folder *folder; | ||
| 200 | |||
| 201 | folder = (Folder *)g_new0(MHFolder, 1); | ||
| 202 | mh_folder_init(folder, name, path); | ||
| 203 | |||
| 204 | return folder; | ||
| 205 | } | ||
| 206 | |||
| 207 | static void mh_folder_destroy(Folder *folder) | ||
| 208 | { | ||
| 209 | folder_local_folder_destroy(LOCAL_FOLDER(folder)); | ||
| 210 | } | ||
| 211 | |||
| 212 | static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path) | ||
| 213 | { | ||
| 214 | folder->klass = mh_get_class(); | ||
| 215 | folder_local_folder_init(folder, name, path); | ||
| 216 | } | ||
| 217 | |||
| 218 | static GSList *mh_get_msg_list_full(Folder *folder, FolderItem *item, | ||
| 219 | gboolean use_cache, gboolean uncached_only) | ||
| 220 | { | ||
| 221 | GSList *mlist; | ||
| 222 | GHashTable *msg_table; | ||
| 223 | time_t cur_mtime; | ||
| 224 | GSList *newlist = NULL; | ||
| 225 | #ifdef MEASURE_TIME | ||
| 226 | GTimer *timer; | ||
| 227 | #endif | ||
| 228 | |||
| 229 | g_return_val_if_fail(item != NULL, NULL); | ||
| 230 | |||
| 231 | S_LOCK(mh); | ||
| 232 | |||
| 233 | #ifdef MEASURE_TIME | ||
| 234 | timer = g_timer_new(); | ||
| 235 | #endif | ||
| 236 | |||
| 237 | cur_mtime = mh_get_mtime(item); | ||
| 238 | |||
| 239 | if (use_cache && item->mtime == cur_mtime) { | ||
| 240 | debug_print("Folder is not modified.\n"); | ||
| 241 | mlist = procmsg_read_cache(item, FALSE); | ||
| 242 | if (!mlist) { | ||
| 243 | mlist = mh_get_uncached_msgs(NULL, item); | ||
| 244 | if (mlist) | ||
| 245 | item->cache_dirty = TRUE; | ||
| 246 | } | ||
| 247 | } else if (use_cache) { | ||
| 248 | GSList *cur, *next; | ||
| 249 | gboolean strict_cache_check = prefs_common.strict_cache_check; | ||
| 250 | |||
| 251 | if (item->stype == F_QUEUE || item->stype == F_DRAFT) | ||
| 252 | strict_cache_check = TRUE; | ||
| 253 | |||
| 254 | mlist = procmsg_read_cache(item, strict_cache_check); | ||
| 255 | msg_table = procmsg_msg_hash_table_create(mlist); | ||
| 256 | newlist = mh_get_uncached_msgs(msg_table, item); | ||
| 257 | if (newlist) | ||
| 258 | item->cache_dirty = TRUE; | ||
| 259 | if (msg_table) | ||
| 260 | g_hash_table_destroy(msg_table); | ||
| 261 | |||
| 262 | if (!strict_cache_check) { | ||
| 263 | /* remove nonexistent messages */ | ||
| 264 | for (cur = mlist; cur != NULL; cur = next) { | ||
| 265 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 266 | next = cur->next; | ||
| 267 | if (!MSG_IS_CACHED(msginfo->flags)) { | ||
| 268 | debug_print("removing nonexistent message %d from cache\n", msginfo->msgnum); | ||
| 269 | mlist = g_slist_remove(mlist, msginfo); | ||
| 270 | procmsg_msginfo_free(msginfo); | ||
| 271 | item->cache_dirty = TRUE; | ||
| 272 | item->mark_dirty = TRUE; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | mlist = g_slist_concat(mlist, newlist); | ||
| 278 | } else { | ||
| 279 | mlist = mh_get_uncached_msgs(NULL, item); | ||
| 280 | item->cache_dirty = TRUE; | ||
| 281 | newlist = mlist; | ||
| 282 | } | ||
| 283 | |||
| 284 | procmsg_set_flags(mlist, item); | ||
| 285 | |||
| 286 | if (!uncached_only) | ||
| 287 | mlist = procmsg_sort_msg_list(mlist, item->sort_key, | ||
| 288 | item->sort_type); | ||
| 289 | |||
| 290 | if (item->mark_queue) | ||
| 291 | item->mark_dirty = TRUE; | ||
| 292 | |||
| 293 | #ifdef MEASURE_TIME | ||
| 294 | g_timer_stop(timer); | ||
| 295 | g_print("%s: %s: elapsed time: %f sec\n", | ||
| 296 | G_STRFUNC, item->path, g_timer_elapsed(timer, NULL)); | ||
| 297 | g_timer_destroy(timer); | ||
| 298 | #endif | ||
| 299 | debug_print("cache_dirty: %d, mark_dirty: %d\n", | ||
| 300 | item->cache_dirty, item->mark_dirty); | ||
| 301 | |||
| 302 | if (!item->opened) { | ||
| 303 | item->mtime = cur_mtime; | ||
| 304 | if (item->cache_dirty) | ||
| 305 | procmsg_write_cache_list(item, mlist); | ||
| 306 | if (item->mark_dirty) | ||
| 307 | procmsg_write_flags_list(item, mlist); | ||
| 308 | } | ||
| 309 | |||
| 310 | if (uncached_only) { | ||
| 311 | GSList *cur; | ||
| 312 | |||
| 313 | if (newlist == NULL) { | ||
| 314 | procmsg_msg_list_free(mlist); | ||
| 315 | S_UNLOCK(mh); | ||
| 316 | return NULL; | ||
| 317 | } | ||
| 318 | if (mlist == newlist) { | ||
| 319 | S_UNLOCK(mh); | ||
| 320 | return newlist; | ||
| 321 | } | ||
| 322 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 323 | if (cur->next == newlist) { | ||
| 324 | cur->next = NULL; | ||
| 325 | procmsg_msg_list_free(mlist); | ||
| 326 | S_UNLOCK(mh); | ||
| 327 | return newlist; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | procmsg_msg_list_free(mlist); | ||
| 331 | S_UNLOCK(mh); | ||
| 332 | return NULL; | ||
| 333 | } | ||
| 334 | |||
| 335 | S_UNLOCK(mh); | ||
| 336 | return mlist; | ||
| 337 | } | ||
| 338 | |||
| 339 | static GSList *mh_get_msg_list(Folder *folder, FolderItem *item, | ||
| 340 | gboolean use_cache) | ||
| 341 | { | ||
| 342 | return mh_get_msg_list_full(folder, item, use_cache, FALSE); | ||
| 343 | } | ||
| 344 | |||
| 345 | static GSList *mh_get_uncached_msg_list(Folder *folder, FolderItem *item) | ||
| 346 | { | ||
| 347 | return mh_get_msg_list_full(folder, item, TRUE, TRUE); | ||
| 348 | } | ||
| 349 | |||
| 350 | static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num) | ||
| 351 | { | ||
| 352 | gchar *path; | ||
| 353 | gchar *file; | ||
| 354 | gchar buf[16]; | ||
| 355 | |||
| 356 | g_return_val_if_fail(item != NULL, NULL); | ||
| 357 | g_return_val_if_fail(num > 0, NULL); | ||
| 358 | |||
| 359 | if (item->last_num < 0 || num > item->last_num) { | ||
| 360 | mh_scan_folder(folder, item); | ||
| 361 | if (item->last_num < 0) return NULL; | ||
| 362 | } | ||
| 363 | |||
| 364 | if (num > item->last_num) | ||
| 365 | return NULL; | ||
| 366 | |||
| 367 | path = folder_item_get_path(item); | ||
| 368 | file = g_strconcat(path, G_DIR_SEPARATOR_S, utos_buf(buf, num), NULL); | ||
| 369 | g_free(path); | ||
| 370 | if (!is_file_exist(file)) { | ||
| 371 | g_free(file); | ||
| 372 | return NULL; | ||
| 373 | } | ||
| 374 | |||
| 375 | return file; | ||
| 376 | } | ||
| 377 | |||
| 378 | static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num) | ||
| 379 | { | ||
| 380 | MsgInfo *msginfo; | ||
| 381 | gchar *file; | ||
| 382 | |||
| 383 | g_return_val_if_fail(item != NULL, NULL); | ||
| 384 | g_return_val_if_fail(num > 0, NULL); | ||
| 385 | |||
| 386 | file = mh_fetch_msg(folder, item, num); | ||
| 387 | if (!file) return NULL; | ||
| 388 | |||
| 389 | msginfo = mh_parse_msg(file, item); | ||
| 390 | if (msginfo) | ||
| 391 | msginfo->msgnum = num; | ||
| 392 | |||
| 393 | g_free(file); | ||
| 394 | |||
| 395 | return msginfo; | ||
| 396 | } | ||
| 397 | |||
| 398 | static gchar *mh_get_new_msg_filename(FolderItem *dest) | ||
| 399 | { | ||
| 400 | gchar *destfile; | ||
| 401 | gchar *destpath; | ||
| 402 | |||
| 403 | destpath = folder_item_get_path(dest); | ||
| 404 | g_return_val_if_fail(destpath != NULL, NULL); | ||
| 405 | |||
| 406 | if (!is_dir_exist(destpath)) | ||
| 407 | make_dir_hier(destpath); | ||
| 408 | |||
| 409 | for (;;) { | ||
| 410 | destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR, | ||
| 411 | dest->last_num + 1); | ||
| 412 | if (is_file_entry_exist(destfile)) { | ||
| 413 | dest->last_num++; | ||
| 414 | g_free(destfile); | ||
| 415 | } else | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | |||
| 419 | g_free(destpath); | ||
| 420 | |||
| 421 | return destfile; | ||
| 422 | } | ||
| 423 | |||
| 424 | #define SET_DEST_MSG_FLAGS(fp, dest, n, fl) \ | ||
| 425 | { \ | ||
| 426 | MsgInfo newmsginfo; \ | ||
| 427 | \ | ||
| 428 | newmsginfo.msgnum = n; \ | ||
| 429 | newmsginfo.flags = fl; \ | ||
| 430 | if (dest->stype == F_OUTBOX || \ | ||
| 431 | dest->stype == F_QUEUE || \ | ||
| 432 | dest->stype == F_DRAFT) { \ | ||
| 433 | MSG_UNSET_PERM_FLAGS(newmsginfo.flags, \ | ||
| 434 | MSG_NEW|MSG_UNREAD|MSG_DELETED); \ | ||
| 435 | } else if (dest->stype == F_TRASH) { \ | ||
| 436 | MSG_UNSET_PERM_FLAGS(newmsginfo.flags, MSG_DELETED); \ | ||
| 437 | } \ | ||
| 438 | \ | ||
| 439 | if (fp) \ | ||
| 440 | procmsg_write_flags(&newmsginfo, fp); \ | ||
| 441 | else \ | ||
| 442 | procmsg_add_mark_queue(dest, n, newmsginfo.flags); \ | ||
| 443 | } | ||
| 444 | |||
| 445 | static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file, | ||
| 446 | MsgFlags *flags, gboolean remove_source) | ||
| 447 | { | ||
| 448 | GSList file_list; | ||
| 449 | MsgFileInfo fileinfo; | ||
| 450 | |||
| 451 | g_return_val_if_fail(file != NULL, -1); | ||
| 452 | |||
| 453 | fileinfo.file = (gchar *)file; | ||
| 454 | fileinfo.flags = flags; | ||
| 455 | file_list.data = &fileinfo; | ||
| 456 | file_list.next = NULL; | ||
| 457 | |||
| 458 | return mh_add_msgs(folder, dest, &file_list, remove_source, NULL); | ||
| 459 | } | ||
| 460 | |||
| 461 | static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, | ||
| 462 | gboolean remove_source, gint *first) | ||
| 463 | { | ||
| 464 | gchar *destfile; | ||
| 465 | GSList *cur; | ||
| 466 | MsgFileInfo *fileinfo; | ||
| 467 | MsgInfo *msginfo; | ||
| 468 | gint first_ = 0; | ||
| 469 | FILE *fp = NULL; | ||
| 470 | |||
| 471 | g_return_val_if_fail(dest != NULL, -1); | ||
| 472 | g_return_val_if_fail(file_list != NULL, -1); | ||
| 473 | |||
| 474 | if (dest->last_num < 0) { | ||
| 475 | mh_scan_folder(folder, dest); | ||
| 476 | if (dest->last_num < 0) return -1; | ||
| 477 | } | ||
| 478 | |||
| 479 | S_LOCK(mh); | ||
| 480 | |||
| 481 | if (!dest->opened) { | ||
| 482 | if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL) | ||
| 483 | g_warning("mh_add_msgs: can't open mark file."); | ||
| 484 | } | ||
| 485 | |||
| 486 | for (cur = file_list; cur != NULL; cur = cur->next) { | ||
| 487 | MsgFlags flags = {MSG_NEW|MSG_UNREAD, 0}; | ||
| 488 | |||
| 489 | fileinfo = (MsgFileInfo *)cur->data; | ||
| 490 | if (fileinfo->flags) | ||
| 491 | flags = *fileinfo->flags; | ||
| 492 | msginfo = procheader_parse_file(fileinfo->file, flags, 0); | ||
| 493 | if (!msginfo) { | ||
| 494 | if (fp) fclose(fp); | ||
| 495 | S_UNLOCK(mh); | ||
| 496 | return -1; | ||
| 497 | } | ||
| 498 | |||
| 499 | destfile = mh_get_new_msg_filename(dest); | ||
| 500 | if (destfile == NULL) { | ||
| 501 | S_UNLOCK(mh); | ||
| 502 | return -1; | ||
| 503 | } | ||
| 504 | if (first_ == 0 || first_ > dest->last_num + 1) | ||
| 505 | first_ = dest->last_num + 1; | ||
| 506 | |||
| 507 | if (syl_link(fileinfo->file, destfile) < 0) { | ||
| 508 | if (copy_file(fileinfo->file, destfile, TRUE) < 0) { | ||
| 509 | g_warning(_("can't copy message %s to %s\n"), | ||
| 510 | fileinfo->file, destfile); | ||
| 511 | g_free(destfile); | ||
| 512 | if (fp) fclose(fp); | ||
| 513 | S_UNLOCK(mh); | ||
| 514 | return -1; | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | if (syl_app_get()) | ||
| 519 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1); | ||
| 520 | |||
| 521 | g_free(destfile); | ||
| 522 | dest->last_num++; | ||
| 523 | dest->total++; | ||
| 524 | dest->updated = TRUE; | ||
| 525 | dest->mtime = 0; | ||
| 526 | |||
| 527 | if (MSG_IS_RECEIVED(flags)) { | ||
| 528 | /* resets new flags of existing messages on | ||
| 529 | received mode */ | ||
| 530 | if (dest->unmarked_num == 0) | ||
| 531 | dest->new = 0; | ||
| 532 | dest->unmarked_num++; | ||
| 533 | procmsg_add_mark_queue(dest, dest->last_num, flags); | ||
| 534 | } else { | ||
| 535 | SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, flags); | ||
| 536 | } | ||
| 537 | procmsg_add_cache_queue(dest, dest->last_num, msginfo); | ||
| 538 | if (MSG_IS_NEW(flags)) | ||
| 539 | dest->new++; | ||
| 540 | if (MSG_IS_UNREAD(flags)) | ||
| 541 | dest->unread++; | ||
| 542 | } | ||
| 543 | |||
| 544 | if (fp) | ||
| 545 | fclose(fp); | ||
| 546 | |||
| 547 | if (first) | ||
| 548 | *first = first_; | ||
| 549 | |||
| 550 | if (remove_source) { | ||
| 551 | for (cur = file_list; cur != NULL; cur = cur->next) { | ||
| 552 | fileinfo = (MsgFileInfo *)cur->data; | ||
| 553 | if (g_unlink(fileinfo->file) < 0) | ||
| 554 | FILE_OP_ERROR(fileinfo->file, "unlink"); | ||
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 | S_UNLOCK(mh); | ||
| 559 | return dest->last_num; | ||
| 560 | } | ||
| 561 | |||
| 562 | |||
| 563 | static gint mh_add_msg_msginfo(Folder *folder, FolderItem *dest, | ||
| 564 | MsgInfo *msginfo, gboolean remove_source) | ||
| 565 | { | ||
| 566 | GSList msglist; | ||
| 567 | |||
| 568 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 569 | |||
| 570 | msglist.data = msginfo; | ||
| 571 | msglist.next = NULL; | ||
| 572 | |||
| 573 | return mh_add_msgs_msginfo(folder, dest, &msglist, remove_source, NULL); | ||
| 574 | } | ||
| 575 | |||
| 576 | static gint mh_add_msgs_msginfo(Folder *folder, FolderItem *dest, | ||
| 577 | GSList *msglist, gboolean remove_source, | ||
| 578 | gint *first) | ||
| 579 | { | ||
| 580 | GSList *cur; | ||
| 581 | MsgInfo *msginfo; | ||
| 582 | gchar *srcfile; | ||
| 583 | gchar *destfile; | ||
| 584 | gint first_ = 0; | ||
| 585 | FILE *fp = NULL; | ||
| 586 | |||
| 587 | g_return_val_if_fail(dest != NULL, -1); | ||
| 588 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 589 | |||
| 590 | if (dest->last_num < 0) { | ||
| 591 | mh_scan_folder(folder, dest); | ||
| 592 | if (dest->last_num < 0) return -1; | ||
| 593 | } | ||
| 594 | |||
| 595 | S_LOCK(mh); | ||
| 596 | |||
| 597 | if (!dest->opened) { | ||
| 598 | if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL) | ||
| 599 | g_warning("mh_add_msgs_msginfo: can't open mark file."); | ||
| 600 | } | ||
| 601 | |||
| 602 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 603 | msginfo = (MsgInfo *)cur->data; | ||
| 604 | |||
| 605 | destfile = mh_get_new_msg_filename(dest); | ||
| 606 | if (!destfile) { | ||
| 607 | if (fp) fclose(fp); | ||
| 608 | S_UNLOCK(mh); | ||
| 609 | return -1; | ||
| 610 | } | ||
| 611 | if (first_ == 0 || first_ > dest->last_num + 1) | ||
| 612 | first_ = dest->last_num + 1; | ||
| 613 | |||
| 614 | srcfile = procmsg_get_message_file(msginfo); | ||
| 615 | if (!srcfile) { | ||
| 616 | if (fp) fclose(fp); | ||
| 617 | g_free(destfile); | ||
| 618 | S_UNLOCK(mh); | ||
| 619 | return -1; | ||
| 620 | } | ||
| 621 | if (syl_link(srcfile, destfile) < 0) { | ||
| 622 | if (copy_file(srcfile, destfile, TRUE) < 0) { | ||
| 623 | g_warning("mh_add_msgs_msginfo: can't copy message %s to %s", srcfile, destfile); | ||
| 624 | g_free(srcfile); | ||
| 625 | g_free(destfile); | ||
| 626 | if (fp) fclose(fp); | ||
| 627 | S_UNLOCK(mh); | ||
| 628 | return -1; | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | if (syl_app_get()) | ||
| 633 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1); | ||
| 634 | |||
| 635 | g_free(srcfile); | ||
| 636 | g_free(destfile); | ||
| 637 | dest->last_num++; | ||
| 638 | dest->total++; | ||
| 639 | dest->updated = TRUE; | ||
| 640 | dest->mtime = 0; | ||
| 641 | |||
| 642 | if (MSG_IS_RECEIVED(msginfo->flags)) { | ||
| 643 | /* resets new flags of existing messages on | ||
| 644 | received mode */ | ||
| 645 | if (dest->unmarked_num == 0) | ||
| 646 | dest->new = 0; | ||
| 647 | dest->unmarked_num++; | ||
| 648 | procmsg_add_mark_queue(dest, dest->last_num, | ||
| 649 | msginfo->flags); | ||
| 650 | } else { | ||
| 651 | SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, | ||
| 652 | msginfo->flags); | ||
| 653 | } | ||
| 654 | procmsg_add_cache_queue(dest, dest->last_num, msginfo); | ||
| 655 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 656 | dest->new++; | ||
| 657 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 658 | dest->unread++; | ||
| 659 | } | ||
| 660 | |||
| 661 | if (fp) | ||
| 662 | fclose(fp); | ||
| 663 | |||
| 664 | if (first) | ||
| 665 | *first = first_; | ||
| 666 | |||
| 667 | if (remove_source) { | ||
| 668 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 669 | msginfo = (MsgInfo *)cur->data; | ||
| 670 | srcfile = procmsg_get_message_file(msginfo); | ||
| 671 | if (g_unlink(srcfile) < 0) | ||
| 672 | FILE_OP_ERROR(srcfile, "unlink"); | ||
| 673 | g_free(srcfile); | ||
| 674 | } | ||
| 675 | } | ||
| 676 | |||
| 677 | S_UNLOCK(mh); | ||
| 678 | return dest->last_num; | ||
| 679 | } | ||
| 680 | |||
| 681 | |||
| 682 | static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist) | ||
| 683 | { | ||
| 684 | FolderItem *src; | ||
| 685 | gchar *srcfile; | ||
| 686 | gchar *destfile; | ||
| 687 | GSList *cur; | ||
| 688 | MsgInfo *msginfo; | ||
| 689 | |||
| 690 | g_return_val_if_fail(dest != NULL, -1); | ||
| 691 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 692 | |||
| 693 | if (dest->last_num < 0) { | ||
| 694 | mh_scan_folder(folder, dest); | ||
| 695 | if (dest->last_num < 0) return -1; | ||
| 696 | } | ||
| 697 | |||
| 698 | S_LOCK(mh); | ||
| 699 | |||
| 700 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 701 | msginfo = (MsgInfo *)cur->data; | ||
| 702 | src = msginfo->folder; | ||
| 703 | |||
| 704 | if (src == dest) { | ||
| 705 | g_warning(_("the src folder is identical to the dest.\n")); | ||
| 706 | continue; | ||
| 707 | } | ||
| 708 | debug_print("Moving message %s/%d to %s ...\n", | ||
| 709 | src->path, msginfo->msgnum, dest->path); | ||
| 710 | |||
| 711 | destfile = mh_get_new_msg_filename(dest); | ||
| 712 | if (!destfile) break; | ||
| 713 | srcfile = procmsg_get_message_file(msginfo); | ||
| 714 | |||
| 715 | /* g_signal_emit_by_name(syl_app_get(), "remove-msg", src, srcfile, msginfo->msgnum); */ | ||
| 716 | |||
| 717 | if (move_file(srcfile, destfile, FALSE) < 0) { | ||
| 718 | g_free(srcfile); | ||
| 719 | g_free(destfile); | ||
| 720 | break; | ||
| 721 | } | ||
| 722 | |||
| 723 | if (syl_app_get()) { | ||
| 724 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1); | ||
| 725 | g_signal_emit_by_name(syl_app_get(), "remove-msg", src, srcfile, msginfo->msgnum); | ||
| 726 | } | ||
| 727 | |||
| 728 | g_free(srcfile); | ||
| 729 | g_free(destfile); | ||
| 730 | src->total--; | ||
| 731 | src->updated = TRUE; | ||
| 732 | src->mtime = 0; | ||
| 733 | dest->last_num++; | ||
| 734 | dest->total++; | ||
| 735 | dest->updated = TRUE; | ||
| 736 | dest->mtime = 0; | ||
| 737 | |||
| 738 | SET_DEST_MSG_FLAGS(NULL, dest, dest->last_num, msginfo->flags); | ||
| 739 | procmsg_add_cache_queue(dest, dest->last_num, msginfo); | ||
| 740 | |||
| 741 | if (MSG_IS_NEW(msginfo->flags)) { | ||
| 742 | src->new--; | ||
| 743 | dest->new++; | ||
| 744 | } | ||
| 745 | if (MSG_IS_UNREAD(msginfo->flags)) { | ||
| 746 | src->unread--; | ||
| 747 | dest->unread++; | ||
| 748 | } | ||
| 749 | |||
| 750 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID); | ||
| 751 | } | ||
| 752 | |||
| 753 | if (!dest->opened) { | ||
| 754 | procmsg_flush_mark_queue(dest, NULL); | ||
| 755 | procmsg_flush_cache_queue(dest, NULL); | ||
| 756 | } | ||
| 757 | |||
| 758 | S_UNLOCK(mh); | ||
| 759 | return dest->last_num; | ||
| 760 | } | ||
| 761 | |||
| 762 | static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) | ||
| 763 | { | ||
| 764 | GSList msglist; | ||
| 765 | |||
| 766 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 767 | |||
| 768 | msglist.data = msginfo; | ||
| 769 | msglist.next = NULL; | ||
| 770 | |||
| 771 | return mh_move_msgs(folder, dest, &msglist); | ||
| 772 | } | ||
| 773 | |||
| 774 | static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist) | ||
| 775 | { | ||
| 776 | MsgInfo *msginfo; | ||
| 777 | gint ret = 0; | ||
| 778 | gint first; | ||
| 779 | |||
| 780 | msginfo = (MsgInfo *)msglist->data; | ||
| 781 | if (folder == msginfo->folder->folder) | ||
| 782 | return mh_do_move_msgs(folder, dest, msglist); | ||
| 783 | |||
| 784 | ret = mh_add_msgs_msginfo(folder, dest, msglist, FALSE, &first); | ||
| 785 | |||
| 786 | if (ret != -1) | ||
| 787 | ret = folder_item_remove_msgs(msginfo->folder, msglist); | ||
| 788 | |||
| 789 | return ret; | ||
| 790 | } | ||
| 791 | |||
| 792 | static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) | ||
| 793 | { | ||
| 794 | GSList msglist; | ||
| 795 | |||
| 796 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 797 | |||
| 798 | msglist.data = msginfo; | ||
| 799 | msglist.next = NULL; | ||
| 800 | |||
| 801 | return mh_copy_msgs(folder, dest, &msglist); | ||
| 802 | } | ||
| 803 | |||
| 804 | static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist) | ||
| 805 | { | ||
| 806 | gchar *srcfile; | ||
| 807 | gchar *destfile; | ||
| 808 | GSList *cur; | ||
| 809 | MsgInfo *msginfo; | ||
| 810 | |||
| 811 | g_return_val_if_fail(dest != NULL, -1); | ||
| 812 | g_return_val_if_fail(msglist != NULL, -1); | ||
| 813 | |||
| 814 | if (dest->last_num < 0) { | ||
| 815 | mh_scan_folder(folder, dest); | ||
| 816 | if (dest->last_num < 0) return -1; | ||
| 817 | } | ||
| 818 | |||
| 819 | S_LOCK(mh); | ||
| 820 | |||
| 821 | for (cur = msglist; cur != NULL; cur = cur->next) { | ||
| 822 | msginfo = (MsgInfo *)cur->data; | ||
| 823 | |||
| 824 | if (msginfo->folder == dest) { | ||
| 825 | g_warning(_("the src folder is identical to the dest.\n")); | ||
| 826 | continue; | ||
| 827 | } | ||
| 828 | debug_print(_("Copying message %s/%d to %s ...\n"), | ||
| 829 | msginfo->folder->path, msginfo->msgnum, dest->path); | ||
| 830 | |||
| 831 | destfile = mh_get_new_msg_filename(dest); | ||
| 832 | if (!destfile) break; | ||
| 833 | srcfile = procmsg_get_message_file(msginfo); | ||
| 834 | |||
| 835 | if (copy_file(srcfile, destfile, TRUE) < 0) { | ||
| 836 | FILE_OP_ERROR(srcfile, "copy"); | ||
| 837 | g_free(srcfile); | ||
| 838 | g_free(destfile); | ||
| 839 | break; | ||
| 840 | } | ||
| 841 | |||
| 842 | if (syl_app_get()) | ||
| 843 | g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1); | ||
| 844 | |||
| 845 | g_free(srcfile); | ||
| 846 | g_free(destfile); | ||
| 847 | dest->last_num++; | ||
| 848 | dest->total++; | ||
| 849 | dest->updated = TRUE; | ||
| 850 | dest->mtime = 0; | ||
| 851 | |||
| 852 | SET_DEST_MSG_FLAGS(NULL, dest, dest->last_num, msginfo->flags); | ||
| 853 | procmsg_add_cache_queue(dest, dest->last_num, msginfo); | ||
| 854 | |||
| 855 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 856 | dest->new++; | ||
| 857 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 858 | dest->unread++; | ||
| 859 | } | ||
| 860 | |||
| 861 | if (!dest->opened) { | ||
| 862 | procmsg_flush_mark_queue(dest, NULL); | ||
| 863 | procmsg_flush_cache_queue(dest, NULL); | ||
| 864 | } | ||
| 865 | |||
| 866 | S_UNLOCK(mh); | ||
| 867 | return dest->last_num; | ||
| 868 | } | ||
| 869 | |||
| 870 | static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo) | ||
| 871 | { | ||
| 872 | gchar *file; | ||
| 873 | |||
| 874 | g_return_val_if_fail(item != NULL, -1); | ||
| 875 | |||
| 876 | file = mh_fetch_msg(folder, item, msginfo->msgnum); | ||
| 877 | g_return_val_if_fail(file != NULL, -1); | ||
| 878 | |||
| 879 | if (syl_app_get()) | ||
| 880 | g_signal_emit_by_name(syl_app_get(), "remove-msg", item, file, msginfo->msgnum); | ||
| 881 | |||
| 882 | S_LOCK(mh); | ||
| 883 | |||
| 884 | if (g_unlink(file) < 0) { | ||
| 885 | FILE_OP_ERROR(file, "unlink"); | ||
| 886 | g_free(file); | ||
| 887 | S_UNLOCK(mh); | ||
| 888 | return -1; | ||
| 889 | } | ||
| 890 | g_free(file); | ||
| 891 | |||
| 892 | item->total--; | ||
| 893 | item->updated = TRUE; | ||
| 894 | item->mtime = 0; | ||
| 895 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 896 | item->new--; | ||
| 897 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 898 | item->unread--; | ||
| 899 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID); | ||
| 900 | |||
| 901 | S_UNLOCK(mh); | ||
| 902 | |||
| 903 | if (msginfo->msgnum == item->last_num) | ||
| 904 | mh_scan_folder_full(folder, item, FALSE); | ||
| 905 | |||
| 906 | return 0; | ||
| 907 | } | ||
| 908 | |||
| 909 | static gint mh_remove_all_msg(Folder *folder, FolderItem *item) | ||
| 910 | { | ||
| 911 | gchar *path; | ||
| 912 | gint val; | ||
| 913 | |||
| 914 | g_return_val_if_fail(item != NULL, -1); | ||
| 915 | |||
| 916 | path = folder_item_get_path(item); | ||
| 917 | g_return_val_if_fail(path != NULL, -1); | ||
| 918 | if (syl_app_get()) | ||
| 919 | g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item); | ||
| 920 | |||
| 921 | S_LOCK(mh); | ||
| 922 | |||
| 923 | val = remove_all_numbered_files(path); | ||
| 924 | g_free(path); | ||
| 925 | if (val == 0) { | ||
| 926 | item->new = item->unread = item->total = 0; | ||
| 927 | item->last_num = 0; | ||
| 928 | item->updated = TRUE; | ||
| 929 | item->mtime = 0; | ||
| 930 | } | ||
| 931 | |||
| 932 | S_UNLOCK(mh); | ||
| 933 | |||
| 934 | return val; | ||
| 935 | } | ||
| 936 | |||
| 937 | static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item, | ||
| 938 | MsgInfo *msginfo) | ||
| 939 | { | ||
| 940 | GStatBuf s; | ||
| 941 | gchar buf[16]; | ||
| 942 | |||
| 943 | if (g_stat(utos_buf(buf, msginfo->msgnum), &s) < 0 || | ||
| 944 | msginfo->size != s.st_size || | ||
| 945 | msginfo->mtime != s.st_mtime) | ||
| 946 | return TRUE; | ||
| 947 | |||
| 948 | return FALSE; | ||
| 949 | } | ||
| 950 | |||
| 951 | static gint mh_close(Folder *folder, FolderItem *item) | ||
| 952 | { | ||
| 953 | return 0; | ||
| 954 | } | ||
| 955 | |||
| 956 | #ifdef G_OS_WIN32 | ||
| 957 | struct wfddata { | ||
| 958 | WIN32_FIND_DATAA wfda; | ||
| 959 | WIN32_FIND_DATAW wfdw; | ||
| 960 | DWORD file_attr; | ||
| 961 | gchar *file_name; | ||
| 962 | }; | ||
| 963 | |||
| 964 | static HANDLE find_first_file(const gchar *path, struct wfddata *wfd) | ||
| 965 | { | ||
| 966 | HANDLE hfind; | ||
| 967 | |||
| 968 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 969 | if (path) { | ||
| 970 | gchar *wildcard_path; | ||
| 971 | wchar_t *wpath; | ||
| 972 | |||
| 973 | wildcard_path = g_strconcat(path, "\\*", NULL); | ||
| 974 | wpath = g_utf8_to_utf16(wildcard_path, -1, | ||
| 975 | NULL, NULL, NULL); | ||
| 976 | if (wpath) { | ||
| 977 | hfind = FindFirstFileW(wpath, &wfd->wfdw); | ||
| 978 | g_free(wpath); | ||
| 979 | } else | ||
| 980 | hfind = INVALID_HANDLE_VALUE; | ||
| 981 | g_free(wildcard_path); | ||
| 982 | } else | ||
| 983 | hfind = FindFirstFileW(L"*", &wfd->wfdw); | ||
| 984 | |||
| 985 | if (hfind != INVALID_HANDLE_VALUE) { | ||
| 986 | wfd->file_attr = wfd->wfdw.dwFileAttributes; | ||
| 987 | wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1, | ||
| 988 | NULL, NULL, NULL); | ||
| 989 | } | ||
| 990 | } else { | ||
| 991 | if (path) { | ||
| 992 | gchar *wildcard_path; | ||
| 993 | gchar *cp_path; | ||
| 994 | |||
| 995 | wildcard_path = g_strconcat(path, "\\*", NULL); | ||
| 996 | cp_path = g_locale_from_utf8(wildcard_path, -1, | ||
| 997 | NULL, NULL, NULL); | ||
| 998 | if (cp_path) { | ||
| 999 | hfind = FindFirstFileA(cp_path, &wfd->wfda); | ||
| 1000 | g_free(cp_path); | ||
| 1001 | } else | ||
| 1002 | hfind = INVALID_HANDLE_VALUE; | ||
| 1003 | g_free(wildcard_path); | ||
| 1004 | } else | ||
| 1005 | hfind = FindFirstFileA("*", &wfd->wfda); | ||
| 1006 | |||
| 1007 | if (hfind != INVALID_HANDLE_VALUE) { | ||
| 1008 | wfd->file_attr = wfd->wfda.dwFileAttributes; | ||
| 1009 | wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName, | ||
| 1010 | -1, NULL, NULL, NULL); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | return hfind; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static BOOL find_next_file(HANDLE hfind, struct wfddata *wfd) | ||
| 1018 | { | ||
| 1019 | BOOL retval; | ||
| 1020 | |||
| 1021 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 1022 | retval = FindNextFileW(hfind, &wfd->wfdw); | ||
| 1023 | if (retval) { | ||
| 1024 | wfd->file_attr = wfd->wfdw.dwFileAttributes; | ||
| 1025 | wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1, | ||
| 1026 | NULL, NULL, NULL); | ||
| 1027 | } | ||
| 1028 | } else { | ||
| 1029 | retval = FindNextFileA(hfind, &wfd->wfda); | ||
| 1030 | if (retval) { | ||
| 1031 | wfd->file_attr = wfd->wfda.dwFileAttributes; | ||
| 1032 | wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName, | ||
| 1033 | -1, NULL, NULL, NULL); | ||
| 1034 | } | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | return retval; | ||
| 1038 | } | ||
| 1039 | #endif | ||
| 1040 | |||
| 1041 | static gint mh_scan_folder_full(Folder *folder, FolderItem *item, | ||
| 1042 | gboolean count_sum) | ||
| 1043 | { | ||
| 1044 | gchar *path; | ||
| 1045 | #ifdef G_OS_WIN32 | ||
| 1046 | struct wfddata wfd; | ||
| 1047 | HANDLE hfind; | ||
| 1048 | #else | ||
| 1049 | DIR *dp; | ||
| 1050 | struct dirent *d; | ||
| 1051 | #endif | ||
| 1052 | gint max = 0; | ||
| 1053 | gint num; | ||
| 1054 | gint n_msg = 0; | ||
| 1055 | |||
| 1056 | g_return_val_if_fail(item != NULL, -1); | ||
| 1057 | |||
| 1058 | debug_print("mh_scan_folder(): Scanning %s ...\n", item->path); | ||
| 1059 | |||
| 1060 | S_LOCK(mh); | ||
| 1061 | |||
| 1062 | path = folder_item_get_path(item); | ||
| 1063 | if (!path) { | ||
| 1064 | S_UNLOCK(mh); | ||
| 1065 | return -1; | ||
| 1066 | } | ||
| 1067 | if (change_dir(path) < 0) { | ||
| 1068 | g_free(path); | ||
| 1069 | S_UNLOCK(mh); | ||
| 1070 | return -1; | ||
| 1071 | } | ||
| 1072 | g_free(path); | ||
| 1073 | |||
| 1074 | #ifdef G_OS_WIN32 | ||
| 1075 | if ((hfind = find_first_file(NULL, &wfd)) == INVALID_HANDLE_VALUE) { | ||
| 1076 | g_warning("failed to open directory\n"); | ||
| 1077 | #else | ||
| 1078 | if ((dp = opendir(".")) == NULL) { | ||
| 1079 | FILE_OP_ERROR(item->path, "opendir"); | ||
| 1080 | #endif | ||
| 1081 | S_UNLOCK(mh); | ||
| 1082 | return -1; | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | if (folder->ui_func) | ||
| 1086 | folder->ui_func(folder, item, folder->ui_func_data); | ||
| 1087 | |||
| 1088 | #ifdef G_OS_WIN32 | ||
| 1089 | do { | ||
| 1090 | if ((wfd.file_attr & | ||
| 1091 | (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) { | ||
| 1092 | if (wfd.file_name) { | ||
| 1093 | if ((num = to_number(wfd.file_name)) > 0) { | ||
| 1094 | n_msg++; | ||
| 1095 | if (max < num) | ||
| 1096 | max = num; | ||
| 1097 | } | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | if (wfd.file_name) { | ||
| 1102 | g_free(wfd.file_name); | ||
| 1103 | wfd.file_name = NULL; | ||
| 1104 | } | ||
| 1105 | } while (find_next_file(hfind, &wfd)); | ||
| 1106 | |||
| 1107 | FindClose(hfind); | ||
| 1108 | #else | ||
| 1109 | while ((d = readdir(dp)) != NULL) { | ||
| 1110 | if ((num = to_number(d->d_name)) > 0 && | ||
| 1111 | dirent_is_regular_file(d)) { | ||
| 1112 | n_msg++; | ||
| 1113 | if (max < num) | ||
| 1114 | max = num; | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | closedir(dp); | ||
| 1119 | #endif | ||
| 1120 | |||
| 1121 | if (n_msg == 0) | ||
| 1122 | item->new = item->unread = item->total = 0; | ||
| 1123 | else if (count_sum) { | ||
| 1124 | gint new, unread, total, min, max_; | ||
| 1125 | |||
| 1126 | procmsg_get_mark_sum | ||
| 1127 | (item, &new, &unread, &total, &min, &max_, 0); | ||
| 1128 | |||
| 1129 | if (n_msg > total) { | ||
| 1130 | item->unmarked_num = new = n_msg - total; | ||
| 1131 | unread += n_msg - total; | ||
| 1132 | } else | ||
| 1133 | item->unmarked_num = 0; | ||
| 1134 | |||
| 1135 | item->new = new; | ||
| 1136 | item->unread = unread; | ||
| 1137 | item->total = n_msg; | ||
| 1138 | |||
| 1139 | if (item->cache_queue && !item->opened) { | ||
| 1140 | procmsg_flush_cache_queue(item, NULL); | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | item->updated = TRUE; | ||
| 1145 | item->mtime = 0; | ||
| 1146 | |||
| 1147 | debug_print("Last number in dir %s = %d\n", item->path, max); | ||
| 1148 | item->last_num = max; | ||
| 1149 | |||
| 1150 | S_UNLOCK(mh); | ||
| 1151 | return 0; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | static gint mh_scan_folder(Folder *folder, FolderItem *item) | ||
| 1155 | { | ||
| 1156 | return mh_scan_folder_full(folder, item, TRUE); | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | static gint mh_scan_tree(Folder *folder) | ||
| 1160 | { | ||
| 1161 | FolderItem *item; | ||
| 1162 | gchar *rootpath; | ||
| 1163 | |||
| 1164 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1165 | |||
| 1166 | S_LOCK(mh); | ||
| 1167 | |||
| 1168 | if (!folder->node) { | ||
| 1169 | item = folder_item_new(folder->name, NULL); | ||
| 1170 | item->folder = folder; | ||
| 1171 | folder->node = item->node = g_node_new(item); | ||
| 1172 | } else | ||
| 1173 | item = FOLDER_ITEM(folder->node->data); | ||
| 1174 | |||
| 1175 | rootpath = folder_item_get_path(item); | ||
| 1176 | if (change_dir(rootpath) < 0) { | ||
| 1177 | g_free(rootpath); | ||
| 1178 | S_UNLOCK(mh); | ||
| 1179 | return -1; | ||
| 1180 | } | ||
| 1181 | g_free(rootpath); | ||
| 1182 | |||
| 1183 | mh_create_tree(folder); | ||
| 1184 | mh_remove_missing_folder_items(folder); | ||
| 1185 | mh_scan_tree_recursive(item); | ||
| 1186 | |||
| 1187 | S_UNLOCK(mh); | ||
| 1188 | return 0; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | #define MAKE_DIR_IF_NOT_EXIST(dir) \ | ||
| 1192 | { \ | ||
| 1193 | if (!is_dir_exist(dir)) { \ | ||
| 1194 | if (is_file_exist(dir)) { \ | ||
| 1195 | g_warning(_("File `%s' already exists.\n" \ | ||
| 1196 | "Can't create folder."), dir); \ | ||
| 1197 | return -1; \ | ||
| 1198 | } \ | ||
| 1199 | if (make_dir(dir) < 0) \ | ||
| 1200 | return -1; \ | ||
| 1201 | } \ | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | #define MAKE_DIR_HIER_IF_NOT_EXIST(dir) \ | ||
| 1205 | { \ | ||
| 1206 | if (!is_dir_exist(dir)) { \ | ||
| 1207 | if (is_file_exist(dir)) { \ | ||
| 1208 | g_warning(_("File `%s' already exists.\n" \ | ||
| 1209 | "Can't create folder."), dir); \ | ||
| 1210 | return -1; \ | ||
| 1211 | } \ | ||
| 1212 | if (make_dir_hier(dir) < 0) \ | ||
| 1213 | return -1; \ | ||
| 1214 | } \ | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | static gint mh_create_tree(Folder *folder) | ||
| 1218 | { | ||
| 1219 | gchar *rootpath; | ||
| 1220 | |||
| 1221 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1222 | |||
| 1223 | CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1); | ||
| 1224 | rootpath = LOCAL_FOLDER(folder)->rootpath; | ||
| 1225 | MAKE_DIR_HIER_IF_NOT_EXIST(rootpath); | ||
| 1226 | CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1); | ||
| 1227 | MAKE_DIR_IF_NOT_EXIST(INBOX_DIR); | ||
| 1228 | MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR); | ||
| 1229 | MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR); | ||
| 1230 | MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR); | ||
| 1231 | MAKE_DIR_IF_NOT_EXIST(TRASH_DIR); | ||
| 1232 | MAKE_DIR_IF_NOT_EXIST(JUNK_DIR); | ||
| 1233 | |||
| 1234 | return 0; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | #undef MAKE_DIR_IF_NOT_EXIST | ||
| 1238 | #undef MAKE_DIR_HIER_IF_NOT_EXIST | ||
| 1239 | |||
| 1240 | static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent, | ||
| 1241 | const gchar *name) | ||
| 1242 | { | ||
| 1243 | gchar *path; | ||
| 1244 | gchar *fs_name; | ||
| 1245 | gchar *fullpath; | ||
| 1246 | FolderItem *new_item; | ||
| 1247 | |||
| 1248 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 1249 | g_return_val_if_fail(parent != NULL, NULL); | ||
| 1250 | g_return_val_if_fail(name != NULL, NULL); | ||
| 1251 | |||
| 1252 | S_LOCK(mh); | ||
| 1253 | |||
| 1254 | path = folder_item_get_path(parent); | ||
| 1255 | fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL); | ||
| 1256 | fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, | ||
| 1257 | fs_name ? fs_name : name, NULL); | ||
| 1258 | g_free(fs_name); | ||
| 1259 | g_free(path); | ||
| 1260 | |||
| 1261 | if (make_dir_hier(fullpath) < 0) { | ||
| 1262 | g_free(fullpath); | ||
| 1263 | S_UNLOCK(mh); | ||
| 1264 | return NULL; | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | g_free(fullpath); | ||
| 1268 | |||
| 1269 | /* path is a logical folder path */ | ||
| 1270 | if (parent->path) | ||
| 1271 | path = g_strconcat(parent->path, "/", name, NULL); | ||
| 1272 | else | ||
| 1273 | path = g_strdup(name); | ||
| 1274 | new_item = folder_item_new(name, path); | ||
| 1275 | folder_item_append(parent, new_item); | ||
| 1276 | g_free(path); | ||
| 1277 | |||
| 1278 | S_UNLOCK(mh); | ||
| 1279 | return new_item; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | static gint mh_move_folder_real(Folder *folder, FolderItem *item, | ||
| 1283 | FolderItem *new_parent, const gchar *name) | ||
| 1284 | { | ||
| 1285 | gchar *rootpath; | ||
| 1286 | gchar *oldpath; | ||
| 1287 | gchar *newpath; | ||
| 1288 | gchar *dirname; | ||
| 1289 | gchar *new_dir; | ||
| 1290 | gchar *name_; | ||
| 1291 | gchar *utf8_name; | ||
| 1292 | gchar *paths[2]; | ||
| 1293 | gchar *old_id, *new_id; | ||
| 1294 | |||
| 1295 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1296 | g_return_val_if_fail(item != NULL, -1); | ||
| 1297 | g_return_val_if_fail(folder == item->folder, -1); | ||
| 1298 | g_return_val_if_fail(item->path != NULL, -1); | ||
| 1299 | g_return_val_if_fail(new_parent != NULL || name != NULL, -1); | ||
| 1300 | if (new_parent) { | ||
| 1301 | g_return_val_if_fail(item != new_parent, -1); | ||
| 1302 | g_return_val_if_fail(item->parent != new_parent, -1); | ||
| 1303 | g_return_val_if_fail(item->folder == new_parent->folder, -1); | ||
| 1304 | if (g_node_is_ancestor(item->node, new_parent->node)) { | ||
| 1305 | g_warning("folder to be moved is ancestor of new parent\n"); | ||
| 1306 | return -1; | ||
| 1307 | } | ||
| 1308 | } | ||
| 1309 | |||
| 1310 | S_LOCK(mh); | ||
| 1311 | |||
| 1312 | oldpath = folder_item_get_path(item); | ||
| 1313 | if (new_parent) { | ||
| 1314 | if (name) { | ||
| 1315 | name_ = g_filename_from_utf8(name, -1, NULL, NULL, | ||
| 1316 | NULL); | ||
| 1317 | if (!name_) | ||
| 1318 | name_ = g_strdup(name); | ||
| 1319 | utf8_name = g_strdup(name); | ||
| 1320 | } else { | ||
| 1321 | name_ = g_path_get_basename(oldpath); | ||
| 1322 | utf8_name = g_filename_to_utf8(name_, -1, NULL, NULL, | ||
| 1323 | NULL); | ||
| 1324 | if (!utf8_name) | ||
| 1325 | utf8_name = g_strdup(name_); | ||
| 1326 | } | ||
| 1327 | new_dir = folder_item_get_path(new_parent); | ||
| 1328 | newpath = g_strconcat(new_dir, G_DIR_SEPARATOR_S, name_, NULL); | ||
| 1329 | g_free(new_dir); | ||
| 1330 | } else { | ||
| 1331 | name_ = g_filename_from_utf8(name, -1, NULL, NULL, NULL); | ||
| 1332 | utf8_name = g_strdup(name); | ||
| 1333 | dirname = g_dirname(oldpath); | ||
| 1334 | newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, | ||
| 1335 | name_ ? name_ : name, NULL); | ||
| 1336 | g_free(dirname); | ||
| 1337 | } | ||
| 1338 | g_free(name_); | ||
| 1339 | |||
| 1340 | if (is_file_entry_exist(newpath)) { | ||
| 1341 | g_warning("%s already exists\n", newpath); | ||
| 1342 | g_free(oldpath); | ||
| 1343 | g_free(newpath); | ||
| 1344 | g_free(utf8_name); | ||
| 1345 | S_UNLOCK(mh); | ||
| 1346 | return -1; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | rootpath = folder_get_path(folder); | ||
| 1350 | if (change_dir(rootpath) < 0) { | ||
| 1351 | g_free(rootpath); | ||
| 1352 | g_free(oldpath); | ||
| 1353 | g_free(newpath); | ||
| 1354 | g_free(utf8_name); | ||
| 1355 | S_UNLOCK(mh); | ||
| 1356 | return -1; | ||
| 1357 | } | ||
| 1358 | g_free(rootpath); | ||
| 1359 | |||
| 1360 | debug_print("mh_move_folder: rename(%s, %s)\n", oldpath, newpath); | ||
| 1361 | |||
| 1362 | if (g_rename(oldpath, newpath) < 0) { | ||
| 1363 | FILE_OP_ERROR(oldpath, "rename"); | ||
| 1364 | g_free(oldpath); | ||
| 1365 | g_free(newpath); | ||
| 1366 | g_free(utf8_name); | ||
| 1367 | S_UNLOCK(mh); | ||
| 1368 | return -1; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | g_free(oldpath); | ||
| 1372 | g_free(newpath); | ||
| 1373 | |||
| 1374 | old_id = folder_item_get_identifier(item); | ||
| 1375 | |||
| 1376 | if (new_parent) { | ||
| 1377 | g_node_unlink(item->node); | ||
| 1378 | g_node_append(new_parent->node, item->node); | ||
| 1379 | item->parent = new_parent; | ||
| 1380 | if (new_parent->path != NULL) { | ||
| 1381 | newpath = g_strconcat(new_parent->path, "/", utf8_name, | ||
| 1382 | NULL); | ||
| 1383 | g_free(utf8_name); | ||
| 1384 | } else | ||
| 1385 | newpath = utf8_name; | ||
| 1386 | } else { | ||
| 1387 | if (strchr(item->path, '/') != NULL) { | ||
| 1388 | dirname = g_dirname(item->path); | ||
| 1389 | newpath = g_strconcat(dirname, "/", utf8_name, NULL); | ||
| 1390 | g_free(dirname); | ||
| 1391 | g_free(utf8_name); | ||
| 1392 | } else | ||
| 1393 | newpath = utf8_name; | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | if (name) { | ||
| 1397 | g_free(item->name); | ||
| 1398 | item->name = g_strdup(name); | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | paths[0] = g_strdup(item->path); | ||
| 1402 | paths[1] = newpath; | ||
| 1403 | g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1404 | mh_rename_folder_func, paths); | ||
| 1405 | |||
| 1406 | g_free(paths[0]); | ||
| 1407 | g_free(paths[1]); | ||
| 1408 | |||
| 1409 | new_id = folder_item_get_identifier(item); | ||
| 1410 | if (syl_app_get()) | ||
| 1411 | g_signal_emit_by_name(syl_app_get(), "move-folder", item, | ||
| 1412 | old_id, new_id); | ||
| 1413 | g_free(new_id); | ||
| 1414 | g_free(old_id); | ||
| 1415 | |||
| 1416 | S_UNLOCK(mh); | ||
| 1417 | return 0; | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | static gint mh_move_folder(Folder *folder, FolderItem *item, | ||
| 1421 | FolderItem *new_parent) | ||
| 1422 | { | ||
| 1423 | return mh_move_folder_real(folder, item, new_parent, NULL); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static gint mh_rename_folder(Folder *folder, FolderItem *item, | ||
| 1427 | const gchar *name) | ||
| 1428 | { | ||
| 1429 | return mh_move_folder_real(folder, item, NULL, name); | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | static gint mh_remove_folder(Folder *folder, FolderItem *item) | ||
| 1433 | { | ||
| 1434 | gchar *path; | ||
| 1435 | |||
| 1436 | g_return_val_if_fail(folder != NULL, -1); | ||
| 1437 | g_return_val_if_fail(item != NULL, -1); | ||
| 1438 | g_return_val_if_fail(item->path != NULL, -1); | ||
| 1439 | |||
| 1440 | S_LOCK(mh); | ||
| 1441 | |||
| 1442 | path = folder_item_get_path(item); | ||
| 1443 | if (remove_dir_recursive(path) < 0) { | ||
| 1444 | g_warning("can't remove directory `%s'\n", path); | ||
| 1445 | g_free(path); | ||
| 1446 | S_UNLOCK(mh); | ||
| 1447 | return -1; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | g_free(path); | ||
| 1451 | if (syl_app_get()) | ||
| 1452 | g_signal_emit_by_name(syl_app_get(), "remove-folder", item); | ||
| 1453 | folder_item_remove(item); | ||
| 1454 | |||
| 1455 | S_UNLOCK(mh); | ||
| 1456 | return 0; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | |||
| 1460 | static time_t mh_get_mtime(FolderItem *item) | ||
| 1461 | { | ||
| 1462 | gchar *path; | ||
| 1463 | GStatBuf s; | ||
| 1464 | |||
| 1465 | path = folder_item_get_path(item); | ||
| 1466 | if (g_stat(path, &s) < 0) { | ||
| 1467 | FILE_OP_ERROR(path, "stat"); | ||
| 1468 | g_free(path); | ||
| 1469 | return -1; | ||
| 1470 | } else { | ||
| 1471 | g_free(path); | ||
| 1472 | return MAX(s.st_mtime, s.st_ctime); | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | |||
| 1476 | static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item) | ||
| 1477 | { | ||
| 1478 | gchar *path; | ||
| 1479 | GDir *dp; | ||
| 1480 | const gchar *dir_name; | ||
| 1481 | GSList *newlist = NULL; | ||
| 1482 | GSList *last = NULL; | ||
| 1483 | MsgInfo *msginfo; | ||
| 1484 | gint n_newmsg = 0; | ||
| 1485 | gint num; | ||
| 1486 | Folder *folder; | ||
| 1487 | |||
| 1488 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1489 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 1490 | |||
| 1491 | folder = item->folder; | ||
| 1492 | |||
| 1493 | path = folder_item_get_path(item); | ||
| 1494 | g_return_val_if_fail(path != NULL, NULL); | ||
| 1495 | if (change_dir(path) < 0) { | ||
| 1496 | g_free(path); | ||
| 1497 | return NULL; | ||
| 1498 | } | ||
| 1499 | g_free(path); | ||
| 1500 | |||
| 1501 | if ((dp = g_dir_open(".", 0, NULL)) == NULL) { | ||
| 1502 | FILE_OP_ERROR(item->path, "opendir"); | ||
| 1503 | return NULL; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | debug_print("Searching uncached messages...\n"); | ||
| 1507 | |||
| 1508 | if (msg_table) { | ||
| 1509 | gint count = 0; | ||
| 1510 | |||
| 1511 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 1512 | if ((num = to_number(dir_name)) <= 0) continue; | ||
| 1513 | |||
| 1514 | msginfo = g_hash_table_lookup | ||
| 1515 | (msg_table, GUINT_TO_POINTER(num)); | ||
| 1516 | |||
| 1517 | if (msginfo) { | ||
| 1518 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_CACHED); | ||
| 1519 | } else { | ||
| 1520 | /* not found in the cache (uncached message) */ | ||
| 1521 | msginfo = mh_parse_msg(dir_name, item); | ||
| 1522 | if (!msginfo) continue; | ||
| 1523 | |||
| 1524 | if (!newlist) | ||
| 1525 | last = newlist = | ||
| 1526 | g_slist_append(NULL, msginfo); | ||
| 1527 | else { | ||
| 1528 | last = g_slist_append(last, msginfo); | ||
| 1529 | last = last->next; | ||
| 1530 | } | ||
| 1531 | n_newmsg++; | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | count++; | ||
| 1535 | if (folder->ui_func) | ||
| 1536 | folder->ui_func(folder, item, folder->ui_func_data ? folder->ui_func_data : GINT_TO_POINTER(count)); | ||
| 1537 | } | ||
| 1538 | } else { | ||
| 1539 | /* discard all previous cache */ | ||
| 1540 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 1541 | if (to_number(dir_name) <= 0) continue; | ||
| 1542 | |||
| 1543 | msginfo = mh_parse_msg(dir_name, item); | ||
| 1544 | if (!msginfo) continue; | ||
| 1545 | |||
| 1546 | if (!newlist) | ||
| 1547 | last = newlist = g_slist_append(NULL, msginfo); | ||
| 1548 | else { | ||
| 1549 | last = g_slist_append(last, msginfo); | ||
| 1550 | last = last->next; | ||
| 1551 | } | ||
| 1552 | n_newmsg++; | ||
| 1553 | if (folder->ui_func) | ||
| 1554 | folder->ui_func(folder, item, folder->ui_func_data ? folder->ui_func_data : GINT_TO_POINTER(n_newmsg)); | ||
| 1555 | } | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | g_dir_close(dp); | ||
| 1559 | |||
| 1560 | if (n_newmsg) | ||
| 1561 | debug_print("%d uncached message(s) found.\n", n_newmsg); | ||
| 1562 | else | ||
| 1563 | debug_print("done.\n"); | ||
| 1564 | |||
| 1565 | /* sort new messages in numerical order */ | ||
| 1566 | if (newlist && item->sort_key == SORT_BY_NONE) { | ||
| 1567 | debug_print("Sorting uncached messages in numerical order...\n"); | ||
| 1568 | newlist = g_slist_sort | ||
| 1569 | (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort); | ||
| 1570 | debug_print("done.\n"); | ||
| 1571 | } | ||
| 1572 | |||
| 1573 | return newlist; | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item) | ||
| 1577 | { | ||
| 1578 | MsgInfo *msginfo; | ||
| 1579 | MsgFlags flags; | ||
| 1580 | |||
| 1581 | g_return_val_if_fail(item != NULL, NULL); | ||
| 1582 | g_return_val_if_fail(file != NULL, NULL); | ||
| 1583 | |||
| 1584 | flags.perm_flags = MSG_NEW|MSG_UNREAD; | ||
| 1585 | flags.tmp_flags = 0; | ||
| 1586 | |||
| 1587 | if (item->stype == F_QUEUE) { | ||
| 1588 | MSG_SET_TMP_FLAGS(flags, MSG_QUEUED); | ||
| 1589 | } else if (item->stype == F_DRAFT) { | ||
| 1590 | MSG_SET_TMP_FLAGS(flags, MSG_DRAFT); | ||
| 1591 | } | ||
| 1592 | |||
| 1593 | msginfo = procheader_parse_file(file, flags, FALSE); | ||
| 1594 | if (!msginfo) return NULL; | ||
| 1595 | |||
| 1596 | msginfo->msgnum = atoi(file); | ||
| 1597 | msginfo->folder = item; | ||
| 1598 | |||
| 1599 | return msginfo; | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | #if 0 | ||
| 1603 | static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir) | ||
| 1604 | { | ||
| 1605 | gchar *entry; | ||
| 1606 | gboolean result; | ||
| 1607 | |||
| 1608 | entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL); | ||
| 1609 | result = is_dir_exist(entry); | ||
| 1610 | g_free(entry); | ||
| 1611 | |||
| 1612 | return result; | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | /* | ||
| 1616 | * check whether PATH is a Maildir style mailbox. | ||
| 1617 | * This is the case if the 3 subdir: new, cur, tmp are existing. | ||
| 1618 | * This functon assumes that entry is an directory | ||
| 1619 | */ | ||
| 1620 | static gboolean mh_is_maildir(const gchar *path) | ||
| 1621 | { | ||
| 1622 | return mh_is_maildir_one(path, "new") && | ||
| 1623 | mh_is_maildir_one(path, "cur") && | ||
| 1624 | mh_is_maildir_one(path, "tmp"); | ||
| 1625 | } | ||
| 1626 | #endif | ||
| 1627 | |||
| 1628 | static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data) | ||
| 1629 | { | ||
| 1630 | FolderItem *item; | ||
| 1631 | gchar *path; | ||
| 1632 | |||
| 1633 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 1634 | |||
| 1635 | if (G_NODE_IS_ROOT(node)) | ||
| 1636 | return FALSE; | ||
| 1637 | |||
| 1638 | item = FOLDER_ITEM(node->data); | ||
| 1639 | |||
| 1640 | #if 0 | ||
| 1641 | if (item->path && strchr(item->path, '/')) { | ||
| 1642 | debug_print("folder '%s' includes Unix path separator. removing...\n", item->path); | ||
| 1643 | folder_item_remove(item); | ||
| 1644 | return FALSE; | ||
| 1645 | } | ||
| 1646 | #endif | ||
| 1647 | |||
| 1648 | path = folder_item_get_path(item); | ||
| 1649 | if (!is_dir_exist(path)) { | ||
| 1650 | debug_print("folder '%s' not found. removing...\n", path); | ||
| 1651 | folder_item_remove(item); | ||
| 1652 | } | ||
| 1653 | g_free(path); | ||
| 1654 | |||
| 1655 | return FALSE; | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | static void mh_remove_missing_folder_items(Folder *folder) | ||
| 1659 | { | ||
| 1660 | g_return_if_fail(folder != NULL); | ||
| 1661 | |||
| 1662 | debug_print("searching missing folders...\n"); | ||
| 1663 | |||
| 1664 | g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1665 | mh_remove_missing_folder_items_func, folder); | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | #define MAX_RECURSION_LEVEL 64 | ||
| 1669 | |||
| 1670 | static void mh_scan_tree_recursive(FolderItem *item) | ||
| 1671 | { | ||
| 1672 | Folder *folder; | ||
| 1673 | #ifdef G_OS_WIN32 | ||
| 1674 | struct wfddata wfd; | ||
| 1675 | HANDLE hfind; | ||
| 1676 | #else | ||
| 1677 | DIR *dp; | ||
| 1678 | struct dirent *d; | ||
| 1679 | GStatBuf s; | ||
| 1680 | #endif | ||
| 1681 | const gchar *dir_name; | ||
| 1682 | gchar *fs_path; | ||
| 1683 | gchar *entry; | ||
| 1684 | gchar *utf8entry; | ||
| 1685 | gchar *utf8name; | ||
| 1686 | gint n_msg = 0; | ||
| 1687 | |||
| 1688 | g_return_if_fail(item != NULL); | ||
| 1689 | g_return_if_fail(item->folder != NULL); | ||
| 1690 | |||
| 1691 | if (item->stype == F_VIRTUAL) | ||
| 1692 | return; | ||
| 1693 | |||
| 1694 | folder = item->folder; | ||
| 1695 | |||
| 1696 | if (g_node_depth(item->node) >= MAX_RECURSION_LEVEL) { | ||
| 1697 | g_warning("mh_scan_tree_recursive(): max recursion level (%u) reached.", MAX_RECURSION_LEVEL); | ||
| 1698 | return; | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | debug_print("scanning %s ...\n", | ||
| 1702 | item->path ? item->path : LOCAL_FOLDER(folder)->rootpath); | ||
| 1703 | if (folder->ui_func) | ||
| 1704 | folder->ui_func(folder, item, folder->ui_func_data); | ||
| 1705 | |||
| 1706 | fs_path = item->path ? | ||
| 1707 | g_filename_from_utf8(item->path, -1, NULL, NULL, NULL) | ||
| 1708 | : g_strdup("."); | ||
| 1709 | if (!fs_path) | ||
| 1710 | fs_path = g_strdup(item->path); | ||
| 1711 | #ifdef G_OS_WIN32 | ||
| 1712 | hfind = find_first_file(fs_path, &wfd); | ||
| 1713 | if (hfind == INVALID_HANDLE_VALUE) { | ||
| 1714 | g_warning("failed to open directory: %s\n", fs_path); | ||
| 1715 | g_free(fs_path); | ||
| 1716 | return; | ||
| 1717 | } | ||
| 1718 | #else | ||
| 1719 | dp = opendir(fs_path); | ||
| 1720 | if (!dp) { | ||
| 1721 | FILE_OP_ERROR(fs_path, "opendir"); | ||
| 1722 | g_free(fs_path); | ||
| 1723 | return; | ||
| 1724 | } | ||
| 1725 | #endif | ||
| 1726 | g_free(fs_path); | ||
| 1727 | |||
| 1728 | #ifdef G_OS_WIN32 | ||
| 1729 | do { | ||
| 1730 | if (!wfd.file_name) continue; | ||
| 1731 | if (wfd.file_name[0] == '.') { | ||
| 1732 | g_free(wfd.file_name); | ||
| 1733 | wfd.file_name = NULL; | ||
| 1734 | continue; | ||
| 1735 | } | ||
| 1736 | dir_name = utf8name = wfd.file_name; | ||
| 1737 | wfd.file_name = NULL; | ||
| 1738 | #else | ||
| 1739 | while ((d = readdir(dp)) != NULL) { | ||
| 1740 | dir_name = d->d_name; | ||
| 1741 | if (dir_name[0] == '.') continue; | ||
| 1742 | |||
| 1743 | utf8name = g_filename_to_utf8(dir_name, -1, NULL, NULL, NULL); | ||
| 1744 | if (!utf8name) | ||
| 1745 | utf8name = g_strdup(dir_name); | ||
| 1746 | #endif | ||
| 1747 | |||
| 1748 | if (item->path) { | ||
| 1749 | utf8entry = g_strconcat(item->path, "/", utf8name, | ||
| 1750 | NULL); | ||
| 1751 | } else | ||
| 1752 | utf8entry = g_strdup(utf8name); | ||
| 1753 | entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL); | ||
| 1754 | if (!entry) | ||
| 1755 | entry = g_strdup(utf8entry); | ||
| 1756 | #ifdef G_OS_WIN32 | ||
| 1757 | subst_char(entry, '/', G_DIR_SEPARATOR); | ||
| 1758 | #endif | ||
| 1759 | |||
| 1760 | if ( | ||
| 1761 | #ifdef G_OS_WIN32 | ||
| 1762 | (wfd.file_attr & FILE_ATTRIBUTE_DIRECTORY) != 0 | ||
| 1763 | #else | ||
| 1764 | #if HAVE_DIRENT_D_TYPE | ||
| 1765 | d->d_type == DT_DIR || | ||
| 1766 | ((d->d_type == DT_UNKNOWN || d->d_type == DT_LNK) && | ||
| 1767 | #endif | ||
| 1768 | g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode) | ||
| 1769 | #if HAVE_DIRENT_D_TYPE | ||
| 1770 | ) | ||
| 1771 | #endif | ||
| 1772 | #endif /* G_OS_WIN32 */ | ||
| 1773 | ) { | ||
| 1774 | FolderItem *new_item = NULL; | ||
| 1775 | GNode *node; | ||
| 1776 | |||
| 1777 | #ifndef G_OS_WIN32 | ||
| 1778 | if (g_utf8_validate(utf8name, -1, NULL) == FALSE) { | ||
| 1779 | g_warning(_("Directory name\n" | ||
| 1780 | "'%s' is not a valid UTF-8 string.\n" | ||
| 1781 | "Maybe the locale encoding is used for filename.\n" | ||
| 1782 | "If that is the case, you must set the following environmental variable\n" | ||
| 1783 | "(see README for detail):\n" | ||
| 1784 | "\n" | ||
| 1785 | "\tG_FILENAME_ENCODING=@locale\n"), | ||
| 1786 | utf8name); | ||
| 1787 | g_free(entry); | ||
| 1788 | g_free(utf8entry); | ||
| 1789 | g_free(utf8name); | ||
| 1790 | continue; | ||
| 1791 | } | ||
| 1792 | #endif /* G_OS_WIN32 */ | ||
| 1793 | |||
| 1794 | node = item->node; | ||
| 1795 | for (node = node->children; node != NULL; node = node->next) { | ||
| 1796 | FolderItem *cur_item = FOLDER_ITEM(node->data); | ||
| 1797 | if (!strcmp2(cur_item->path, utf8entry)) { | ||
| 1798 | new_item = cur_item; | ||
| 1799 | break; | ||
| 1800 | } | ||
| 1801 | } | ||
| 1802 | if (!new_item) { | ||
| 1803 | debug_print("new folder '%s' found.\n", | ||
| 1804 | utf8entry); | ||
| 1805 | new_item = folder_item_new(utf8name, utf8entry); | ||
| 1806 | folder_item_append(item, new_item); | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | if (!item->path) { | ||
| 1810 | if (!folder->inbox && | ||
| 1811 | !strcmp(dir_name, INBOX_DIR)) { | ||
| 1812 | new_item->stype = F_INBOX; | ||
| 1813 | folder->inbox = new_item; | ||
| 1814 | } else if (!folder->outbox && | ||
| 1815 | !strcmp(dir_name, OUTBOX_DIR)) { | ||
| 1816 | new_item->stype = F_OUTBOX; | ||
| 1817 | folder->outbox = new_item; | ||
| 1818 | } else if (!folder->draft && | ||
| 1819 | !strcmp(dir_name, DRAFT_DIR)) { | ||
| 1820 | new_item->stype = F_DRAFT; | ||
| 1821 | folder->draft = new_item; | ||
| 1822 | } else if (!folder->queue && | ||
| 1823 | !strcmp(dir_name, QUEUE_DIR)) { | ||
| 1824 | new_item->stype = F_QUEUE; | ||
| 1825 | folder->queue = new_item; | ||
| 1826 | } else if (!folder->trash && | ||
| 1827 | !strcmp(dir_name, TRASH_DIR)) { | ||
| 1828 | new_item->stype = F_TRASH; | ||
| 1829 | folder->trash = new_item; | ||
| 1830 | } else if (!folder_get_junk(folder) && | ||
| 1831 | !strcmp(dir_name, JUNK_DIR)) { | ||
| 1832 | new_item->stype = F_JUNK; | ||
| 1833 | folder_set_junk(folder, new_item); | ||
| 1834 | } | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | mh_scan_tree_recursive(new_item); | ||
| 1838 | } else if (to_number(dir_name) > 0) n_msg++; | ||
| 1839 | |||
| 1840 | g_free(entry); | ||
| 1841 | g_free(utf8entry); | ||
| 1842 | g_free(utf8name); | ||
| 1843 | #ifdef G_OS_WIN32 | ||
| 1844 | } while (find_next_file(hfind, &wfd)); | ||
| 1845 | #else | ||
| 1846 | } | ||
| 1847 | #endif | ||
| 1848 | |||
| 1849 | #ifdef G_OS_WIN32 | ||
| 1850 | FindClose(hfind); | ||
| 1851 | #else | ||
| 1852 | closedir(dp); | ||
| 1853 | #endif | ||
| 1854 | |||
| 1855 | if (item->path) { | ||
| 1856 | gint new, unread, total, min, max; | ||
| 1857 | |||
| 1858 | procmsg_get_mark_sum | ||
| 1859 | (item, &new, &unread, &total, &min, &max, 0); | ||
| 1860 | if (n_msg > total) { | ||
| 1861 | new += n_msg - total; | ||
| 1862 | unread += n_msg - total; | ||
| 1863 | } | ||
| 1864 | item->new = new; | ||
| 1865 | item->unread = unread; | ||
| 1866 | item->total = n_msg; | ||
| 1867 | item->updated = TRUE; | ||
| 1868 | item->mtime = 0; | ||
| 1869 | } | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | static gboolean mh_rename_folder_func(GNode *node, gpointer data) | ||
| 1873 | { | ||
| 1874 | FolderItem *item = node->data; | ||
| 1875 | gchar **paths = data; | ||
| 1876 | const gchar *oldpath = paths[0]; | ||
| 1877 | const gchar *newpath = paths[1]; | ||
| 1878 | gchar *base; | ||
| 1879 | gchar *new_itempath; | ||
| 1880 | gint oldpathlen; | ||
| 1881 | |||
| 1882 | oldpathlen = strlen(oldpath); | ||
| 1883 | if (strncmp(oldpath, item->path, oldpathlen) != 0) { | ||
| 1884 | g_warning("path doesn't match: %s, %s\n", oldpath, item->path); | ||
| 1885 | return TRUE; | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | base = item->path + oldpathlen; | ||
| 1889 | while (*base == '/') base++; | ||
| 1890 | if (*base == '\0') | ||
| 1891 | new_itempath = g_strdup(newpath); | ||
| 1892 | else | ||
| 1893 | new_itempath = g_strconcat(newpath, "/", base, NULL); | ||
| 1894 | g_free(item->path); | ||
| 1895 | item->path = new_itempath; | ||
| 1896 | |||
| 1897 | return FALSE; | ||
| 1898 | } | ||
diff --git a/libsylph/mh.h b/libsylph/mh.h new file mode 100644 index 0000000..32cb590 --- /dev/null +++ b/libsylph/mh.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2007 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __MH_H__ | ||
| 21 | #define __MH_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "folder.h" | ||
| 26 | |||
| 27 | typedef struct _MHFolder MHFolder; | ||
| 28 | |||
| 29 | #define MH_FOLDER(obj) ((MHFolder *)obj) | ||
| 30 | |||
| 31 | struct _MHFolder | ||
| 32 | { | ||
| 33 | LocalFolder lfolder; | ||
| 34 | }; | ||
| 35 | |||
| 36 | FolderClass *mh_get_class (void); | ||
| 37 | |||
| 38 | #endif /* __MH_H__ */ | ||
diff --git a/libsylph/news.c b/libsylph/news.c new file mode 100644 index 0000000..ffff9f9 --- /dev/null +++ b/libsylph/news.c | |||
| @@ -0,0 +1,1110 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <dirent.h> | ||
| 32 | #include <unistd.h> | ||
| 33 | #include <time.h> | ||
| 34 | |||
| 35 | #include "news.h" | ||
| 36 | #include "nntp.h" | ||
| 37 | #include "socket.h" | ||
| 38 | #include "recv.h" | ||
| 39 | #include "procmsg.h" | ||
| 40 | #include "procheader.h" | ||
| 41 | #include "folder.h" | ||
| 42 | #include "session.h" | ||
| 43 | #include "codeconv.h" | ||
| 44 | #include "utils.h" | ||
| 45 | #include "prefs_common.h" | ||
| 46 | #include "prefs_account.h" | ||
| 47 | #if USE_SSL | ||
| 48 | # include "ssl.h" | ||
| 49 | #endif | ||
| 50 | #include "socks.h" | ||
| 51 | |||
| 52 | #define NNTP_PORT 119 | ||
| 53 | #if USE_SSL | ||
| 54 | #define NNTPS_PORT 563 | ||
| 55 | #endif | ||
| 56 | |||
| 57 | static void news_folder_init (Folder *folder, | ||
| 58 | const gchar *name, | ||
| 59 | const gchar *path); | ||
| 60 | |||
| 61 | static Folder *news_folder_new (const gchar *name, | ||
| 62 | const gchar *folder); | ||
| 63 | static void news_folder_destroy (Folder *folder); | ||
| 64 | |||
| 65 | static GSList *news_get_article_list (Folder *folder, | ||
| 66 | FolderItem *item, | ||
| 67 | gboolean use_cache); | ||
| 68 | static gchar *news_fetch_msg (Folder *folder, | ||
| 69 | FolderItem *item, | ||
| 70 | gint num); | ||
| 71 | static MsgInfo *news_get_msginfo (Folder *folder, | ||
| 72 | FolderItem *item, | ||
| 73 | gint num); | ||
| 74 | |||
| 75 | static gint news_close (Folder *folder, | ||
| 76 | FolderItem *item); | ||
| 77 | |||
| 78 | static gint news_scan_group (Folder *folder, | ||
| 79 | FolderItem *item); | ||
| 80 | |||
| 81 | #if USE_SSL | ||
| 82 | static Session *news_session_new (const gchar *server, | ||
| 83 | gushort port, | ||
| 84 | SocksInfo *socks_info, | ||
| 85 | const gchar *userid, | ||
| 86 | const gchar *passwd, | ||
| 87 | SSLType ssl_type); | ||
| 88 | #else | ||
| 89 | static Session *news_session_new (const gchar *server, | ||
| 90 | gushort port, | ||
| 91 | SocksInfo *socks_info, | ||
| 92 | const gchar *userid, | ||
| 93 | const gchar *passwd); | ||
| 94 | #endif | ||
| 95 | |||
| 96 | static gint news_get_article_cmd (NNTPSession *session, | ||
| 97 | const gchar *cmd, | ||
| 98 | gint num, | ||
| 99 | gchar *filename); | ||
| 100 | static gint news_get_article (NNTPSession *session, | ||
| 101 | gint num, | ||
| 102 | gchar *filename); | ||
| 103 | #if 0 | ||
| 104 | static gint news_get_header (NNTPSession *session, | ||
| 105 | gint num, | ||
| 106 | gchar *filename); | ||
| 107 | #endif | ||
| 108 | |||
| 109 | static gint news_select_group (NNTPSession *session, | ||
| 110 | const gchar *group, | ||
| 111 | gint *num, | ||
| 112 | gint *first, | ||
| 113 | gint *last); | ||
| 114 | static GSList *news_get_uncached_articles(NNTPSession *session, | ||
| 115 | FolderItem *item, | ||
| 116 | gint cache_last, | ||
| 117 | gint *rfirst, | ||
| 118 | gint *rlast); | ||
| 119 | static MsgInfo *news_parse_xover (const gchar *xover_str); | ||
| 120 | static gchar *news_parse_xhdr (const gchar *xhdr_str, | ||
| 121 | MsgInfo *msginfo); | ||
| 122 | static GSList *news_delete_old_articles (GSList *alist, | ||
| 123 | FolderItem *item, | ||
| 124 | gint first); | ||
| 125 | static void news_delete_all_articles (FolderItem *item); | ||
| 126 | static void news_delete_expired_caches (GSList *alist, | ||
| 127 | FolderItem *item); | ||
| 128 | |||
| 129 | static FolderClass news_class = | ||
| 130 | { | ||
| 131 | F_NEWS, | ||
| 132 | |||
| 133 | news_folder_new, | ||
| 134 | news_folder_destroy, | ||
| 135 | |||
| 136 | NULL, | ||
| 137 | NULL, | ||
| 138 | |||
| 139 | news_get_article_list, | ||
| 140 | NULL, | ||
| 141 | news_fetch_msg, | ||
| 142 | news_get_msginfo, | ||
| 143 | NULL, | ||
| 144 | NULL, | ||
| 145 | NULL, | ||
| 146 | NULL, | ||
| 147 | NULL, | ||
| 148 | NULL, | ||
| 149 | NULL, | ||
| 150 | NULL, | ||
| 151 | NULL, | ||
| 152 | NULL, | ||
| 153 | NULL, | ||
| 154 | NULL, | ||
| 155 | news_close, | ||
| 156 | news_scan_group, | ||
| 157 | |||
| 158 | NULL, | ||
| 159 | NULL, | ||
| 160 | NULL, | ||
| 161 | NULL | ||
| 162 | }; | ||
| 163 | |||
| 164 | |||
| 165 | FolderClass *news_get_class(void) | ||
| 166 | { | ||
| 167 | return &news_class; | ||
| 168 | } | ||
| 169 | |||
| 170 | static Folder *news_folder_new(const gchar *name, const gchar *path) | ||
| 171 | { | ||
| 172 | Folder *folder; | ||
| 173 | |||
| 174 | folder = (Folder *)g_new0(NewsFolder, 1); | ||
| 175 | news_folder_init(folder, name, path); | ||
| 176 | |||
| 177 | return folder; | ||
| 178 | } | ||
| 179 | |||
| 180 | static void news_folder_destroy(Folder *folder) | ||
| 181 | { | ||
| 182 | if (REMOTE_FOLDER(folder)->remove_cache_on_destroy) { | ||
| 183 | gchar *dir; | ||
| 184 | gchar *server; | ||
| 185 | |||
| 186 | dir = folder_get_path(folder); | ||
| 187 | if (is_dir_exist(dir)) | ||
| 188 | remove_dir_recursive(dir); | ||
| 189 | g_free(dir); | ||
| 190 | |||
| 191 | server = uriencode_for_filename(folder->account->nntp_server); | ||
| 192 | dir = g_strconcat(get_news_cache_dir(), G_DIR_SEPARATOR_S, | ||
| 193 | server, NULL); | ||
| 194 | if (is_dir_exist(dir)) | ||
| 195 | g_rmdir(dir); | ||
| 196 | g_free(dir); | ||
| 197 | g_free(server); | ||
| 198 | } | ||
| 199 | |||
| 200 | folder_remote_folder_destroy(REMOTE_FOLDER(folder)); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void news_folder_init(Folder *folder, const gchar *name, | ||
| 204 | const gchar *path) | ||
| 205 | { | ||
| 206 | folder->klass = news_get_class(); | ||
| 207 | folder_remote_folder_init(folder, name, path); | ||
| 208 | } | ||
| 209 | |||
| 210 | #if USE_SSL | ||
| 211 | static Session *news_session_new(const gchar *server, gushort port, | ||
| 212 | SocksInfo *socks_info, | ||
| 213 | const gchar *userid, const gchar *passwd, | ||
| 214 | SSLType ssl_type) | ||
| 215 | #else | ||
| 216 | static Session *news_session_new(const gchar *server, gushort port, | ||
| 217 | SocksInfo *socks_info, | ||
| 218 | const gchar *userid, const gchar *passwd) | ||
| 219 | #endif | ||
| 220 | { | ||
| 221 | gchar buf[NNTPBUFSIZE]; | ||
| 222 | Session *session; | ||
| 223 | |||
| 224 | g_return_val_if_fail(server != NULL, NULL); | ||
| 225 | |||
| 226 | log_message(_("creating NNTP connection to %s:%d ...\n"), server, port); | ||
| 227 | |||
| 228 | #if USE_SSL | ||
| 229 | session = nntp_session_new_full(server, port, socks_info, buf, userid, passwd, ssl_type); | ||
| 230 | #else | ||
| 231 | session = nntp_session_new_full(server, port, socks_info, buf, userid, passwd); | ||
| 232 | #endif | ||
| 233 | |||
| 234 | return session; | ||
| 235 | } | ||
| 236 | |||
| 237 | static Session *news_session_new_for_folder(Folder *folder) | ||
| 238 | { | ||
| 239 | Session *session; | ||
| 240 | PrefsAccount *ac; | ||
| 241 | SocksInfo *socks_info = NULL; | ||
| 242 | const gchar *userid = NULL; | ||
| 243 | gchar *passwd = NULL; | ||
| 244 | gushort port; | ||
| 245 | |||
| 246 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 247 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 248 | |||
| 249 | ac = folder->account; | ||
| 250 | if (ac->use_nntp_auth && ac->userid && ac->userid[0]) { | ||
| 251 | userid = ac->userid; | ||
| 252 | if (ac->passwd && ac->passwd[0]) | ||
| 253 | passwd = g_strdup(ac->passwd); | ||
| 254 | else | ||
| 255 | passwd = input_query_password(ac->nntp_server, userid); | ||
| 256 | } | ||
| 257 | |||
| 258 | if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) { | ||
| 259 | socks_info = socks_info_new(ac->socks_type, ac->proxy_host, ac->proxy_port, ac->use_proxy_auth ? ac->proxy_name : NULL, ac->use_proxy_auth ? ac->proxy_pass : NULL); | ||
| 260 | } | ||
| 261 | |||
| 262 | #if USE_SSL | ||
| 263 | port = ac->set_nntpport ? ac->nntpport | ||
| 264 | : ac->ssl_nntp ? NNTPS_PORT : NNTP_PORT; | ||
| 265 | session = news_session_new(ac->nntp_server, port, socks_info, | ||
| 266 | userid, passwd, ac->ssl_nntp); | ||
| 267 | #else | ||
| 268 | port = ac->set_nntpport ? ac->nntpport : NNTP_PORT; | ||
| 269 | session = news_session_new(ac->nntp_server, port, socks_info, | ||
| 270 | userid, passwd); | ||
| 271 | #endif | ||
| 272 | |||
| 273 | if (socks_info) | ||
| 274 | socks_info_free(socks_info); | ||
| 275 | |||
| 276 | g_free(passwd); | ||
| 277 | |||
| 278 | return session; | ||
| 279 | } | ||
| 280 | |||
| 281 | static NNTPSession *news_session_get(Folder *folder) | ||
| 282 | { | ||
| 283 | RemoteFolder *rfolder = REMOTE_FOLDER(folder); | ||
| 284 | |||
| 285 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 286 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL); | ||
| 287 | g_return_val_if_fail(folder->account != NULL, NULL); | ||
| 288 | |||
| 289 | if (!prefs_common.online_mode) | ||
| 290 | return NULL; | ||
| 291 | |||
| 292 | if (!rfolder->session) { | ||
| 293 | rfolder->session = news_session_new_for_folder(folder); | ||
| 294 | return NNTP_SESSION(rfolder->session); | ||
| 295 | } | ||
| 296 | |||
| 297 | if (time(NULL) - rfolder->session->last_access_time < | ||
| 298 | SESSION_TIMEOUT_INTERVAL) { | ||
| 299 | return NNTP_SESSION(rfolder->session); | ||
| 300 | } | ||
| 301 | |||
| 302 | if (nntp_mode(NNTP_SESSION(rfolder->session), FALSE) | ||
| 303 | != NN_SUCCESS) { | ||
| 304 | log_warning(_("NNTP connection to %s:%d has been" | ||
| 305 | " disconnected. Reconnecting...\n"), | ||
| 306 | folder->account->nntp_server, | ||
| 307 | folder->account->set_nntpport ? | ||
| 308 | folder->account->nntpport : NNTP_PORT); | ||
| 309 | session_destroy(rfolder->session); | ||
| 310 | rfolder->session = news_session_new_for_folder(folder); | ||
| 311 | } | ||
| 312 | |||
| 313 | if (rfolder->session) | ||
| 314 | session_set_access_time(rfolder->session); | ||
| 315 | |||
| 316 | return NNTP_SESSION(rfolder->session); | ||
| 317 | } | ||
| 318 | |||
| 319 | static GSList *news_get_article_list(Folder *folder, FolderItem *item, | ||
| 320 | gboolean use_cache) | ||
| 321 | { | ||
| 322 | GSList *alist; | ||
| 323 | NNTPSession *session; | ||
| 324 | |||
| 325 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 326 | g_return_val_if_fail(item != NULL, NULL); | ||
| 327 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL); | ||
| 328 | |||
| 329 | session = news_session_get(folder); | ||
| 330 | |||
| 331 | if (!session) { | ||
| 332 | alist = procmsg_read_cache(item, FALSE); | ||
| 333 | item->last_num = procmsg_get_last_num_in_msg_list(alist); | ||
| 334 | } else if (use_cache) { | ||
| 335 | GSList *newlist; | ||
| 336 | gint cache_last; | ||
| 337 | gint first, last; | ||
| 338 | |||
| 339 | alist = procmsg_read_cache(item, FALSE); | ||
| 340 | |||
| 341 | cache_last = procmsg_get_last_num_in_msg_list(alist); | ||
| 342 | newlist = news_get_uncached_articles | ||
| 343 | (session, item, cache_last, &first, &last); | ||
| 344 | if (newlist) | ||
| 345 | item->cache_dirty = TRUE; | ||
| 346 | if (first == 0 && last == 0) { | ||
| 347 | news_delete_all_articles(item); | ||
| 348 | procmsg_msg_list_free(alist); | ||
| 349 | alist = NULL; | ||
| 350 | item->cache_dirty = TRUE; | ||
| 351 | } else { | ||
| 352 | alist = news_delete_old_articles(alist, item, first); | ||
| 353 | news_delete_expired_caches(alist, item); | ||
| 354 | } | ||
| 355 | |||
| 356 | alist = g_slist_concat(alist, newlist); | ||
| 357 | |||
| 358 | item->last_num = last; | ||
| 359 | } else { | ||
| 360 | gint last; | ||
| 361 | |||
| 362 | alist = news_get_uncached_articles | ||
| 363 | (session, item, 0, NULL, &last); | ||
| 364 | news_delete_all_articles(item); | ||
| 365 | item->last_num = last; | ||
| 366 | item->cache_dirty = TRUE; | ||
| 367 | } | ||
| 368 | |||
| 369 | procmsg_set_flags(alist, item); | ||
| 370 | |||
| 371 | alist = procmsg_sort_msg_list(alist, item->sort_key, item->sort_type); | ||
| 372 | |||
| 373 | if (item->mark_queue) | ||
| 374 | item->mark_dirty = TRUE; | ||
| 375 | |||
| 376 | debug_print("cache_dirty: %d, mark_dirty: %d\n", | ||
| 377 | item->cache_dirty, item->mark_dirty); | ||
| 378 | |||
| 379 | if (!item->opened) { | ||
| 380 | if (item->cache_dirty) | ||
| 381 | procmsg_write_cache_list(item, alist); | ||
| 382 | if (item->mark_dirty) | ||
| 383 | procmsg_write_flags_list(item, alist); | ||
| 384 | } | ||
| 385 | |||
| 386 | return alist; | ||
| 387 | } | ||
| 388 | |||
| 389 | static gchar *news_fetch_msg(Folder *folder, FolderItem *item, gint num) | ||
| 390 | { | ||
| 391 | gchar *path, *filename; | ||
| 392 | NNTPSession *session; | ||
| 393 | gchar nstr[16]; | ||
| 394 | gint ok; | ||
| 395 | |||
| 396 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 397 | g_return_val_if_fail(item != NULL, NULL); | ||
| 398 | |||
| 399 | path = folder_item_get_path(item); | ||
| 400 | if (!is_dir_exist(path)) | ||
| 401 | make_dir_hier(path); | ||
| 402 | filename = g_strconcat(path, G_DIR_SEPARATOR_S, utos_buf(nstr, num), | ||
| 403 | NULL); | ||
| 404 | g_free(path); | ||
| 405 | |||
| 406 | if (is_file_exist(filename) && get_file_size(filename) > 0) { | ||
| 407 | debug_print(_("article %d has been already cached.\n"), num); | ||
| 408 | return filename; | ||
| 409 | } | ||
| 410 | |||
| 411 | session = news_session_get(folder); | ||
| 412 | if (!session) { | ||
| 413 | g_free(filename); | ||
| 414 | return NULL; | ||
| 415 | } | ||
| 416 | |||
| 417 | ok = news_select_group(session, item->path, NULL, NULL, NULL); | ||
| 418 | if (ok != NN_SUCCESS) { | ||
| 419 | if (ok == NN_SOCKET) { | ||
| 420 | session_destroy(SESSION(session)); | ||
| 421 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 422 | } | ||
| 423 | g_free(filename); | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | |||
| 427 | debug_print(_("getting article %d...\n"), num); | ||
| 428 | ok = news_get_article(NNTP_SESSION(REMOTE_FOLDER(folder)->session), | ||
| 429 | num, filename); | ||
| 430 | if (ok != NN_SUCCESS) { | ||
| 431 | g_warning(_("can't read article %d\n"), num); | ||
| 432 | if (ok == NN_SOCKET) { | ||
| 433 | session_destroy(SESSION(session)); | ||
| 434 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 435 | } | ||
| 436 | g_free(filename); | ||
| 437 | return NULL; | ||
| 438 | } | ||
| 439 | |||
| 440 | return filename; | ||
| 441 | } | ||
| 442 | |||
| 443 | static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num) | ||
| 444 | { | ||
| 445 | MsgInfo *msginfo; | ||
| 446 | MsgFlags flags = {0, 0}; | ||
| 447 | gchar *file; | ||
| 448 | |||
| 449 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 450 | g_return_val_if_fail(item != NULL, NULL); | ||
| 451 | |||
| 452 | file = news_fetch_msg(folder, item, num); | ||
| 453 | if (!file) return NULL; | ||
| 454 | |||
| 455 | msginfo = procheader_parse_file(file, flags, FALSE); | ||
| 456 | |||
| 457 | g_free(file); | ||
| 458 | |||
| 459 | return msginfo; | ||
| 460 | } | ||
| 461 | |||
| 462 | static gint news_close(Folder *folder, FolderItem *item) | ||
| 463 | { | ||
| 464 | return 0; | ||
| 465 | } | ||
| 466 | |||
| 467 | static gint news_scan_group(Folder *folder, FolderItem *item) | ||
| 468 | { | ||
| 469 | NNTPSession *session; | ||
| 470 | gint num = 0, first = 0, last = 0; | ||
| 471 | gint new = 0, unread = 0, total = 0; | ||
| 472 | gint min = 0, max = 0; | ||
| 473 | gint ok; | ||
| 474 | |||
| 475 | g_return_val_if_fail(folder != NULL, -1); | ||
| 476 | g_return_val_if_fail(item != NULL, -1); | ||
| 477 | |||
| 478 | session = news_session_get(folder); | ||
| 479 | if (!session) return -1; | ||
| 480 | |||
| 481 | ok = news_select_group(session, item->path, &num, &first, &last); | ||
| 482 | if (ok != NN_SUCCESS) { | ||
| 483 | if (ok == NN_SOCKET) { | ||
| 484 | session_destroy(SESSION(session)); | ||
| 485 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 486 | } | ||
| 487 | return -1; | ||
| 488 | } | ||
| 489 | |||
| 490 | if (num == 0) { | ||
| 491 | item->new = item->unread = item->total = item->last_num = 0; | ||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | procmsg_get_mark_sum(item, &new, &unread, &total, &min, &max, first); | ||
| 496 | |||
| 497 | if (max < first || last < min) | ||
| 498 | new = unread = total = num; | ||
| 499 | else { | ||
| 500 | if (min < first) | ||
| 501 | min = first; | ||
| 502 | |||
| 503 | if (last < max) | ||
| 504 | max = last; | ||
| 505 | else if (max < last) { | ||
| 506 | new += last - max; | ||
| 507 | unread += last - max; | ||
| 508 | } | ||
| 509 | |||
| 510 | if (new > num) new = num; | ||
| 511 | if (unread > num) unread = num; | ||
| 512 | } | ||
| 513 | |||
| 514 | item->new = new; | ||
| 515 | item->unread = unread; | ||
| 516 | item->total = num; | ||
| 517 | item->last_num = last; | ||
| 518 | |||
| 519 | return 0; | ||
| 520 | } | ||
| 521 | |||
| 522 | static NewsGroupInfo *news_group_info_new(const gchar *name, | ||
| 523 | gint first, gint last, gchar type) | ||
| 524 | { | ||
| 525 | NewsGroupInfo *ginfo; | ||
| 526 | |||
| 527 | ginfo = g_new(NewsGroupInfo, 1); | ||
| 528 | ginfo->name = g_strdup(name); | ||
| 529 | ginfo->first = first; | ||
| 530 | ginfo->last = last; | ||
| 531 | ginfo->type = type; | ||
| 532 | ginfo->subscribed = FALSE; | ||
| 533 | |||
| 534 | return ginfo; | ||
| 535 | } | ||
| 536 | |||
| 537 | static void news_group_info_free(NewsGroupInfo *ginfo) | ||
| 538 | { | ||
| 539 | g_free(ginfo->name); | ||
| 540 | g_free(ginfo); | ||
| 541 | } | ||
| 542 | |||
| 543 | static gint news_group_info_compare(NewsGroupInfo *ginfo1, | ||
| 544 | NewsGroupInfo *ginfo2) | ||
| 545 | { | ||
| 546 | return g_ascii_strcasecmp(ginfo1->name, ginfo2->name); | ||
| 547 | } | ||
| 548 | |||
| 549 | GSList *news_get_group_list(Folder *folder) | ||
| 550 | { | ||
| 551 | gchar *path, *filename; | ||
| 552 | FILE *fp; | ||
| 553 | GSList *list = NULL; | ||
| 554 | GSList *last = NULL; | ||
| 555 | gchar buf[NNTPBUFSIZE]; | ||
| 556 | |||
| 557 | g_return_val_if_fail(folder != NULL, NULL); | ||
| 558 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL); | ||
| 559 | |||
| 560 | path = folder_item_get_path(FOLDER_ITEM(folder->node->data)); | ||
| 561 | if (!is_dir_exist(path)) | ||
| 562 | make_dir_hier(path); | ||
| 563 | filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL); | ||
| 564 | g_free(path); | ||
| 565 | |||
| 566 | if ((fp = g_fopen(filename, "rb")) == NULL) { | ||
| 567 | NNTPSession *session; | ||
| 568 | gint ok; | ||
| 569 | |||
| 570 | session = news_session_get(folder); | ||
| 571 | if (!session) { | ||
| 572 | g_free(filename); | ||
| 573 | return NULL; | ||
| 574 | } | ||
| 575 | |||
| 576 | ok = nntp_list(session); | ||
| 577 | if (ok != NN_SUCCESS) { | ||
| 578 | if (ok == NN_SOCKET) { | ||
| 579 | session_destroy(SESSION(session)); | ||
| 580 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 581 | } | ||
| 582 | g_free(filename); | ||
| 583 | return NULL; | ||
| 584 | } | ||
| 585 | if (recv_write_to_file(SESSION(session)->sock, filename) < 0) { | ||
| 586 | log_warning("can't retrieve newsgroup list\n"); | ||
| 587 | session_destroy(SESSION(session)); | ||
| 588 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 589 | g_free(filename); | ||
| 590 | return NULL; | ||
| 591 | } | ||
| 592 | |||
| 593 | if ((fp = g_fopen(filename, "rb")) == NULL) { | ||
| 594 | FILE_OP_ERROR(filename, "fopen"); | ||
| 595 | g_free(filename); | ||
| 596 | return NULL; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 600 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 601 | gchar *p = buf; | ||
| 602 | gchar *name; | ||
| 603 | gint last_num; | ||
| 604 | gint first_num; | ||
| 605 | gchar type; | ||
| 606 | NewsGroupInfo *ginfo; | ||
| 607 | |||
| 608 | p = strchr(p, ' '); | ||
| 609 | if (!p) { | ||
| 610 | strretchomp(buf); | ||
| 611 | log_warning("invalid LIST response: %s\n", buf); | ||
| 612 | continue; | ||
| 613 | } | ||
| 614 | *p = '\0'; | ||
| 615 | p++; | ||
| 616 | name = buf; | ||
| 617 | |||
| 618 | if (sscanf(p, "%d %d %c", &last_num, &first_num, &type) < 3) { | ||
| 619 | strretchomp(p); | ||
| 620 | log_warning("invalid LIST response: %s %s\n", name, p); | ||
| 621 | continue; | ||
| 622 | } | ||
| 623 | |||
| 624 | ginfo = news_group_info_new(name, first_num, last_num, type); | ||
| 625 | |||
| 626 | if (!last) | ||
| 627 | last = list = g_slist_append(NULL, ginfo); | ||
| 628 | else { | ||
| 629 | last = g_slist_append(last, ginfo); | ||
| 630 | last = last->next; | ||
| 631 | } | ||
| 632 | } | ||
| 633 | |||
| 634 | fclose(fp); | ||
| 635 | g_free(filename); | ||
| 636 | |||
| 637 | list = g_slist_sort(list, (GCompareFunc)news_group_info_compare); | ||
| 638 | |||
| 639 | return list; | ||
| 640 | } | ||
| 641 | |||
| 642 | void news_group_list_free(GSList *group_list) | ||
| 643 | { | ||
| 644 | GSList *cur; | ||
| 645 | |||
| 646 | if (!group_list) return; | ||
| 647 | |||
| 648 | for (cur = group_list; cur != NULL; cur = cur->next) | ||
| 649 | news_group_info_free((NewsGroupInfo *)cur->data); | ||
| 650 | g_slist_free(group_list); | ||
| 651 | } | ||
| 652 | |||
| 653 | void news_remove_group_list_cache(Folder *folder) | ||
| 654 | { | ||
| 655 | gchar *path, *filename; | ||
| 656 | |||
| 657 | g_return_if_fail(folder != NULL); | ||
| 658 | g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS); | ||
| 659 | |||
| 660 | path = folder_item_get_path(FOLDER_ITEM(folder->node->data)); | ||
| 661 | filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL); | ||
| 662 | g_free(path); | ||
| 663 | |||
| 664 | if (is_file_exist(filename)) { | ||
| 665 | if (remove(filename) < 0) | ||
| 666 | FILE_OP_ERROR(filename, "remove"); | ||
| 667 | } | ||
| 668 | g_free(filename); | ||
| 669 | } | ||
| 670 | |||
| 671 | gint news_post(Folder *folder, const gchar *file) | ||
| 672 | { | ||
| 673 | FILE *fp; | ||
| 674 | gint ok; | ||
| 675 | |||
| 676 | g_return_val_if_fail(folder != NULL, -1); | ||
| 677 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, -1); | ||
| 678 | g_return_val_if_fail(file != NULL, -1); | ||
| 679 | |||
| 680 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 681 | FILE_OP_ERROR(file, "fopen"); | ||
| 682 | return -1; | ||
| 683 | } | ||
| 684 | |||
| 685 | ok = news_post_stream(folder, fp); | ||
| 686 | |||
| 687 | fclose(fp); | ||
| 688 | |||
| 689 | return ok; | ||
| 690 | } | ||
| 691 | |||
| 692 | gint news_post_stream(Folder *folder, FILE *fp) | ||
| 693 | { | ||
| 694 | NNTPSession *session; | ||
| 695 | gint ok; | ||
| 696 | |||
| 697 | g_return_val_if_fail(folder != NULL, -1); | ||
| 698 | g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, -1); | ||
| 699 | g_return_val_if_fail(fp != NULL, -1); | ||
| 700 | |||
| 701 | session = news_session_get(folder); | ||
| 702 | if (!session) return -1; | ||
| 703 | |||
| 704 | ok = nntp_post(session, fp); | ||
| 705 | if (ok != NN_SUCCESS) { | ||
| 706 | log_warning(_("can't post article.\n")); | ||
| 707 | if (ok == NN_SOCKET) { | ||
| 708 | session_destroy(SESSION(session)); | ||
| 709 | REMOTE_FOLDER(folder)->session = NULL; | ||
| 710 | } | ||
| 711 | return -1; | ||
| 712 | } | ||
| 713 | |||
| 714 | return 0; | ||
| 715 | } | ||
| 716 | |||
| 717 | static gint news_get_article_cmd(NNTPSession *session, const gchar *cmd, | ||
| 718 | gint num, gchar *filename) | ||
| 719 | { | ||
| 720 | gchar *msgid; | ||
| 721 | gint ok; | ||
| 722 | |||
| 723 | ok = nntp_get_article(session, cmd, num, &msgid); | ||
| 724 | if (ok != NN_SUCCESS) | ||
| 725 | return ok; | ||
| 726 | |||
| 727 | debug_print("Message-Id = %s, num = %d\n", msgid, num); | ||
| 728 | g_free(msgid); | ||
| 729 | |||
| 730 | ok = recv_write_to_file(SESSION(session)->sock, filename); | ||
| 731 | if (ok < 0) { | ||
| 732 | log_warning(_("can't retrieve article %d\n"), num); | ||
| 733 | if (ok == -2) | ||
| 734 | return NN_SOCKET; | ||
| 735 | else | ||
| 736 | return NN_IOERR; | ||
| 737 | } | ||
| 738 | |||
| 739 | return NN_SUCCESS; | ||
| 740 | } | ||
| 741 | |||
| 742 | static gint news_get_article(NNTPSession *session, gint num, gchar *filename) | ||
| 743 | { | ||
| 744 | return news_get_article_cmd(session, "ARTICLE", num, filename); | ||
| 745 | } | ||
| 746 | |||
| 747 | #if 0 | ||
| 748 | static gint news_get_header(NNTPSession *session, gint num, gchar *filename) | ||
| 749 | { | ||
| 750 | return news_get_article_cmd(session, "HEAD", num, filename); | ||
| 751 | } | ||
| 752 | #endif | ||
| 753 | |||
| 754 | /** | ||
| 755 | * news_select_group: | ||
| 756 | * @session: Active NNTP session. | ||
| 757 | * @group: Newsgroup name. | ||
| 758 | * @num: Estimated number of articles. | ||
| 759 | * @first: First article number. | ||
| 760 | * @last: Last article number. | ||
| 761 | * | ||
| 762 | * Select newsgroup @group with the GROUP command if it is not already | ||
| 763 | * selected in @session, or article numbers need to be returned. | ||
| 764 | * | ||
| 765 | * Return value: NNTP result code. | ||
| 766 | **/ | ||
| 767 | static gint news_select_group(NNTPSession *session, const gchar *group, | ||
| 768 | gint *num, gint *first, gint *last) | ||
| 769 | { | ||
| 770 | gint ok; | ||
| 771 | gint num_, first_, last_; | ||
| 772 | |||
| 773 | if (!num || !first || !last) { | ||
| 774 | if (session->group && | ||
| 775 | g_ascii_strcasecmp(session->group, group) == 0) | ||
| 776 | return NN_SUCCESS; | ||
| 777 | num = &num_; | ||
| 778 | first = &first_; | ||
| 779 | last = &last_; | ||
| 780 | } | ||
| 781 | |||
| 782 | g_free(session->group); | ||
| 783 | session->group = NULL; | ||
| 784 | |||
| 785 | ok = nntp_group(session, group, num, first, last); | ||
| 786 | if (ok == NN_SUCCESS) | ||
| 787 | session->group = g_strdup(group); | ||
| 788 | else | ||
| 789 | log_warning(_("can't select group: %s\n"), group); | ||
| 790 | |||
| 791 | return ok; | ||
| 792 | } | ||
| 793 | |||
| 794 | static GSList *news_get_uncached_articles(NNTPSession *session, | ||
| 795 | FolderItem *item, gint cache_last, | ||
| 796 | gint *rfirst, gint *rlast) | ||
| 797 | { | ||
| 798 | gint ok; | ||
| 799 | gint num = 0, first = 0, last = 0, begin = 0, end = 0; | ||
| 800 | gchar buf[NNTPBUFSIZE]; | ||
| 801 | GSList *newlist = NULL; | ||
| 802 | GSList *llast = NULL; | ||
| 803 | MsgInfo *msginfo; | ||
| 804 | gint max_articles; | ||
| 805 | |||
| 806 | if (rfirst) *rfirst = -1; | ||
| 807 | if (rlast) *rlast = -1; | ||
| 808 | |||
| 809 | g_return_val_if_fail(session != NULL, NULL); | ||
| 810 | g_return_val_if_fail(item != NULL, NULL); | ||
| 811 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 812 | g_return_val_if_fail(item->folder->account != NULL, NULL); | ||
| 813 | g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_NEWS, NULL); | ||
| 814 | |||
| 815 | ok = news_select_group(session, item->path, &num, &first, &last); | ||
| 816 | if (ok != NN_SUCCESS) { | ||
| 817 | if (ok == NN_SOCKET) { | ||
| 818 | session_destroy(SESSION(session)); | ||
| 819 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 820 | } | ||
| 821 | return NULL; | ||
| 822 | } | ||
| 823 | |||
| 824 | /* calculate getting overview range */ | ||
| 825 | if (first > last) { | ||
| 826 | log_warning(_("invalid article range: %d - %d\n"), | ||
| 827 | first, last); | ||
| 828 | return NULL; | ||
| 829 | } | ||
| 830 | |||
| 831 | if (rfirst) *rfirst = first; | ||
| 832 | if (rlast) *rlast = last; | ||
| 833 | |||
| 834 | if (cache_last < first) | ||
| 835 | begin = first; | ||
| 836 | else if (last < cache_last) | ||
| 837 | begin = first; | ||
| 838 | else if (last == cache_last) { | ||
| 839 | debug_print(_("no new articles.\n")); | ||
| 840 | return NULL; | ||
| 841 | } else | ||
| 842 | begin = cache_last + 1; | ||
| 843 | end = last; | ||
| 844 | |||
| 845 | max_articles = item->folder->account->max_nntp_articles; | ||
| 846 | if (max_articles > 0 && end - begin + 1 > max_articles) | ||
| 847 | begin = end - max_articles + 1; | ||
| 848 | |||
| 849 | log_message(_("getting xover %d - %d in %s...\n"), | ||
| 850 | begin, end, item->path); | ||
| 851 | ok = nntp_xover(session, begin, end); | ||
| 852 | if (ok != NN_SUCCESS) { | ||
| 853 | log_warning(_("can't get xover\n")); | ||
| 854 | if (ok == NN_SOCKET) { | ||
| 855 | session_destroy(SESSION(session)); | ||
| 856 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 857 | } | ||
| 858 | return NULL; | ||
| 859 | } | ||
| 860 | |||
| 861 | for (;;) { | ||
| 862 | if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) { | ||
| 863 | log_warning(_("error occurred while getting xover.\n")); | ||
| 864 | session_destroy(SESSION(session)); | ||
| 865 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 866 | return newlist; | ||
| 867 | } | ||
| 868 | |||
| 869 | if (buf[0] == '.' && buf[1] == '\r') break; | ||
| 870 | |||
| 871 | msginfo = news_parse_xover(buf); | ||
| 872 | if (!msginfo) { | ||
| 873 | log_warning(_("invalid xover line: %s\n"), buf); | ||
| 874 | continue; | ||
| 875 | } | ||
| 876 | |||
| 877 | msginfo->folder = item; | ||
| 878 | msginfo->flags.perm_flags = MSG_NEW|MSG_UNREAD; | ||
| 879 | msginfo->flags.tmp_flags = MSG_NEWS; | ||
| 880 | msginfo->newsgroups = g_strdup(item->path); | ||
| 881 | |||
| 882 | if (!newlist) | ||
| 883 | llast = newlist = g_slist_append(newlist, msginfo); | ||
| 884 | else { | ||
| 885 | llast = g_slist_append(llast, msginfo); | ||
| 886 | llast = llast->next; | ||
| 887 | } | ||
| 888 | } | ||
| 889 | |||
| 890 | ok = nntp_xhdr(session, "to", begin, end); | ||
| 891 | if (ok != NN_SUCCESS) { | ||
| 892 | log_warning(_("can't get xhdr\n")); | ||
| 893 | if (ok == NN_SOCKET) { | ||
| 894 | session_destroy(SESSION(session)); | ||
| 895 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 896 | } | ||
| 897 | return newlist; | ||
| 898 | } | ||
| 899 | |||
| 900 | llast = newlist; | ||
| 901 | |||
| 902 | for (;;) { | ||
| 903 | if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) { | ||
| 904 | log_warning(_("error occurred while getting xhdr.\n")); | ||
| 905 | session_destroy(SESSION(session)); | ||
| 906 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 907 | return newlist; | ||
| 908 | } | ||
| 909 | |||
| 910 | if (buf[0] == '.' && buf[1] == '\r') break; | ||
| 911 | if (!llast) { | ||
| 912 | g_warning("llast == NULL\n"); | ||
| 913 | continue; | ||
| 914 | } | ||
| 915 | |||
| 916 | msginfo = (MsgInfo *)llast->data; | ||
| 917 | msginfo->to = news_parse_xhdr(buf, msginfo); | ||
| 918 | |||
| 919 | llast = llast->next; | ||
| 920 | } | ||
| 921 | |||
| 922 | ok = nntp_xhdr(session, "cc", begin, end); | ||
| 923 | if (ok != NN_SUCCESS) { | ||
| 924 | log_warning(_("can't get xhdr\n")); | ||
| 925 | if (ok == NN_SOCKET) { | ||
| 926 | session_destroy(SESSION(session)); | ||
| 927 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 928 | } | ||
| 929 | return newlist; | ||
| 930 | } | ||
| 931 | |||
| 932 | llast = newlist; | ||
| 933 | |||
| 934 | for (;;) { | ||
| 935 | if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) { | ||
| 936 | log_warning(_("error occurred while getting xhdr.\n")); | ||
| 937 | session_destroy(SESSION(session)); | ||
| 938 | REMOTE_FOLDER(item->folder)->session = NULL; | ||
| 939 | return newlist; | ||
| 940 | } | ||
| 941 | |||
| 942 | if (buf[0] == '.' && buf[1] == '\r') break; | ||
| 943 | if (!llast) { | ||
| 944 | g_warning("llast == NULL\n"); | ||
| 945 | continue; | ||
| 946 | } | ||
| 947 | |||
| 948 | msginfo = (MsgInfo *)llast->data; | ||
| 949 | msginfo->cc = news_parse_xhdr(buf, msginfo); | ||
| 950 | |||
| 951 | llast = llast->next; | ||
| 952 | } | ||
| 953 | |||
| 954 | session_set_access_time(SESSION(session)); | ||
| 955 | |||
| 956 | return newlist; | ||
| 957 | } | ||
| 958 | |||
| 959 | #define PARSE_ONE_PARAM(p, srcp) \ | ||
| 960 | { \ | ||
| 961 | p = strchr(srcp, '\t'); \ | ||
| 962 | if (!p) return NULL; \ | ||
| 963 | else \ | ||
| 964 | *p++ = '\0'; \ | ||
| 965 | } | ||
| 966 | |||
| 967 | static MsgInfo *news_parse_xover(const gchar *xover_str) | ||
| 968 | { | ||
| 969 | MsgInfo *msginfo; | ||
| 970 | gchar *subject, *sender, *size, *line, *date, *msgid, *ref, *tmp; | ||
| 971 | gchar *p; | ||
| 972 | gint num, size_int, line_int; | ||
| 973 | gchar *xover_buf; | ||
| 974 | |||
| 975 | Xstrdup_a(xover_buf, xover_str, return NULL); | ||
| 976 | |||
| 977 | PARSE_ONE_PARAM(subject, xover_buf); | ||
| 978 | PARSE_ONE_PARAM(sender, subject); | ||
| 979 | PARSE_ONE_PARAM(date, sender); | ||
| 980 | PARSE_ONE_PARAM(msgid, date); | ||
| 981 | PARSE_ONE_PARAM(ref, msgid); | ||
| 982 | PARSE_ONE_PARAM(size, ref); | ||
| 983 | PARSE_ONE_PARAM(line, size); | ||
| 984 | |||
| 985 | tmp = strchr(line, '\t'); | ||
| 986 | if (!tmp) tmp = strchr(line, '\r'); | ||
| 987 | if (!tmp) tmp = strchr(line, '\n'); | ||
| 988 | if (tmp) *tmp = '\0'; | ||
| 989 | |||
| 990 | num = atoi(xover_str); | ||
| 991 | size_int = atoi(size); | ||
| 992 | line_int = atoi(line); | ||
| 993 | |||
| 994 | /* set MsgInfo */ | ||
| 995 | msginfo = g_new0(MsgInfo, 1); | ||
| 996 | msginfo->msgnum = num; | ||
| 997 | msginfo->size = size_int; | ||
| 998 | |||
| 999 | msginfo->date = g_strdup(date); | ||
| 1000 | msginfo->date_t = procheader_date_parse(NULL, date, 0); | ||
| 1001 | |||
| 1002 | msginfo->from = conv_unmime_header(sender, NULL); | ||
| 1003 | msginfo->fromname = procheader_get_fromname(msginfo->from); | ||
| 1004 | |||
| 1005 | msginfo->subject = conv_unmime_header(subject, NULL); | ||
| 1006 | |||
| 1007 | extract_parenthesis(msgid, '<', '>'); | ||
| 1008 | remove_space(msgid); | ||
| 1009 | if (*msgid != '\0') | ||
| 1010 | msginfo->msgid = g_strdup(msgid); | ||
| 1011 | |||
| 1012 | eliminate_parenthesis(ref, '(', ')'); | ||
| 1013 | if ((p = strrchr(ref, '<')) != NULL) { | ||
| 1014 | extract_parenthesis(p, '<', '>'); | ||
| 1015 | remove_space(p); | ||
| 1016 | if (*p != '\0') | ||
| 1017 | msginfo->inreplyto = g_strdup(p); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | return msginfo; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | static gchar *news_parse_xhdr(const gchar *xhdr_str, MsgInfo *msginfo) | ||
| 1024 | { | ||
| 1025 | gchar *p; | ||
| 1026 | gchar *tmp; | ||
| 1027 | gint num; | ||
| 1028 | |||
| 1029 | p = strchr(xhdr_str, ' '); | ||
| 1030 | if (!p) | ||
| 1031 | return NULL; | ||
| 1032 | else | ||
| 1033 | p++; | ||
| 1034 | |||
| 1035 | num = atoi(xhdr_str); | ||
| 1036 | if (msginfo->msgnum != num) return NULL; | ||
| 1037 | |||
| 1038 | tmp = strchr(p, '\r'); | ||
| 1039 | if (!tmp) tmp = strchr(p, '\n'); | ||
| 1040 | |||
| 1041 | if (tmp) | ||
| 1042 | return g_strndup(p, tmp - p); | ||
| 1043 | else | ||
| 1044 | return g_strdup(p); | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | static GSList *news_delete_old_articles(GSList *alist, FolderItem *item, | ||
| 1048 | gint first) | ||
| 1049 | { | ||
| 1050 | GSList *cur, *next; | ||
| 1051 | MsgInfo *msginfo; | ||
| 1052 | gchar *dir; | ||
| 1053 | |||
| 1054 | g_return_val_if_fail(item != NULL, alist); | ||
| 1055 | g_return_val_if_fail(item->folder != NULL, alist); | ||
| 1056 | g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_NEWS, alist); | ||
| 1057 | |||
| 1058 | if (first < 2) return alist; | ||
| 1059 | |||
| 1060 | debug_print("Deleting cached articles 1 - %d ...\n", first - 1); | ||
| 1061 | |||
| 1062 | dir = folder_item_get_path(item); | ||
| 1063 | remove_numbered_files(dir, 1, first - 1); | ||
| 1064 | g_free(dir); | ||
| 1065 | |||
| 1066 | for (cur = alist; cur != NULL; ) { | ||
| 1067 | next = cur->next; | ||
| 1068 | |||
| 1069 | msginfo = (MsgInfo *)cur->data; | ||
| 1070 | if (msginfo && msginfo->msgnum < first) { | ||
| 1071 | procmsg_msginfo_free(msginfo); | ||
| 1072 | alist = g_slist_remove(alist, msginfo); | ||
| 1073 | item->cache_dirty = TRUE; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | cur = next; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | return alist; | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | static void news_delete_all_articles(FolderItem *item) | ||
| 1083 | { | ||
| 1084 | gchar *dir; | ||
| 1085 | |||
| 1086 | g_return_if_fail(item != NULL); | ||
| 1087 | g_return_if_fail(item->folder != NULL); | ||
| 1088 | g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS); | ||
| 1089 | |||
| 1090 | debug_print("Deleting all cached articles...\n"); | ||
| 1091 | |||
| 1092 | dir = folder_item_get_path(item); | ||
| 1093 | remove_all_numbered_files(dir); | ||
| 1094 | g_free(dir); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | static void news_delete_expired_caches(GSList *alist, FolderItem *item) | ||
| 1098 | { | ||
| 1099 | gchar *dir; | ||
| 1100 | |||
| 1101 | g_return_if_fail(item != NULL); | ||
| 1102 | g_return_if_fail(item->folder != NULL); | ||
| 1103 | g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS); | ||
| 1104 | |||
| 1105 | debug_print("Deleting expired cached articles...\n"); | ||
| 1106 | |||
| 1107 | dir = folder_item_get_path(item); | ||
| 1108 | remove_expired_files(dir, 24 * 7); | ||
| 1109 | g_free(dir); | ||
| 1110 | } | ||
diff --git a/libsylph/news.h b/libsylph/news.h new file mode 100644 index 0000000..8adcb2f --- /dev/null +++ b/libsylph/news.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2007 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __NEWS_H__ | ||
| 21 | #define __NEWS_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | |||
| 26 | #include "folder.h" | ||
| 27 | |||
| 28 | typedef struct _NewsFolder NewsFolder; | ||
| 29 | typedef struct _NewsGroupInfo NewsGroupInfo; | ||
| 30 | |||
| 31 | #define NEWS_FOLDER(obj) ((NewsFolder *)obj) | ||
| 32 | |||
| 33 | struct _NewsFolder | ||
| 34 | { | ||
| 35 | RemoteFolder rfolder; | ||
| 36 | |||
| 37 | gboolean use_auth; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct _NewsGroupInfo | ||
| 41 | { | ||
| 42 | gchar *name; | ||
| 43 | guint first; | ||
| 44 | guint last; | ||
| 45 | gchar type; | ||
| 46 | gboolean subscribed; | ||
| 47 | }; | ||
| 48 | |||
| 49 | FolderClass *news_get_class (void); | ||
| 50 | |||
| 51 | GSList *news_get_group_list (Folder *folder); | ||
| 52 | void news_group_list_free (GSList *group_list); | ||
| 53 | void news_remove_group_list_cache (Folder *folder); | ||
| 54 | |||
| 55 | gint news_post (Folder *folder, | ||
| 56 | const gchar *file); | ||
| 57 | gint news_post_stream (Folder *folder, | ||
| 58 | FILE *fp); | ||
| 59 | |||
| 60 | #endif /* __NEWS_H__ */ | ||
diff --git a/libsylph/nntp.c b/libsylph/nntp.c new file mode 100644 index 0000000..64c9da1 --- /dev/null +++ b/libsylph/nntp.c | |||
| @@ -0,0 +1,471 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <glib/gi18n.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <string.h> | ||
| 28 | |||
| 29 | #include "nntp.h" | ||
| 30 | #include "socket.h" | ||
| 31 | #include "utils.h" | ||
| 32 | #if USE_SSL | ||
| 33 | # include "ssl.h" | ||
| 34 | #endif | ||
| 35 | #include "socks.h" | ||
| 36 | |||
| 37 | static gint verbose = 1; | ||
| 38 | |||
| 39 | static void nntp_session_destroy(Session *session); | ||
| 40 | |||
| 41 | static gint nntp_ok (SockInfo *sock, | ||
| 42 | gchar *argbuf); | ||
| 43 | |||
| 44 | static gint nntp_gen_send (SockInfo *sock, | ||
| 45 | const gchar *format, | ||
| 46 | ...); | ||
| 47 | static gint nntp_gen_recv (SockInfo *sock, | ||
| 48 | gchar *buf, | ||
| 49 | gint size); | ||
| 50 | static gint nntp_gen_command (NNTPSession *session, | ||
| 51 | gchar *argbuf, | ||
| 52 | const gchar *format, | ||
| 53 | ...); | ||
| 54 | |||
| 55 | |||
| 56 | #if USE_SSL | ||
| 57 | Session *nntp_session_new_full(const gchar *server, gushort port, | ||
| 58 | SocksInfo *socks_info, gchar *buf, | ||
| 59 | const gchar *userid, const gchar *passwd, | ||
| 60 | SSLType ssl_type) | ||
| 61 | #else | ||
| 62 | Session *nntp_session_new_full(const gchar *server, gushort port, | ||
| 63 | SocksInfo *socks_info, gchar *buf, | ||
| 64 | const gchar *userid, const gchar *passwd) | ||
| 65 | #endif | ||
| 66 | { | ||
| 67 | NNTPSession *session; | ||
| 68 | SockInfo *sock; | ||
| 69 | const gchar *server_; | ||
| 70 | gushort port_; | ||
| 71 | |||
| 72 | if (socks_info) { | ||
| 73 | server_ = socks_info->proxy_host; | ||
| 74 | port_ = socks_info->proxy_port; | ||
| 75 | } else { | ||
| 76 | server_ = server; | ||
| 77 | port_ = port; | ||
| 78 | } | ||
| 79 | |||
| 80 | if ((sock = sock_connect(server_, port_)) == NULL) { | ||
| 81 | log_warning(_("Can't connect to NNTP server: %s:%d\n"), | ||
| 82 | server, port); | ||
| 83 | return NULL; | ||
| 84 | } | ||
| 85 | |||
| 86 | if (socks_info) { | ||
| 87 | if (socks_connect(sock, server, port, socks_info) < 0) { | ||
| 88 | log_warning("Can't establish SOCKS connection: %s:%d\n", | ||
| 89 | server, port); | ||
| 90 | sock_close(sock); | ||
| 91 | return NULL; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | #if USE_SSL | ||
| 96 | if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) { | ||
| 97 | log_warning("Can't establish NNTP session with: %s:%d\n", | ||
| 98 | server, port); | ||
| 99 | sock_close(sock); | ||
| 100 | return NULL; | ||
| 101 | } | ||
| 102 | #endif | ||
| 103 | |||
| 104 | if (nntp_ok(sock, buf) != NN_SUCCESS) { | ||
| 105 | sock_close(sock); | ||
| 106 | return NULL; | ||
| 107 | } | ||
| 108 | |||
| 109 | session = g_new0(NNTPSession, 1); | ||
| 110 | |||
| 111 | session_init(SESSION(session)); | ||
| 112 | |||
| 113 | SESSION(session)->type = SESSION_NEWS; | ||
| 114 | SESSION(session)->server = g_strdup(server); | ||
| 115 | SESSION(session)->sock = sock; | ||
| 116 | SESSION(session)->last_access_time = time(NULL); | ||
| 117 | SESSION(session)->data = NULL; | ||
| 118 | |||
| 119 | SESSION(session)->destroy = nntp_session_destroy; | ||
| 120 | |||
| 121 | session->group = NULL; | ||
| 122 | |||
| 123 | if (userid && passwd) { | ||
| 124 | gint ok; | ||
| 125 | |||
| 126 | session->userid = g_strdup(userid); | ||
| 127 | session->passwd = g_strdup(passwd); | ||
| 128 | |||
| 129 | ok = nntp_gen_send(sock, "AUTHINFO USER %s", session->userid); | ||
| 130 | if (ok != NN_SUCCESS) { | ||
| 131 | session_destroy(SESSION(session)); | ||
| 132 | return NULL; | ||
| 133 | } | ||
| 134 | ok = nntp_ok(sock, NULL); | ||
| 135 | if (ok == NN_AUTHCONT) { | ||
| 136 | ok = nntp_gen_send(sock, "AUTHINFO PASS %s", | ||
| 137 | session->passwd); | ||
| 138 | if (ok != NN_SUCCESS) { | ||
| 139 | session_destroy(SESSION(session)); | ||
| 140 | return NULL; | ||
| 141 | } | ||
| 142 | ok = nntp_ok(sock, NULL); | ||
| 143 | if (ok != NN_SUCCESS) | ||
| 144 | session->auth_failed = TRUE; | ||
| 145 | } | ||
| 146 | if (ok == NN_SOCKET) { | ||
| 147 | session_destroy(SESSION(session)); | ||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | session_set_access_time(SESSION(session)); | ||
| 153 | |||
| 154 | return SESSION(session); | ||
| 155 | } | ||
| 156 | |||
| 157 | #if USE_SSL | ||
| 158 | Session *nntp_session_new(const gchar *server, gushort port, gchar *buf, | ||
| 159 | const gchar *userid, const gchar *passwd, | ||
| 160 | SSLType ssl_type) | ||
| 161 | { | ||
| 162 | return nntp_session_new_full(server, port, NULL, buf, userid, passwd, | ||
| 163 | ssl_type); | ||
| 164 | } | ||
| 165 | #else | ||
| 166 | Session *nntp_session_new(const gchar *server, gushort port, gchar *buf, | ||
| 167 | const gchar *userid, const gchar *passwd) | ||
| 168 | { | ||
| 169 | return nntp_session_new_full(server, port, NULL, buf, userid, passwd); | ||
| 170 | } | ||
| 171 | #endif | ||
| 172 | |||
| 173 | static void nntp_session_destroy(Session *session) | ||
| 174 | { | ||
| 175 | NNTPSession *nntp_session = NNTP_SESSION(session); | ||
| 176 | |||
| 177 | g_return_if_fail(session != NULL); | ||
| 178 | |||
| 179 | g_free(nntp_session->group); | ||
| 180 | g_free(nntp_session->userid); | ||
| 181 | g_free(nntp_session->passwd); | ||
| 182 | } | ||
| 183 | |||
| 184 | gint nntp_group(NNTPSession *session, const gchar *group, | ||
| 185 | gint *num, gint *first, gint *last) | ||
| 186 | { | ||
| 187 | gint ok; | ||
| 188 | gint resp; | ||
| 189 | gchar buf[NNTPBUFSIZE]; | ||
| 190 | |||
| 191 | ok = nntp_gen_command(session, buf, "GROUP %s", group); | ||
| 192 | |||
| 193 | if (ok != NN_SUCCESS && ok != NN_SOCKET && ok != NN_AUTHREQ) { | ||
| 194 | ok = nntp_mode(session, FALSE); | ||
| 195 | if (ok == NN_SUCCESS) | ||
| 196 | ok = nntp_gen_command(session, buf, "GROUP %s", group); | ||
| 197 | } | ||
| 198 | |||
| 199 | if (ok != NN_SUCCESS) | ||
| 200 | return ok; | ||
| 201 | |||
| 202 | if (sscanf(buf, "%d %d %d %d", &resp, num, first, last) | ||
| 203 | != 4) { | ||
| 204 | log_warning(_("protocol error: %s\n"), buf); | ||
| 205 | return NN_PROTOCOL; | ||
| 206 | } | ||
| 207 | |||
| 208 | return NN_SUCCESS; | ||
| 209 | } | ||
| 210 | |||
| 211 | gint nntp_get_article(NNTPSession *session, const gchar *cmd, gint num, | ||
| 212 | gchar **msgid) | ||
| 213 | { | ||
| 214 | gint ok; | ||
| 215 | gchar buf[NNTPBUFSIZE]; | ||
| 216 | |||
| 217 | if (num > 0) | ||
| 218 | ok = nntp_gen_command(session, buf, "%s %d", cmd, num); | ||
| 219 | else | ||
| 220 | ok = nntp_gen_command(session, buf, cmd); | ||
| 221 | |||
| 222 | if (ok != NN_SUCCESS) | ||
| 223 | return ok; | ||
| 224 | |||
| 225 | extract_parenthesis(buf, '<', '>'); | ||
| 226 | if (buf[0] == '\0') { | ||
| 227 | log_warning(_("protocol error\n")); | ||
| 228 | *msgid = g_strdup("0"); | ||
| 229 | } else | ||
| 230 | *msgid = g_strdup(buf); | ||
| 231 | |||
| 232 | return NN_SUCCESS; | ||
| 233 | } | ||
| 234 | |||
| 235 | gint nntp_article(NNTPSession *session, gint num, gchar **msgid) | ||
| 236 | { | ||
| 237 | return nntp_get_article(session, "ARTICLE", num, msgid); | ||
| 238 | } | ||
| 239 | |||
| 240 | gint nntp_body(NNTPSession *session, gint num, gchar **msgid) | ||
| 241 | { | ||
| 242 | return nntp_get_article(session, "BODY", num, msgid); | ||
| 243 | } | ||
| 244 | |||
| 245 | gint nntp_head(NNTPSession *session, gint num, gchar **msgid) | ||
| 246 | { | ||
| 247 | return nntp_get_article(session, "HEAD", num, msgid); | ||
| 248 | } | ||
| 249 | |||
| 250 | gint nntp_stat(NNTPSession *session, gint num, gchar **msgid) | ||
| 251 | { | ||
| 252 | return nntp_get_article(session, "STAT", num, msgid); | ||
| 253 | } | ||
| 254 | |||
| 255 | gint nntp_next(NNTPSession *session, gint *num, gchar **msgid) | ||
| 256 | { | ||
| 257 | gint ok; | ||
| 258 | gint resp; | ||
| 259 | gchar buf[NNTPBUFSIZE]; | ||
| 260 | |||
| 261 | ok = nntp_gen_command(session, buf, "NEXT"); | ||
| 262 | |||
| 263 | if (ok != NN_SUCCESS) | ||
| 264 | return ok; | ||
| 265 | |||
| 266 | if (sscanf(buf, "%d %d", &resp, num) != 2) { | ||
| 267 | log_warning(_("protocol error: %s\n"), buf); | ||
| 268 | return NN_PROTOCOL; | ||
| 269 | } | ||
| 270 | |||
| 271 | extract_parenthesis(buf, '<', '>'); | ||
| 272 | if (buf[0] == '\0') { | ||
| 273 | log_warning(_("protocol error\n")); | ||
| 274 | return NN_PROTOCOL; | ||
| 275 | } | ||
| 276 | *msgid = g_strdup(buf); | ||
| 277 | |||
| 278 | return NN_SUCCESS; | ||
| 279 | } | ||
| 280 | |||
| 281 | gint nntp_xover(NNTPSession *session, gint first, gint last) | ||
| 282 | { | ||
| 283 | gint ok; | ||
| 284 | gchar buf[NNTPBUFSIZE]; | ||
| 285 | |||
| 286 | ok = nntp_gen_command(session, buf, "XOVER %d-%d", first, last); | ||
| 287 | if (ok != NN_SUCCESS) | ||
| 288 | return ok; | ||
| 289 | |||
| 290 | return NN_SUCCESS; | ||
| 291 | } | ||
| 292 | |||
| 293 | gint nntp_xhdr(NNTPSession *session, const gchar *header, gint first, gint last) | ||
| 294 | { | ||
| 295 | gint ok; | ||
| 296 | gchar buf[NNTPBUFSIZE]; | ||
| 297 | |||
| 298 | ok = nntp_gen_command(session, buf, "XHDR %s %d-%d", | ||
| 299 | header, first, last); | ||
| 300 | if (ok != NN_SUCCESS) | ||
| 301 | return ok; | ||
| 302 | |||
| 303 | return NN_SUCCESS; | ||
| 304 | } | ||
| 305 | |||
| 306 | gint nntp_list(NNTPSession *session) | ||
| 307 | { | ||
| 308 | return nntp_gen_command(session, NULL, "LIST"); | ||
| 309 | } | ||
| 310 | |||
| 311 | gint nntp_post(NNTPSession *session, FILE *fp) | ||
| 312 | { | ||
| 313 | gint ok; | ||
| 314 | gchar buf[NNTPBUFSIZE]; | ||
| 315 | gchar *msg; | ||
| 316 | |||
| 317 | ok = nntp_gen_command(session, buf, "POST"); | ||
| 318 | if (ok != NN_SUCCESS) | ||
| 319 | return ok; | ||
| 320 | |||
| 321 | msg = get_outgoing_rfc2822_str(fp); | ||
| 322 | if (sock_write_all(SESSION(session)->sock, msg, strlen(msg)) < 0) { | ||
| 323 | log_warning(_("Error occurred while posting\n")); | ||
| 324 | g_free(msg); | ||
| 325 | return NN_SOCKET; | ||
| 326 | } | ||
| 327 | g_free(msg); | ||
| 328 | |||
| 329 | sock_write_all(SESSION(session)->sock, ".\r\n", 3); | ||
| 330 | if ((ok = nntp_ok(SESSION(session)->sock, buf)) != NN_SUCCESS) | ||
| 331 | return ok; | ||
| 332 | |||
| 333 | session_set_access_time(SESSION(session)); | ||
| 334 | |||
| 335 | return NN_SUCCESS; | ||
| 336 | } | ||
| 337 | |||
| 338 | gint nntp_newgroups(NNTPSession *session) | ||
| 339 | { | ||
| 340 | return NN_SUCCESS; | ||
| 341 | } | ||
| 342 | |||
| 343 | gint nntp_newnews(NNTPSession *session) | ||
| 344 | { | ||
| 345 | return NN_SUCCESS; | ||
| 346 | } | ||
| 347 | |||
| 348 | gint nntp_mode(NNTPSession *session, gboolean stream) | ||
| 349 | { | ||
| 350 | gint ok; | ||
| 351 | |||
| 352 | ok = nntp_gen_command(session, NULL, "MODE %s", | ||
| 353 | stream ? "STREAM" : "READER"); | ||
| 354 | |||
| 355 | return ok; | ||
| 356 | } | ||
| 357 | |||
| 358 | static gint nntp_ok(SockInfo *sock, gchar *argbuf) | ||
| 359 | { | ||
| 360 | gint ok; | ||
| 361 | gchar buf[NNTPBUFSIZE]; | ||
| 362 | |||
| 363 | if ((ok = nntp_gen_recv(sock, buf, sizeof(buf))) == NN_SUCCESS) { | ||
| 364 | if (strlen(buf) < 3) | ||
| 365 | return NN_ERROR; | ||
| 366 | |||
| 367 | if ((buf[0] == '1' || buf[0] == '2' || buf[0] == '3') && | ||
| 368 | (buf[3] == ' ' || buf[3] == '\0')) { | ||
| 369 | if (argbuf) | ||
| 370 | strcpy(argbuf, buf); | ||
| 371 | |||
| 372 | if (!strncmp(buf, "381", 3)) | ||
| 373 | return NN_AUTHCONT; | ||
| 374 | |||
| 375 | return NN_SUCCESS; | ||
| 376 | } else if (!strncmp(buf, "480", 3)) | ||
| 377 | return NN_AUTHREQ; | ||
| 378 | else | ||
| 379 | return NN_ERROR; | ||
| 380 | } | ||
| 381 | |||
| 382 | return ok; | ||
| 383 | } | ||
| 384 | |||
| 385 | static gint nntp_gen_send(SockInfo *sock, const gchar *format, ...) | ||
| 386 | { | ||
| 387 | gchar buf[NNTPBUFSIZE]; | ||
| 388 | va_list args; | ||
| 389 | |||
| 390 | va_start(args, format); | ||
| 391 | g_vsnprintf(buf, sizeof(buf), format, args); | ||
| 392 | va_end(args); | ||
| 393 | |||
| 394 | if (verbose) { | ||
| 395 | if (!g_ascii_strncasecmp(buf, "AUTHINFO PASS", 13)) | ||
| 396 | log_print("NNTP> AUTHINFO PASS ********\n"); | ||
| 397 | else | ||
| 398 | log_print("NNTP> %s\n", buf); | ||
| 399 | } | ||
| 400 | |||
| 401 | strcat(buf, "\r\n"); | ||
| 402 | if (sock_write_all(sock, buf, strlen(buf)) < 0) { | ||
| 403 | log_warning(_("Error occurred while sending command\n")); | ||
| 404 | return NN_SOCKET; | ||
| 405 | } | ||
| 406 | |||
| 407 | return NN_SUCCESS; | ||
| 408 | } | ||
| 409 | |||
| 410 | static gint nntp_gen_recv(SockInfo *sock, gchar *buf, gint size) | ||
| 411 | { | ||
| 412 | if (sock_gets(sock, buf, size) == -1) | ||
| 413 | return NN_SOCKET; | ||
| 414 | |||
| 415 | strretchomp(buf); | ||
| 416 | |||
| 417 | if (verbose) | ||
| 418 | log_print("NNTP< %s\n", buf); | ||
| 419 | |||
| 420 | return NN_SUCCESS; | ||
| 421 | } | ||
| 422 | |||
| 423 | static gint nntp_gen_command(NNTPSession *session, gchar *argbuf, | ||
| 424 | const gchar *format, ...) | ||
| 425 | { | ||
| 426 | gchar buf[NNTPBUFSIZE]; | ||
| 427 | va_list args; | ||
| 428 | gint ok; | ||
| 429 | SockInfo *sock; | ||
| 430 | |||
| 431 | va_start(args, format); | ||
| 432 | g_vsnprintf(buf, sizeof(buf), format, args); | ||
| 433 | va_end(args); | ||
| 434 | |||
| 435 | sock = SESSION(session)->sock; | ||
| 436 | ok = nntp_gen_send(sock, "%s", buf); | ||
| 437 | if (ok != NN_SUCCESS) | ||
| 438 | return ok; | ||
| 439 | ok = nntp_ok(sock, argbuf); | ||
| 440 | if (ok == NN_AUTHREQ) { | ||
| 441 | if (!session->userid || !session->passwd) { | ||
| 442 | session->auth_failed = TRUE; | ||
| 443 | return ok; | ||
| 444 | } | ||
| 445 | |||
| 446 | ok = nntp_gen_send(sock, "AUTHINFO USER %s", session->userid); | ||
| 447 | if (ok != NN_SUCCESS) | ||
| 448 | return ok; | ||
| 449 | ok = nntp_ok(sock, NULL); | ||
| 450 | if (ok == NN_AUTHCONT) { | ||
| 451 | ok = nntp_gen_send(sock, "AUTHINFO PASS %s", | ||
| 452 | session->passwd); | ||
| 453 | if (ok != NN_SUCCESS) | ||
| 454 | return ok; | ||
| 455 | ok = nntp_ok(sock, NULL); | ||
| 456 | } | ||
| 457 | if (ok != NN_SUCCESS) { | ||
| 458 | session->auth_failed = TRUE; | ||
| 459 | return ok; | ||
| 460 | } | ||
| 461 | |||
| 462 | ok = nntp_gen_send(sock, "%s", buf); | ||
| 463 | if (ok != NN_SUCCESS) | ||
| 464 | return ok; | ||
| 465 | ok = nntp_ok(sock, argbuf); | ||
| 466 | } | ||
| 467 | |||
| 468 | session_set_access_time(SESSION(session)); | ||
| 469 | |||
| 470 | return ok; | ||
| 471 | } | ||
diff --git a/libsylph/nntp.h b/libsylph/nntp.h new file mode 100644 index 0000000..045fae7 --- /dev/null +++ b/libsylph/nntp.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __NNTP_H__ | ||
| 21 | #define __NNTP_H__ | ||
| 22 | |||
| 23 | #include "session.h" | ||
| 24 | #if USE_SSL | ||
| 25 | # include "ssl.h" | ||
| 26 | #endif | ||
| 27 | #include "socks.h" | ||
| 28 | |||
| 29 | typedef struct _NNTPSession NNTPSession; | ||
| 30 | |||
| 31 | #define NNTP_SESSION(obj) ((NNTPSession *)obj) | ||
| 32 | |||
| 33 | struct _NNTPSession | ||
| 34 | { | ||
| 35 | Session session; | ||
| 36 | |||
| 37 | gchar *group; | ||
| 38 | |||
| 39 | gchar *userid; | ||
| 40 | gchar *passwd; | ||
| 41 | gboolean auth_failed; | ||
| 42 | }; | ||
| 43 | |||
| 44 | #define NN_SUCCESS 0 | ||
| 45 | #define NN_SOCKET 2 | ||
| 46 | #define NN_AUTHFAIL 3 | ||
| 47 | #define NN_PROTOCOL 4 | ||
| 48 | #define NN_SYNTAX 5 | ||
| 49 | #define NN_IOERR 6 | ||
| 50 | #define NN_ERROR 7 | ||
| 51 | #define NN_AUTHREQ 8 | ||
| 52 | #define NN_AUTHCONT 9 | ||
| 53 | |||
| 54 | #define NNTPBUFSIZE 8192 | ||
| 55 | |||
| 56 | #if USE_SSL | ||
| 57 | Session *nntp_session_new (const gchar *server, | ||
| 58 | gushort port, | ||
| 59 | gchar *buf, | ||
| 60 | const gchar *userid, | ||
| 61 | const gchar *passwd, | ||
| 62 | SSLType ssl_type); | ||
| 63 | Session *nntp_session_new_full (const gchar *server, | ||
| 64 | gushort port, | ||
| 65 | SocksInfo *socks_info, | ||
| 66 | gchar *buf, | ||
| 67 | const gchar *userid, | ||
| 68 | const gchar *passwd, | ||
| 69 | SSLType ssl_type); | ||
| 70 | #else | ||
| 71 | Session *nntp_session_new (const gchar *server, | ||
| 72 | gushort port, | ||
| 73 | gchar *buf, | ||
| 74 | const gchar *userid, | ||
| 75 | const gchar *passwd); | ||
| 76 | Session *nntp_session_new_full (const gchar *server, | ||
| 77 | gushort port, | ||
| 78 | SocksInfo *socks_info, | ||
| 79 | gchar *buf, | ||
| 80 | const gchar *userid, | ||
| 81 | const gchar *passwd); | ||
| 82 | #endif | ||
| 83 | |||
| 84 | gint nntp_group (NNTPSession *session, | ||
| 85 | const gchar *group, | ||
| 86 | gint *num, | ||
| 87 | gint *first, | ||
| 88 | gint *last); | ||
| 89 | gint nntp_get_article (NNTPSession *session, | ||
| 90 | const gchar *cmd, | ||
| 91 | gint num, | ||
| 92 | gchar **msgid); | ||
| 93 | gint nntp_article (NNTPSession *session, | ||
| 94 | gint num, | ||
| 95 | gchar **msgid); | ||
| 96 | gint nntp_body (NNTPSession *session, | ||
| 97 | gint num, | ||
| 98 | gchar **msgid); | ||
| 99 | gint nntp_head (NNTPSession *session, | ||
| 100 | gint num, | ||
| 101 | gchar **msgid); | ||
| 102 | gint nntp_stat (NNTPSession *session, | ||
| 103 | gint num, | ||
| 104 | gchar **msgid); | ||
| 105 | gint nntp_next (NNTPSession *session, | ||
| 106 | gint *num, | ||
| 107 | gchar **msgid); | ||
| 108 | gint nntp_xover (NNTPSession *session, | ||
| 109 | gint first, | ||
| 110 | gint last); | ||
| 111 | gint nntp_xhdr (NNTPSession *session, | ||
| 112 | const gchar *header, | ||
| 113 | gint first, | ||
| 114 | gint last); | ||
| 115 | gint nntp_list (NNTPSession *session); | ||
| 116 | gint nntp_post (NNTPSession *session, | ||
| 117 | FILE *fp); | ||
| 118 | gint nntp_newgroups (NNTPSession *session); | ||
| 119 | gint nntp_newnews (NNTPSession *session); | ||
| 120 | gint nntp_mode (NNTPSession *session, | ||
| 121 | gboolean stream); | ||
| 122 | |||
| 123 | #endif /* __NNTP_H__ */ | ||
diff --git a/libsylph/pop.c b/libsylph/pop.c new file mode 100644 index 0000000..8cb7f5c --- /dev/null +++ b/libsylph/pop.c | |||
| @@ -0,0 +1,927 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdarg.h> | ||
| 31 | #include <ctype.h> | ||
| 32 | #include <unistd.h> | ||
| 33 | #include <time.h> | ||
| 34 | #include <errno.h> | ||
| 35 | |||
| 36 | #include "pop.h" | ||
| 37 | #include "md5.h" | ||
| 38 | #include "prefs.h" | ||
| 39 | #include "prefs_account.h" | ||
| 40 | #include "utils.h" | ||
| 41 | #include "recv.h" | ||
| 42 | |||
| 43 | gint pop3_greeting_recv (Pop3Session *session, | ||
| 44 | const gchar *msg); | ||
| 45 | gint pop3_getauth_user_send (Pop3Session *session); | ||
| 46 | gint pop3_getauth_pass_send (Pop3Session *session); | ||
| 47 | gint pop3_getauth_apop_send (Pop3Session *session); | ||
| 48 | #if USE_SSL | ||
| 49 | gint pop3_stls_send (Pop3Session *session); | ||
| 50 | gint pop3_stls_recv (Pop3Session *session); | ||
| 51 | #endif | ||
| 52 | gint pop3_getrange_stat_send (Pop3Session *session); | ||
| 53 | gint pop3_getrange_stat_recv (Pop3Session *session, | ||
| 54 | const gchar *msg); | ||
| 55 | gint pop3_getrange_last_send (Pop3Session *session); | ||
| 56 | gint pop3_getrange_last_recv (Pop3Session *session, | ||
| 57 | const gchar *msg); | ||
| 58 | gint pop3_getrange_uidl_send (Pop3Session *session); | ||
| 59 | gint pop3_getrange_uidl_recv (Pop3Session *session, | ||
| 60 | const gchar *data, | ||
| 61 | guint len); | ||
| 62 | gint pop3_getsize_list_send (Pop3Session *session); | ||
| 63 | gint pop3_getsize_list_recv (Pop3Session *session, | ||
| 64 | const gchar *data, | ||
| 65 | guint len); | ||
| 66 | gint pop3_retr_send (Pop3Session *session); | ||
| 67 | gint pop3_retr_recv (Pop3Session *session, | ||
| 68 | FILE *fp, | ||
| 69 | guint len); | ||
| 70 | gint pop3_delete_send (Pop3Session *session); | ||
| 71 | gint pop3_delete_recv (Pop3Session *session); | ||
| 72 | gint pop3_logout_send (Pop3Session *session); | ||
| 73 | |||
| 74 | void pop3_gen_send (Pop3Session *session, | ||
| 75 | const gchar *format, ...); | ||
| 76 | |||
| 77 | static void pop3_session_destroy (Session *session); | ||
| 78 | |||
| 79 | gint pop3_write_msg_to_file (const gchar *file, | ||
| 80 | FILE *src_fp, | ||
| 81 | guint len); | ||
| 82 | |||
| 83 | static Pop3State pop3_lookup_next (Pop3Session *session); | ||
| 84 | |||
| 85 | Pop3ErrorValue pop3_ok (Pop3Session *session, | ||
| 86 | const gchar *msg); | ||
| 87 | |||
| 88 | static gint pop3_session_recv_msg (Session *session, | ||
| 89 | const gchar *msg); | ||
| 90 | static gint pop3_session_recv_data_finished (Session *session, | ||
| 91 | guchar *data, | ||
| 92 | guint len); | ||
| 93 | static gint pop3_session_recv_data_as_file_finished | ||
| 94 | (Session *session, | ||
| 95 | FILE *fp, | ||
| 96 | guint len); | ||
| 97 | |||
| 98 | |||
| 99 | gint pop3_greeting_recv(Pop3Session *session, const gchar *msg) | ||
| 100 | { | ||
| 101 | session->state = POP3_GREETING; | ||
| 102 | |||
| 103 | session->greeting = g_strdup(msg); | ||
| 104 | return PS_SUCCESS; | ||
| 105 | } | ||
| 106 | |||
| 107 | #if USE_SSL | ||
| 108 | gint pop3_stls_send(Pop3Session *session) | ||
| 109 | { | ||
| 110 | session->state = POP3_STLS; | ||
| 111 | pop3_gen_send(session, "STLS"); | ||
| 112 | return PS_SUCCESS; | ||
| 113 | } | ||
| 114 | |||
| 115 | gint pop3_stls_recv(Pop3Session *session) | ||
| 116 | { | ||
| 117 | if (session_start_tls(SESSION(session)) < 0) { | ||
| 118 | session->error_val = PS_SOCKET; | ||
| 119 | return PS_SOCKET; | ||
| 120 | } | ||
| 121 | return PS_SUCCESS; | ||
| 122 | } | ||
| 123 | #endif /* USE_SSL */ | ||
| 124 | |||
| 125 | gint pop3_getauth_user_send(Pop3Session *session) | ||
| 126 | { | ||
| 127 | g_return_val_if_fail(session->user != NULL, -1); | ||
| 128 | |||
| 129 | session->state = POP3_GETAUTH_USER; | ||
| 130 | pop3_gen_send(session, "USER %s", session->user); | ||
| 131 | return PS_SUCCESS; | ||
| 132 | } | ||
| 133 | |||
| 134 | gint pop3_getauth_pass_send(Pop3Session *session) | ||
| 135 | { | ||
| 136 | g_return_val_if_fail(session->pass != NULL, -1); | ||
| 137 | |||
| 138 | session->state = POP3_GETAUTH_PASS; | ||
| 139 | pop3_gen_send(session, "PASS %s", session->pass); | ||
| 140 | return PS_SUCCESS; | ||
| 141 | } | ||
| 142 | |||
| 143 | gint pop3_getauth_apop_send(Pop3Session *session) | ||
| 144 | { | ||
| 145 | gchar *start, *end; | ||
| 146 | gchar *apop_str; | ||
| 147 | SMD5 *md5; | ||
| 148 | gchar *md5sum; | ||
| 149 | |||
| 150 | g_return_val_if_fail(session->user != NULL, -1); | ||
| 151 | g_return_val_if_fail(session->pass != NULL, -1); | ||
| 152 | |||
| 153 | session->state = POP3_GETAUTH_APOP; | ||
| 154 | |||
| 155 | if ((start = strchr(session->greeting, '<')) == NULL) { | ||
| 156 | log_warning(_("Required APOP timestamp not found " | ||
| 157 | "in greeting\n")); | ||
| 158 | session->error_val = PS_PROTOCOL; | ||
| 159 | return PS_PROTOCOL; | ||
| 160 | } | ||
| 161 | |||
| 162 | if ((end = strchr(start, '>')) == NULL || end == start + 1) { | ||
| 163 | log_warning(_("Timestamp syntax error in greeting\n")); | ||
| 164 | session->error_val = PS_PROTOCOL; | ||
| 165 | return PS_PROTOCOL; | ||
| 166 | } | ||
| 167 | |||
| 168 | *(end + 1) = '\0'; | ||
| 169 | |||
| 170 | if (!is_ascii_str(start) || strchr(start, '@') == NULL) { | ||
| 171 | log_warning(_("Invalid timestamp in greeting\n")); | ||
| 172 | session->error_val = PS_PROTOCOL; | ||
| 173 | return PS_PROTOCOL; | ||
| 174 | } | ||
| 175 | |||
| 176 | apop_str = g_strconcat(start, session->pass, NULL); | ||
| 177 | md5 = s_gnet_md5_new((guchar *)apop_str, strlen(apop_str)); | ||
| 178 | md5sum = s_gnet_md5_get_string(md5); | ||
| 179 | |||
| 180 | pop3_gen_send(session, "APOP %s %s", session->user, md5sum); | ||
| 181 | |||
| 182 | g_free(md5sum); | ||
| 183 | s_gnet_md5_delete(md5); | ||
| 184 | g_free(apop_str); | ||
| 185 | |||
| 186 | return PS_SUCCESS; | ||
| 187 | } | ||
| 188 | |||
| 189 | gint pop3_getrange_stat_send(Pop3Session *session) | ||
| 190 | { | ||
| 191 | session->state = POP3_GETRANGE_STAT; | ||
| 192 | pop3_gen_send(session, "STAT"); | ||
| 193 | return PS_SUCCESS; | ||
| 194 | } | ||
| 195 | |||
| 196 | gint pop3_getrange_stat_recv(Pop3Session *session, const gchar *msg) | ||
| 197 | { | ||
| 198 | if (sscanf(msg, "%d %lld", &session->count, &session->total_bytes) != 2) { | ||
| 199 | log_warning(_("POP3 protocol error\n")); | ||
| 200 | session->error_val = PS_PROTOCOL; | ||
| 201 | return PS_PROTOCOL; | ||
| 202 | } else { | ||
| 203 | if (session->count == 0) { | ||
| 204 | session->uidl_is_valid = TRUE; | ||
| 205 | } else { | ||
| 206 | session->msg = g_new0(Pop3MsgInfo, session->count + 1); | ||
| 207 | session->cur_msg = 1; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | return PS_SUCCESS; | ||
| 212 | } | ||
| 213 | |||
| 214 | gint pop3_getrange_last_send(Pop3Session *session) | ||
| 215 | { | ||
| 216 | session->state = POP3_GETRANGE_LAST; | ||
| 217 | pop3_gen_send(session, "LAST"); | ||
| 218 | return PS_SUCCESS; | ||
| 219 | } | ||
| 220 | |||
| 221 | gint pop3_getrange_last_recv(Pop3Session *session, const gchar *msg) | ||
| 222 | { | ||
| 223 | gint last; | ||
| 224 | |||
| 225 | if (sscanf(msg, "%d", &last) == 0) { | ||
| 226 | log_warning(_("POP3 protocol error\n")); | ||
| 227 | session->error_val = PS_PROTOCOL; | ||
| 228 | return PS_PROTOCOL; | ||
| 229 | } else { | ||
| 230 | if (session->count > last) { | ||
| 231 | session->new_msg_exist = TRUE; | ||
| 232 | session->cur_msg = last + 1; | ||
| 233 | } else | ||
| 234 | session->cur_msg = 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | return PS_SUCCESS; | ||
| 238 | } | ||
| 239 | |||
| 240 | gint pop3_getrange_uidl_send(Pop3Session *session) | ||
| 241 | { | ||
| 242 | session->state = POP3_GETRANGE_UIDL; | ||
| 243 | pop3_gen_send(session, "UIDL"); | ||
| 244 | return PS_SUCCESS; | ||
| 245 | } | ||
| 246 | |||
| 247 | gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data, guint len) | ||
| 248 | { | ||
| 249 | gchar id[IDLEN + 1]; | ||
| 250 | gchar buf[POPBUFSIZE]; | ||
| 251 | gint buf_len; | ||
| 252 | gint num; | ||
| 253 | time_t recv_time; | ||
| 254 | const gchar *p = data; | ||
| 255 | const gchar *lastp = data + len; | ||
| 256 | const gchar *newline; | ||
| 257 | |||
| 258 | while (p < lastp) { | ||
| 259 | if ((newline = memchr(p, '\r', lastp - p)) == NULL) | ||
| 260 | return PS_PROTOCOL; | ||
| 261 | buf_len = MIN(newline - p, sizeof(buf) - 1); | ||
| 262 | memcpy(buf, p, buf_len); | ||
| 263 | buf[buf_len] = '\0'; | ||
| 264 | |||
| 265 | p = newline + 1; | ||
| 266 | if (p < lastp && *p == '\n') p++; | ||
| 267 | |||
| 268 | if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2 || | ||
| 269 | num <= 0 || num > session->count) { | ||
| 270 | log_warning(_("invalid UIDL response: %s\n"), buf); | ||
| 271 | continue; | ||
| 272 | } | ||
| 273 | |||
| 274 | session->msg[num].uidl = g_strdup(id); | ||
| 275 | |||
| 276 | recv_time = GPOINTER_TO_INT(g_hash_table_lookup(session->uidl_table, id)); | ||
| 277 | session->msg[num].recv_time = recv_time; | ||
| 278 | |||
| 279 | if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE) | ||
| 280 | session->msg[num].received = TRUE; | ||
| 281 | |||
| 282 | if (!session->new_msg_exist && | ||
| 283 | (session->ac_prefs->getall || recv_time == RECV_TIME_NONE || | ||
| 284 | session->ac_prefs->rmmail)) { | ||
| 285 | session->cur_msg = num; | ||
| 286 | session->new_msg_exist = TRUE; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | session->uidl_is_valid = TRUE; | ||
| 291 | return PS_SUCCESS; | ||
| 292 | } | ||
| 293 | |||
| 294 | gint pop3_getsize_list_send(Pop3Session *session) | ||
| 295 | { | ||
| 296 | session->state = POP3_GETSIZE_LIST; | ||
| 297 | pop3_gen_send(session, "LIST"); | ||
| 298 | return PS_SUCCESS; | ||
| 299 | } | ||
| 300 | |||
| 301 | gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data, guint len) | ||
| 302 | { | ||
| 303 | gchar buf[POPBUFSIZE]; | ||
| 304 | gint buf_len; | ||
| 305 | guint num, size; | ||
| 306 | const gchar *p = data; | ||
| 307 | const gchar *lastp = data + len; | ||
| 308 | const gchar *newline; | ||
| 309 | |||
| 310 | while (p < lastp) { | ||
| 311 | if ((newline = memchr(p, '\r', lastp - p)) == NULL) | ||
| 312 | return PS_PROTOCOL; | ||
| 313 | buf_len = MIN(newline - p, sizeof(buf) - 1); | ||
| 314 | memcpy(buf, p, buf_len); | ||
| 315 | buf[buf_len] = '\0'; | ||
| 316 | |||
| 317 | p = newline + 1; | ||
| 318 | if (p < lastp && *p == '\n') p++; | ||
| 319 | |||
| 320 | if (sscanf(buf, "%u %u", &num, &size) != 2) { | ||
| 321 | session->error_val = PS_PROTOCOL; | ||
| 322 | return PS_PROTOCOL; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (num > 0 && num <= session->count) | ||
| 326 | session->msg[num].size = size; | ||
| 327 | if (num > 0 && num < session->cur_msg) | ||
| 328 | session->cur_total_bytes += size; | ||
| 329 | } | ||
| 330 | |||
| 331 | return PS_SUCCESS; | ||
| 332 | } | ||
| 333 | |||
| 334 | gint pop3_retr_send(Pop3Session *session) | ||
| 335 | { | ||
| 336 | session->state = POP3_RETR; | ||
| 337 | pop3_gen_send(session, "RETR %d", session->cur_msg); | ||
| 338 | return PS_SUCCESS; | ||
| 339 | } | ||
| 340 | |||
| 341 | gint pop3_retr_recv(Pop3Session *session, FILE *fp, guint len) | ||
| 342 | { | ||
| 343 | gchar *file; | ||
| 344 | gint drop_ok; | ||
| 345 | |||
| 346 | file = get_tmp_file(); | ||
| 347 | if (pop3_write_msg_to_file(file, fp, len) < 0) { | ||
| 348 | g_free(file); | ||
| 349 | session->error_val = PS_IOERR; | ||
| 350 | return PS_IOERR; | ||
| 351 | } | ||
| 352 | |||
| 353 | drop_ok = session->drop_message(session, file); | ||
| 354 | g_unlink(file); | ||
| 355 | g_free(file); | ||
| 356 | if (drop_ok < 0) { | ||
| 357 | session->error_val = PS_IOERR; | ||
| 358 | return PS_IOERR; | ||
| 359 | } | ||
| 360 | |||
| 361 | session->cur_total_bytes += session->msg[session->cur_msg].size; | ||
| 362 | session->cur_total_recv_bytes += session->msg[session->cur_msg].size; | ||
| 363 | session->cur_total_num++; | ||
| 364 | |||
| 365 | session->msg[session->cur_msg].received = TRUE; | ||
| 366 | session->msg[session->cur_msg].recv_time = | ||
| 367 | drop_ok == DROP_DONT_RECEIVE ? RECV_TIME_KEEP | ||
| 368 | : drop_ok == DROP_DELETE ? RECV_TIME_DELETE | ||
| 369 | : session->current_time; | ||
| 370 | |||
| 371 | return PS_SUCCESS; | ||
| 372 | } | ||
| 373 | |||
| 374 | gint pop3_delete_send(Pop3Session *session) | ||
| 375 | { | ||
| 376 | session->state = POP3_DELETE; | ||
| 377 | pop3_gen_send(session, "DELE %d", session->cur_msg); | ||
| 378 | return PS_SUCCESS; | ||
| 379 | } | ||
| 380 | |||
| 381 | gint pop3_delete_recv(Pop3Session *session) | ||
| 382 | { | ||
| 383 | session->msg[session->cur_msg].recv_time = RECV_TIME_DELETE; | ||
| 384 | session->msg[session->cur_msg].deleted = TRUE; | ||
| 385 | return PS_SUCCESS; | ||
| 386 | } | ||
| 387 | |||
| 388 | gint pop3_logout_send(Pop3Session *session) | ||
| 389 | { | ||
| 390 | session->state = POP3_LOGOUT; | ||
| 391 | pop3_gen_send(session, "QUIT"); | ||
| 392 | return PS_SUCCESS; | ||
| 393 | } | ||
| 394 | |||
| 395 | void pop3_gen_send(Pop3Session *session, const gchar *format, ...) | ||
| 396 | { | ||
| 397 | gchar buf[POPBUFSIZE + 1]; | ||
| 398 | va_list args; | ||
| 399 | |||
| 400 | va_start(args, format); | ||
| 401 | g_vsnprintf(buf, sizeof(buf) - 2, format, args); | ||
| 402 | va_end(args); | ||
| 403 | |||
| 404 | if (!g_ascii_strncasecmp(buf, "PASS ", 5)) | ||
| 405 | log_print("POP3> PASS ********\n"); | ||
| 406 | else | ||
| 407 | log_print("POP3> %s\n", buf); | ||
| 408 | |||
| 409 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 410 | } | ||
| 411 | |||
| 412 | Session *pop3_session_new(PrefsAccount *account) | ||
| 413 | { | ||
| 414 | Pop3Session *session; | ||
| 415 | |||
| 416 | g_return_val_if_fail(account != NULL, NULL); | ||
| 417 | |||
| 418 | session = g_new0(Pop3Session, 1); | ||
| 419 | |||
| 420 | session_init(SESSION(session)); | ||
| 421 | |||
| 422 | SESSION(session)->type = SESSION_POP3; | ||
| 423 | |||
| 424 | SESSION(session)->recv_msg = pop3_session_recv_msg; | ||
| 425 | SESSION(session)->send_data_finished = NULL; | ||
| 426 | SESSION(session)->recv_data_finished = pop3_session_recv_data_finished; | ||
| 427 | SESSION(session)->recv_data_as_file_finished = | ||
| 428 | pop3_session_recv_data_as_file_finished; | ||
| 429 | |||
| 430 | SESSION(session)->destroy = pop3_session_destroy; | ||
| 431 | |||
| 432 | session->state = POP3_READY; | ||
| 433 | session->ac_prefs = account; | ||
| 434 | session->uidl_table = pop3_get_uidl_table(account); | ||
| 435 | session->current_time = time(NULL); | ||
| 436 | session->error_val = PS_SUCCESS; | ||
| 437 | session->error_msg = NULL; | ||
| 438 | |||
| 439 | session->user = g_strdup(account->userid); | ||
| 440 | session->pass = account->passwd ? g_strdup(account->passwd) : | ||
| 441 | account->tmp_pass ? g_strdup(account->tmp_pass) : NULL; | ||
| 442 | |||
| 443 | SESSION(session)->server = g_strdup(account->recv_server); | ||
| 444 | |||
| 445 | #if USE_SSL | ||
| 446 | SESSION(session)->port = account->set_popport ? | ||
| 447 | account->popport : account->ssl_pop == SSL_TUNNEL ? 995 : 110; | ||
| 448 | SESSION(session)->ssl_type = account->ssl_pop; | ||
| 449 | if (account->ssl_pop != SSL_NONE) | ||
| 450 | SESSION(session)->nonblocking = account->use_nonblocking_ssl; | ||
| 451 | #else | ||
| 452 | SESSION(session)->port = account->set_popport ? account->popport : 110; | ||
| 453 | #endif | ||
| 454 | |||
| 455 | return SESSION(session); | ||
| 456 | } | ||
| 457 | |||
| 458 | static void pop3_session_destroy(Session *session) | ||
| 459 | { | ||
| 460 | Pop3Session *pop3_session = POP3_SESSION(session); | ||
| 461 | gint n; | ||
| 462 | |||
| 463 | g_return_if_fail(session != NULL); | ||
| 464 | |||
| 465 | for (n = 1; n <= pop3_session->count; n++) | ||
| 466 | g_free(pop3_session->msg[n].uidl); | ||
| 467 | g_free(pop3_session->msg); | ||
| 468 | |||
| 469 | if (pop3_session->uidl_table) { | ||
| 470 | hash_free_strings(pop3_session->uidl_table); | ||
| 471 | g_hash_table_destroy(pop3_session->uidl_table); | ||
| 472 | } | ||
| 473 | |||
| 474 | g_free(pop3_session->greeting); | ||
| 475 | g_free(pop3_session->user); | ||
| 476 | g_free(pop3_session->pass); | ||
| 477 | g_free(pop3_session->error_msg); | ||
| 478 | } | ||
| 479 | |||
| 480 | GHashTable *pop3_get_uidl_table(PrefsAccount *ac_prefs) | ||
| 481 | { | ||
| 482 | GHashTable *table; | ||
| 483 | gchar *path; | ||
| 484 | FILE *fp; | ||
| 485 | gchar buf[POPBUFSIZE]; | ||
| 486 | gchar uidl[POPBUFSIZE]; | ||
| 487 | time_t recv_time; | ||
| 488 | time_t now; | ||
| 489 | gchar *uid; | ||
| 490 | |||
| 491 | table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 492 | |||
| 493 | uid = uriencode_for_filename(ac_prefs->userid); | ||
| 494 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 495 | UIDL_DIR, G_DIR_SEPARATOR_S, ac_prefs->recv_server, | ||
| 496 | "-", uid, NULL); | ||
| 497 | g_free(uid); | ||
| 498 | if ((fp = g_fopen(path, "rb")) == NULL) { | ||
| 499 | if (ENOENT != errno) FILE_OP_ERROR(path, "fopen"); | ||
| 500 | g_free(path); | ||
| 501 | return table; | ||
| 502 | } | ||
| 503 | g_free(path); | ||
| 504 | |||
| 505 | now = time(NULL); | ||
| 506 | |||
| 507 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 508 | strretchomp(buf); | ||
| 509 | recv_time = RECV_TIME_NONE; | ||
| 510 | if (sscanf(buf, "%s\t%ld", uidl, &recv_time) != 2) { | ||
| 511 | if (sscanf(buf, "%s", uidl) != 1) | ||
| 512 | continue; | ||
| 513 | else | ||
| 514 | recv_time = now; | ||
| 515 | } | ||
| 516 | if (recv_time == RECV_TIME_NONE) | ||
| 517 | recv_time = RECV_TIME_RECEIVED; | ||
| 518 | g_hash_table_insert(table, g_strdup(uidl), | ||
| 519 | GINT_TO_POINTER(recv_time)); | ||
| 520 | } | ||
| 521 | |||
| 522 | fclose(fp); | ||
| 523 | return table; | ||
| 524 | } | ||
| 525 | |||
| 526 | gint pop3_write_uidl_list(Pop3Session *session) | ||
| 527 | { | ||
| 528 | gchar *path; | ||
| 529 | PrefFile *pfile; | ||
| 530 | Pop3MsgInfo *msg; | ||
| 531 | gint n; | ||
| 532 | gchar *uid; | ||
| 533 | |||
| 534 | if (!session->uidl_is_valid) return 0; | ||
| 535 | |||
| 536 | uid = uriencode_for_filename(session->ac_prefs->userid); | ||
| 537 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 538 | UIDL_DIR, G_DIR_SEPARATOR_S, | ||
| 539 | session->ac_prefs->recv_server, | ||
| 540 | "-", uid, NULL); | ||
| 541 | g_free(uid); | ||
| 542 | if ((pfile = prefs_file_open(path)) == NULL) { | ||
| 543 | g_free(path); | ||
| 544 | return -1; | ||
| 545 | } | ||
| 546 | prefs_file_set_backup_generation(pfile, 0); | ||
| 547 | |||
| 548 | for (n = 1; n <= session->count; n++) { | ||
| 549 | msg = &session->msg[n]; | ||
| 550 | if (!msg->uidl || !msg->received) | ||
| 551 | continue; | ||
| 552 | if (session->state == POP3_DONE && msg->deleted) | ||
| 553 | continue; | ||
| 554 | fprintf(pfile->fp, "%s\t%ld\n", msg->uidl, msg->recv_time); | ||
| 555 | } | ||
| 556 | |||
| 557 | if (prefs_file_close(pfile) < 0) | ||
| 558 | g_warning("%s: failed to write UIDL list.\n", path); | ||
| 559 | |||
| 560 | g_free(path); | ||
| 561 | |||
| 562 | return 0; | ||
| 563 | } | ||
| 564 | |||
| 565 | gint pop3_write_msg_to_file(const gchar *file, FILE *src_fp, guint len) | ||
| 566 | { | ||
| 567 | FILE *fp; | ||
| 568 | gchar buf[BUFFSIZE]; | ||
| 569 | gchar last_ch = '\0'; | ||
| 570 | |||
| 571 | g_return_val_if_fail(file != NULL, -1); | ||
| 572 | |||
| 573 | if ((fp = g_fopen(file, "wb")) == NULL) { | ||
| 574 | FILE_OP_ERROR(file, "fopen"); | ||
| 575 | return -1; | ||
| 576 | } | ||
| 577 | |||
| 578 | if (change_file_mode_rw(fp, file) < 0) | ||
| 579 | FILE_OP_ERROR(file, "chmod"); | ||
| 580 | |||
| 581 | while (fgets(buf, sizeof(buf), src_fp) != NULL) { | ||
| 582 | gchar *p = buf; | ||
| 583 | gint len; | ||
| 584 | |||
| 585 | len = strlen(buf); | ||
| 586 | if (len > 0) { | ||
| 587 | last_ch = buf[len - 1]; | ||
| 588 | if (last_ch == '\n' && len > 1 && | ||
| 589 | buf[len - 2] == '\r') { | ||
| 590 | buf[len - 2] = '\n'; | ||
| 591 | buf[len - 1] = '\0'; | ||
| 592 | } else if (last_ch == '\r') | ||
| 593 | buf[len - 1] = '\0'; | ||
| 594 | } else | ||
| 595 | last_ch = '\0'; | ||
| 596 | |||
| 597 | if ((last_ch == '\0' || last_ch == '\n') && | ||
| 598 | *p == '.' && *(p + 1) == '.') | ||
| 599 | p++; | ||
| 600 | |||
| 601 | if (fputs(p, fp) == EOF) { | ||
| 602 | FILE_OP_ERROR(file, "fputs"); | ||
| 603 | g_warning("can't write to file: %s\n", file); | ||
| 604 | fclose(fp); | ||
| 605 | g_unlink(file); | ||
| 606 | return -1; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | |||
| 610 | if (ferror(src_fp)) { | ||
| 611 | FILE_OP_ERROR(file, "fgets"); | ||
| 612 | fclose(fp); | ||
| 613 | g_unlink(file); | ||
| 614 | return -1; | ||
| 615 | } | ||
| 616 | |||
| 617 | if (fclose(fp) == EOF) { | ||
| 618 | FILE_OP_ERROR(file, "fclose"); | ||
| 619 | g_unlink(file); | ||
| 620 | return -1; | ||
| 621 | } | ||
| 622 | |||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | static Pop3State pop3_lookup_next(Pop3Session *session) | ||
| 627 | { | ||
| 628 | Pop3MsgInfo *msg; | ||
| 629 | PrefsAccount *ac = session->ac_prefs; | ||
| 630 | gint size; | ||
| 631 | gboolean size_limit_over; | ||
| 632 | |||
| 633 | for (;;) { | ||
| 634 | msg = &session->msg[session->cur_msg]; | ||
| 635 | size = msg->size; | ||
| 636 | size_limit_over = | ||
| 637 | (ac->enable_size_limit && | ||
| 638 | ac->size_limit > 0 && | ||
| 639 | size > ac->size_limit * 1024); | ||
| 640 | |||
| 641 | if (msg->recv_time == RECV_TIME_DELETE || | ||
| 642 | (ac->rmmail && | ||
| 643 | msg->recv_time != RECV_TIME_NONE && | ||
| 644 | msg->recv_time != RECV_TIME_KEEP && | ||
| 645 | session->current_time - msg->recv_time >= | ||
| 646 | ac->msg_leave_time * 24 * 60 * 60)) { | ||
| 647 | log_print(_("POP3: Deleting expired message %d\n"), | ||
| 648 | session->cur_msg); | ||
| 649 | session->cur_total_bytes += size; | ||
| 650 | pop3_delete_send(session); | ||
| 651 | return POP3_DELETE; | ||
| 652 | } | ||
| 653 | |||
| 654 | if (size_limit_over && !msg->received) { | ||
| 655 | log_print | ||
| 656 | (_("POP3: Skipping message %d (%d bytes)\n"), | ||
| 657 | session->cur_msg, size); | ||
| 658 | session->skipped_num++; | ||
| 659 | } | ||
| 660 | |||
| 661 | if (size == 0 || msg->received || size_limit_over) { | ||
| 662 | session->cur_total_bytes += size; | ||
| 663 | if (session->cur_msg == session->count) { | ||
| 664 | pop3_logout_send(session); | ||
| 665 | return POP3_LOGOUT; | ||
| 666 | } else | ||
| 667 | session->cur_msg++; | ||
| 668 | } else | ||
| 669 | break; | ||
| 670 | } | ||
| 671 | |||
| 672 | pop3_retr_send(session); | ||
| 673 | |||
| 674 | return POP3_RETR; | ||
| 675 | } | ||
| 676 | |||
| 677 | Pop3ErrorValue pop3_ok(Pop3Session *session, const gchar *msg) | ||
| 678 | { | ||
| 679 | Pop3ErrorValue ok; | ||
| 680 | |||
| 681 | log_print("POP3< %s\n", msg); | ||
| 682 | |||
| 683 | if (!strncmp(msg, "+OK", 3)) | ||
| 684 | ok = PS_SUCCESS; | ||
| 685 | else if (!strncmp(msg, "-ERR", 4)) { | ||
| 686 | if (strstr(msg + 4, "lock") || | ||
| 687 | strstr(msg + 4, "Lock") || | ||
| 688 | strstr(msg + 4, "LOCK") || | ||
| 689 | strstr(msg + 4, "wait")) { | ||
| 690 | log_warning(_("mailbox is locked\n")); | ||
| 691 | ok = PS_LOCKBUSY; | ||
| 692 | } else if (strcasestr(msg + 4, "timeout")) { | ||
| 693 | log_warning(_("session timeout\n")); | ||
| 694 | ok = PS_ERROR; | ||
| 695 | } else { | ||
| 696 | switch (session->state) { | ||
| 697 | #if USE_SSL | ||
| 698 | case POP3_STLS: | ||
| 699 | log_warning(_("can't start TLS session\n")); | ||
| 700 | ok = PS_ERROR; | ||
| 701 | break; | ||
| 702 | #endif | ||
| 703 | case POP3_GETAUTH_USER: | ||
| 704 | case POP3_GETAUTH_PASS: | ||
| 705 | case POP3_GETAUTH_APOP: | ||
| 706 | log_warning(_("error occurred on authentication\n")); | ||
| 707 | ok = PS_AUTHFAIL; | ||
| 708 | break; | ||
| 709 | case POP3_GETRANGE_LAST: | ||
| 710 | case POP3_GETRANGE_UIDL: | ||
| 711 | log_warning(_("command not supported\n")); | ||
| 712 | ok = PS_NOTSUPPORTED; | ||
| 713 | break; | ||
| 714 | default: | ||
| 715 | log_warning(_("error occurred on POP3 session\n")); | ||
| 716 | ok = PS_ERROR; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | g_free(session->error_msg); | ||
| 721 | session->error_msg = g_strdup(msg); | ||
| 722 | fprintf(stderr, "POP3: %s\n", msg); | ||
| 723 | } else | ||
| 724 | ok = PS_PROTOCOL; | ||
| 725 | |||
| 726 | /* don't overwrite previous error on logout */ | ||
| 727 | if (session->state != POP3_LOGOUT) | ||
| 728 | session->error_val = ok; | ||
| 729 | |||
| 730 | return ok; | ||
| 731 | } | ||
| 732 | |||
| 733 | static gint pop3_session_recv_msg(Session *session, const gchar *msg) | ||
| 734 | { | ||
| 735 | Pop3Session *pop3_session = POP3_SESSION(session); | ||
| 736 | gint val = PS_SUCCESS; | ||
| 737 | const gchar *body; | ||
| 738 | |||
| 739 | body = msg; | ||
| 740 | if (pop3_session->state != POP3_GETRANGE_UIDL_RECV && | ||
| 741 | pop3_session->state != POP3_GETSIZE_LIST_RECV) { | ||
| 742 | val = pop3_ok(pop3_session, msg); | ||
| 743 | if (val != PS_SUCCESS) { | ||
| 744 | if (val == PS_SOCKET) { | ||
| 745 | pop3_session->state = POP3_ERROR; | ||
| 746 | return -1; | ||
| 747 | } | ||
| 748 | if (val != PS_NOTSUPPORTED) { | ||
| 749 | if (pop3_session->state != POP3_LOGOUT) { | ||
| 750 | if (pop3_logout_send(pop3_session) == PS_SUCCESS) | ||
| 751 | return 0; | ||
| 752 | else | ||
| 753 | return -1; | ||
| 754 | } | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | if (*body == '+' || *body == '-') | ||
| 759 | body++; | ||
| 760 | while (g_ascii_isalpha(*body)) | ||
| 761 | body++; | ||
| 762 | while (g_ascii_isspace(*body)) | ||
| 763 | body++; | ||
| 764 | } | ||
| 765 | |||
| 766 | switch (pop3_session->state) { | ||
| 767 | case POP3_READY: | ||
| 768 | case POP3_GREETING: | ||
| 769 | val = pop3_greeting_recv(pop3_session, body); | ||
| 770 | #if USE_SSL | ||
| 771 | if (pop3_session->ac_prefs->ssl_pop == SSL_STARTTLS) | ||
| 772 | val = pop3_stls_send(pop3_session); | ||
| 773 | else | ||
| 774 | #endif | ||
| 775 | if (pop3_session->ac_prefs->use_apop_auth) | ||
| 776 | val = pop3_getauth_apop_send(pop3_session); | ||
| 777 | else | ||
| 778 | val = pop3_getauth_user_send(pop3_session); | ||
| 779 | break; | ||
| 780 | #if USE_SSL | ||
| 781 | case POP3_STLS: | ||
| 782 | if ((val = pop3_stls_recv(pop3_session)) != PS_SUCCESS) | ||
| 783 | return -1; | ||
| 784 | if (pop3_session->ac_prefs->use_apop_auth) | ||
| 785 | val = pop3_getauth_apop_send(pop3_session); | ||
| 786 | else | ||
| 787 | val = pop3_getauth_user_send(pop3_session); | ||
| 788 | break; | ||
| 789 | #endif | ||
| 790 | case POP3_GETAUTH_USER: | ||
| 791 | val = pop3_getauth_pass_send(pop3_session); | ||
| 792 | break; | ||
| 793 | case POP3_GETAUTH_PASS: | ||
| 794 | case POP3_GETAUTH_APOP: | ||
| 795 | if (pop3_session->auth_only) | ||
| 796 | val = pop3_logout_send(pop3_session); | ||
| 797 | else | ||
| 798 | val = pop3_getrange_stat_send(pop3_session); | ||
| 799 | break; | ||
| 800 | case POP3_GETRANGE_STAT: | ||
| 801 | if ((val = pop3_getrange_stat_recv(pop3_session, body)) != PS_SUCCESS) | ||
| 802 | return -1; | ||
| 803 | if (pop3_session->count > 0) | ||
| 804 | val = pop3_getrange_uidl_send(pop3_session); | ||
| 805 | else | ||
| 806 | val = pop3_logout_send(pop3_session); | ||
| 807 | break; | ||
| 808 | case POP3_GETRANGE_LAST: | ||
| 809 | if (val == PS_NOTSUPPORTED) | ||
| 810 | pop3_session->error_val = PS_SUCCESS; | ||
| 811 | else if ((val = pop3_getrange_last_recv | ||
| 812 | (pop3_session, body)) != PS_SUCCESS) | ||
| 813 | return -1; | ||
| 814 | if (pop3_session->cur_msg > 0) | ||
| 815 | val = pop3_getsize_list_send(pop3_session); | ||
| 816 | else | ||
| 817 | val = pop3_logout_send(pop3_session); | ||
| 818 | break; | ||
| 819 | case POP3_GETRANGE_UIDL: | ||
| 820 | if (val == PS_NOTSUPPORTED) { | ||
| 821 | pop3_session->error_val = PS_SUCCESS; | ||
| 822 | val = pop3_getrange_last_send(pop3_session); | ||
| 823 | } else { | ||
| 824 | pop3_session->state = POP3_GETRANGE_UIDL_RECV; | ||
| 825 | val = session_recv_data(session, 0, ".\r\n"); | ||
| 826 | } | ||
| 827 | break; | ||
| 828 | case POP3_GETSIZE_LIST: | ||
| 829 | pop3_session->state = POP3_GETSIZE_LIST_RECV; | ||
| 830 | val = session_recv_data(session, 0, ".\r\n"); | ||
| 831 | break; | ||
| 832 | case POP3_RETR: | ||
| 833 | pop3_session->state = POP3_RETR_RECV; | ||
| 834 | val = session_recv_data_as_file(session, 0, ".\r\n"); | ||
| 835 | break; | ||
| 836 | case POP3_DELETE: | ||
| 837 | val = pop3_delete_recv(pop3_session); | ||
| 838 | if (pop3_session->cur_msg == pop3_session->count) | ||
| 839 | val = pop3_logout_send(pop3_session); | ||
| 840 | else { | ||
| 841 | pop3_session->cur_msg++; | ||
| 842 | if (pop3_lookup_next(pop3_session) == POP3_ERROR) | ||
| 843 | return -1; | ||
| 844 | } | ||
| 845 | break; | ||
| 846 | case POP3_LOGOUT: | ||
| 847 | if (val == PS_SUCCESS) | ||
| 848 | pop3_session->state = POP3_DONE; | ||
| 849 | else | ||
| 850 | pop3_session->state = POP3_ERROR; | ||
| 851 | session_disconnect(session); | ||
| 852 | break; | ||
| 853 | case POP3_ERROR: | ||
| 854 | default: | ||
| 855 | return -1; | ||
| 856 | } | ||
| 857 | |||
| 858 | if (val == PS_SUCCESS) | ||
| 859 | return 0; | ||
| 860 | else | ||
| 861 | return -1; | ||
| 862 | } | ||
| 863 | |||
| 864 | static gint pop3_session_recv_data_finished(Session *session, guchar *data, | ||
| 865 | guint len) | ||
| 866 | { | ||
| 867 | Pop3Session *pop3_session = POP3_SESSION(session); | ||
| 868 | Pop3ErrorValue val = PS_SUCCESS; | ||
| 869 | |||
| 870 | switch (pop3_session->state) { | ||
| 871 | case POP3_GETRANGE_UIDL_RECV: | ||
| 872 | val = pop3_getrange_uidl_recv(pop3_session, (gchar *)data, len); | ||
| 873 | if (val == PS_SUCCESS) { | ||
| 874 | if (pop3_session->new_msg_exist) | ||
| 875 | pop3_getsize_list_send(pop3_session); | ||
| 876 | else | ||
| 877 | pop3_logout_send(pop3_session); | ||
| 878 | } else | ||
| 879 | return -1; | ||
| 880 | break; | ||
| 881 | case POP3_GETSIZE_LIST_RECV: | ||
| 882 | val = pop3_getsize_list_recv(pop3_session, (gchar *)data, len); | ||
| 883 | if (val == PS_SUCCESS) { | ||
| 884 | if (pop3_lookup_next(pop3_session) == POP3_ERROR) | ||
| 885 | return -1; | ||
| 886 | } else | ||
| 887 | return -1; | ||
| 888 | break; | ||
| 889 | case POP3_ERROR: | ||
| 890 | default: | ||
| 891 | return -1; | ||
| 892 | } | ||
| 893 | |||
| 894 | return 0; | ||
| 895 | } | ||
| 896 | |||
| 897 | static gint pop3_session_recv_data_as_file_finished(Session *session, FILE *fp, | ||
| 898 | guint len) | ||
| 899 | { | ||
| 900 | Pop3Session *pop3_session = POP3_SESSION(session); | ||
| 901 | |||
| 902 | g_return_val_if_fail(pop3_session->state == POP3_RETR_RECV, -1); | ||
| 903 | |||
| 904 | if (pop3_retr_recv(pop3_session, fp, len) != PS_SUCCESS) | ||
| 905 | return -1; | ||
| 906 | |||
| 907 | /* disconnected? */ | ||
| 908 | if (!session->sock) | ||
| 909 | return -1; | ||
| 910 | |||
| 911 | if (pop3_session->msg[pop3_session->cur_msg].recv_time | ||
| 912 | == RECV_TIME_DELETE || | ||
| 913 | (pop3_session->ac_prefs->rmmail && | ||
| 914 | pop3_session->ac_prefs->msg_leave_time == 0 && | ||
| 915 | pop3_session->msg[pop3_session->cur_msg].recv_time | ||
| 916 | != RECV_TIME_KEEP)) | ||
| 917 | pop3_delete_send(pop3_session); | ||
| 918 | else if (pop3_session->cur_msg == pop3_session->count) | ||
| 919 | pop3_logout_send(pop3_session); | ||
| 920 | else { | ||
| 921 | pop3_session->cur_msg++; | ||
| 922 | if (pop3_lookup_next(pop3_session) == POP3_ERROR) | ||
| 923 | return -1; | ||
| 924 | } | ||
| 925 | |||
| 926 | return 0; | ||
| 927 | } | ||
diff --git a/libsylph/pop.h b/libsylph/pop.h new file mode 100644 index 0000000..050caf5 --- /dev/null +++ b/libsylph/pop.h | |||
| @@ -0,0 +1,157 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2006 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __POP_H__ | ||
| 21 | #define __POP_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <time.h> | ||
| 29 | |||
| 30 | #include "session.h" | ||
| 31 | #include "prefs_account.h" | ||
| 32 | #include "utils.h" | ||
| 33 | |||
| 34 | typedef struct _Pop3MsgInfo Pop3MsgInfo; | ||
| 35 | typedef struct _Pop3Session Pop3Session; | ||
| 36 | |||
| 37 | #define POP3_SESSION(obj) ((Pop3Session *)obj) | ||
| 38 | |||
| 39 | typedef enum { | ||
| 40 | POP3_READY, | ||
| 41 | POP3_GREETING, | ||
| 42 | POP3_STLS, | ||
| 43 | POP3_GETAUTH_USER, | ||
| 44 | POP3_GETAUTH_PASS, | ||
| 45 | POP3_GETAUTH_APOP, | ||
| 46 | POP3_GETRANGE_STAT, | ||
| 47 | POP3_GETRANGE_LAST, | ||
| 48 | POP3_GETRANGE_UIDL, | ||
| 49 | POP3_GETRANGE_UIDL_RECV, | ||
| 50 | POP3_GETSIZE_LIST, | ||
| 51 | POP3_GETSIZE_LIST_RECV, | ||
| 52 | POP3_RETR, | ||
| 53 | POP3_RETR_RECV, | ||
| 54 | POP3_DELETE, | ||
| 55 | POP3_LOGOUT, | ||
| 56 | POP3_DONE, | ||
| 57 | POP3_ERROR, | ||
| 58 | |||
| 59 | N_POP3_STATE | ||
| 60 | } Pop3State; | ||
| 61 | |||
| 62 | typedef enum { | ||
| 63 | PS_SUCCESS = 0, /* command successful */ | ||
| 64 | PS_NOMAIL = 1, /* no mail available */ | ||
| 65 | PS_SOCKET = 2, /* socket I/O woes */ | ||
| 66 | PS_AUTHFAIL = 3, /* user authorization failed */ | ||
| 67 | PS_PROTOCOL = 4, /* protocol violation */ | ||
| 68 | PS_SYNTAX = 5, /* command-line syntax error */ | ||
| 69 | PS_IOERR = 6, /* file I/O error */ | ||
| 70 | PS_ERROR = 7, /* protocol error */ | ||
| 71 | PS_EXCLUDE = 8, /* client-side exclusion error */ | ||
| 72 | PS_LOCKBUSY = 9, /* server responded lock busy */ | ||
| 73 | PS_SMTP = 10, /* SMTP error */ | ||
| 74 | PS_DNS = 11, /* fatal DNS error */ | ||
| 75 | PS_BSMTP = 12, /* output batch could not be opened */ | ||
| 76 | PS_MAXFETCH = 13, /* poll ended by fetch limit */ | ||
| 77 | |||
| 78 | PS_NOTSUPPORTED = 14, /* command not supported */ | ||
| 79 | |||
| 80 | /* leave space for more codes */ | ||
| 81 | |||
| 82 | PS_CONTINUE = 128 /* more responses may follow */ | ||
| 83 | } Pop3ErrorValue; | ||
| 84 | |||
| 85 | typedef enum { | ||
| 86 | RECV_TIME_NONE = 0, | ||
| 87 | RECV_TIME_RECEIVED = 1, | ||
| 88 | RECV_TIME_KEEP = 2, | ||
| 89 | RECV_TIME_DELETE = 3 | ||
| 90 | } RecvTime; | ||
| 91 | |||
| 92 | typedef enum { | ||
| 93 | DROP_OK = 0, | ||
| 94 | DROP_DONT_RECEIVE = 1, | ||
| 95 | DROP_DELETE = 2, | ||
| 96 | DROP_ERROR = -1 | ||
| 97 | } Pop3DropValue; | ||
| 98 | |||
| 99 | struct _Pop3MsgInfo | ||
| 100 | { | ||
| 101 | gint size; | ||
| 102 | gchar *uidl; | ||
| 103 | stime_t recv_time; | ||
| 104 | guint received : 1; | ||
| 105 | guint deleted : 1; | ||
| 106 | }; | ||
| 107 | |||
| 108 | struct _Pop3Session | ||
| 109 | { | ||
| 110 | Session session; | ||
| 111 | |||
| 112 | Pop3State state; | ||
| 113 | |||
| 114 | PrefsAccount *ac_prefs; | ||
| 115 | |||
| 116 | gchar *greeting; | ||
| 117 | gchar *user; | ||
| 118 | gchar *pass; | ||
| 119 | gint count; | ||
| 120 | gint64 total_bytes; | ||
| 121 | gint cur_msg; | ||
| 122 | gint cur_total_num; | ||
| 123 | gint64 cur_total_bytes; | ||
| 124 | gint64 cur_total_recv_bytes; | ||
| 125 | gint skipped_num; | ||
| 126 | |||
| 127 | Pop3MsgInfo *msg; | ||
| 128 | |||
| 129 | GHashTable *uidl_table; | ||
| 130 | |||
| 131 | gboolean auth_only; | ||
| 132 | |||
| 133 | gboolean new_msg_exist; | ||
| 134 | gboolean uidl_is_valid; | ||
| 135 | |||
| 136 | stime_t current_time; | ||
| 137 | |||
| 138 | Pop3ErrorValue error_val; | ||
| 139 | gchar *error_msg; | ||
| 140 | |||
| 141 | gpointer data; | ||
| 142 | |||
| 143 | /* virtual method to drop message */ | ||
| 144 | gint (*drop_message) (Pop3Session *session, | ||
| 145 | const gchar *file); | ||
| 146 | }; | ||
| 147 | |||
| 148 | #define POPBUFSIZE 512 | ||
| 149 | /* #define IDLEN 128 */ | ||
| 150 | #define IDLEN POPBUFSIZE | ||
| 151 | |||
| 152 | Session *pop3_session_new (PrefsAccount *account); | ||
| 153 | |||
| 154 | GHashTable *pop3_get_uidl_table (PrefsAccount *account); | ||
| 155 | gint pop3_write_uidl_list (Pop3Session *session); | ||
| 156 | |||
| 157 | #endif /* __POP_H__ */ | ||
diff --git a/libsylph/prefs.c b/libsylph/prefs.c new file mode 100644 index 0000000..12dd057 --- /dev/null +++ b/libsylph/prefs.c | |||
| @@ -0,0 +1,588 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2009 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <glib/gi18n.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <unistd.h> | ||
| 30 | #include <errno.h> | ||
| 31 | |||
| 32 | #ifdef G_OS_WIN32 | ||
| 33 | # include <windows.h> | ||
| 34 | # include <io.h> | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #include "prefs.h" | ||
| 38 | #include "codeconv.h" | ||
| 39 | #include "utils.h" | ||
| 40 | |||
| 41 | typedef enum | ||
| 42 | { | ||
| 43 | DUMMY_PARAM | ||
| 44 | } DummyEnum; | ||
| 45 | |||
| 46 | typedef struct _PrefFilePrivate PrefFilePrivate; | ||
| 47 | |||
| 48 | struct _PrefFilePrivate { | ||
| 49 | FILE *fp; | ||
| 50 | gchar *path; | ||
| 51 | gint backup_generation; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static void prefs_config_parse_one_line (GHashTable *param_table, | ||
| 55 | const gchar *buf); | ||
| 56 | |||
| 57 | GHashTable *prefs_param_table_get(PrefParam *param) | ||
| 58 | { | ||
| 59 | GHashTable *table; | ||
| 60 | gint i; | ||
| 61 | |||
| 62 | g_return_val_if_fail(param != NULL, NULL); | ||
| 63 | |||
| 64 | table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 65 | |||
| 66 | for (i = 0; param[i].name != NULL; i++) { | ||
| 67 | g_hash_table_insert(table, param[i].name, ¶m[i]); | ||
| 68 | } | ||
| 69 | |||
| 70 | return table; | ||
| 71 | } | ||
| 72 | |||
| 73 | void prefs_param_table_destroy(GHashTable *param_table) | ||
| 74 | { | ||
| 75 | g_hash_table_destroy(param_table); | ||
| 76 | } | ||
| 77 | |||
| 78 | void prefs_read_config(PrefParam *param, const gchar *label, | ||
| 79 | const gchar *rcfile, const gchar *encoding) | ||
| 80 | { | ||
| 81 | FILE *fp; | ||
| 82 | gchar buf[PREFSBUFSIZE]; | ||
| 83 | gchar *block_label; | ||
| 84 | GHashTable *param_table; | ||
| 85 | |||
| 86 | g_return_if_fail(param != NULL); | ||
| 87 | g_return_if_fail(label != NULL); | ||
| 88 | g_return_if_fail(rcfile != NULL); | ||
| 89 | |||
| 90 | debug_print("Reading configuration...\n"); | ||
| 91 | |||
| 92 | prefs_set_default(param); | ||
| 93 | |||
| 94 | if ((fp = g_fopen(rcfile, "rb")) == NULL) { | ||
| 95 | if (ENOENT != errno) FILE_OP_ERROR(rcfile, "fopen"); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | |||
| 99 | block_label = g_strdup_printf("[%s]", label); | ||
| 100 | |||
| 101 | /* search aiming block */ | ||
| 102 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 103 | gint val; | ||
| 104 | |||
| 105 | if (encoding) { | ||
| 106 | gchar *conv_str; | ||
| 107 | |||
| 108 | conv_str = conv_codeset_strdup | ||
| 109 | (buf, encoding, CS_INTERNAL); | ||
| 110 | if (!conv_str) | ||
| 111 | conv_str = g_strdup(buf); | ||
| 112 | val = strncmp | ||
| 113 | (conv_str, block_label, strlen(block_label)); | ||
| 114 | g_free(conv_str); | ||
| 115 | } else | ||
| 116 | val = strncmp(buf, block_label, strlen(block_label)); | ||
| 117 | if (val == 0) { | ||
| 118 | debug_print("Found %s\n", block_label); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | g_free(block_label); | ||
| 123 | |||
| 124 | param_table = prefs_param_table_get(param); | ||
| 125 | |||
| 126 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 127 | strretchomp(buf); | ||
| 128 | if (buf[0] == '\0') continue; | ||
| 129 | /* reached next block */ | ||
| 130 | if (buf[0] == '[') break; | ||
| 131 | |||
| 132 | if (encoding) { | ||
| 133 | gchar *conv_str; | ||
| 134 | |||
| 135 | conv_str = conv_codeset_strdup | ||
| 136 | (buf, encoding, CS_INTERNAL); | ||
| 137 | if (!conv_str) | ||
| 138 | conv_str = g_strdup(buf); | ||
| 139 | prefs_config_parse_one_line(param_table, conv_str); | ||
| 140 | g_free(conv_str); | ||
| 141 | } else | ||
| 142 | prefs_config_parse_one_line(param_table, buf); | ||
| 143 | } | ||
| 144 | |||
| 145 | prefs_param_table_destroy(param_table); | ||
| 146 | |||
| 147 | debug_print("Finished reading configuration.\n"); | ||
| 148 | fclose(fp); | ||
| 149 | } | ||
| 150 | |||
| 151 | static void prefs_config_parse_one_line(GHashTable *param_table, | ||
| 152 | const gchar *buf) | ||
| 153 | { | ||
| 154 | PrefParam *param; | ||
| 155 | const gchar *p = buf; | ||
| 156 | gchar *name; | ||
| 157 | const gchar *value; | ||
| 158 | |||
| 159 | while (*p && *p != '=') | ||
| 160 | p++; | ||
| 161 | |||
| 162 | if (*p != '=') { | ||
| 163 | g_warning("invalid pref line: %s\n", buf); | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 167 | name = g_strndup(buf, p - buf); | ||
| 168 | value = p + 1; | ||
| 169 | |||
| 170 | /* debug_print("%s = %s\n", name, value); */ | ||
| 171 | |||
| 172 | param = g_hash_table_lookup(param_table, name); | ||
| 173 | |||
| 174 | if (!param) { | ||
| 175 | debug_print("pref key '%s' (value '%s') not found\n", | ||
| 176 | name, value); | ||
| 177 | g_free(name); | ||
| 178 | return; | ||
| 179 | } | ||
| 180 | |||
| 181 | switch (param->type) { | ||
| 182 | case P_STRING: | ||
| 183 | g_free(*((gchar **)param->data)); | ||
| 184 | *((gchar **)param->data) = *value ? g_strdup(value) : NULL; | ||
| 185 | break; | ||
| 186 | case P_INT: | ||
| 187 | *((gint *)param->data) = (gint)atoi(value); | ||
| 188 | break; | ||
| 189 | case P_BOOL: | ||
| 190 | *((gboolean *)param->data) = | ||
| 191 | (*value == '0' || *value == '\0') ? FALSE : TRUE; | ||
| 192 | break; | ||
| 193 | case P_ENUM: | ||
| 194 | *((DummyEnum *)param->data) = (DummyEnum)atoi(value); | ||
| 195 | break; | ||
| 196 | case P_USHORT: | ||
| 197 | *((gushort *)param->data) = (gushort)atoi(value); | ||
| 198 | break; | ||
| 199 | default: | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | |||
| 203 | g_free(name); | ||
| 204 | } | ||
| 205 | |||
| 206 | #define TRY(func) \ | ||
| 207 | if (!(func)) \ | ||
| 208 | { \ | ||
| 209 | g_warning(_("failed to write configuration to file\n")); \ | ||
| 210 | if (orig_fp) fclose(orig_fp); \ | ||
| 211 | prefs_file_close_revert(pfile); \ | ||
| 212 | g_free(rcpath); \ | ||
| 213 | g_free(block_label); \ | ||
| 214 | return; \ | ||
| 215 | } \ | ||
| 216 | |||
| 217 | void prefs_write_config(PrefParam *param, const gchar *label, | ||
| 218 | const gchar *rcfile) | ||
| 219 | { | ||
| 220 | FILE *orig_fp; | ||
| 221 | PrefFile *pfile; | ||
| 222 | gchar *rcpath; | ||
| 223 | gchar buf[PREFSBUFSIZE]; | ||
| 224 | gchar *block_label = NULL; | ||
| 225 | gboolean block_matched = FALSE; | ||
| 226 | |||
| 227 | g_return_if_fail(param != NULL); | ||
| 228 | g_return_if_fail(label != NULL); | ||
| 229 | g_return_if_fail(rcfile != NULL); | ||
| 230 | |||
| 231 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, rcfile, NULL); | ||
| 232 | if ((orig_fp = g_fopen(rcpath, "rb")) == NULL) { | ||
| 233 | if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen"); | ||
| 234 | } | ||
| 235 | |||
| 236 | if ((pfile = prefs_file_open(rcpath)) == NULL) { | ||
| 237 | g_warning(_("failed to write configuration to file\n")); | ||
| 238 | if (orig_fp) fclose(orig_fp); | ||
| 239 | g_free(rcpath); | ||
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | block_label = g_strdup_printf("[%s]", label); | ||
| 244 | |||
| 245 | /* search aiming block */ | ||
| 246 | if (orig_fp) { | ||
| 247 | while (fgets(buf, sizeof(buf), orig_fp) != NULL) { | ||
| 248 | gint val; | ||
| 249 | |||
| 250 | val = strncmp(buf, block_label, strlen(block_label)); | ||
| 251 | if (val == 0) { | ||
| 252 | debug_print(_("Found %s\n"), block_label); | ||
| 253 | block_matched = TRUE; | ||
| 254 | break; | ||
| 255 | } else | ||
| 256 | TRY(fputs(buf, pfile->fp) != EOF); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | TRY(fprintf(pfile->fp, "%s\n", block_label) > 0); | ||
| 261 | g_free(block_label); | ||
| 262 | block_label = NULL; | ||
| 263 | |||
| 264 | /* write all param data to file */ | ||
| 265 | TRY(prefs_file_write_param(pfile, param) == 0); | ||
| 266 | |||
| 267 | if (block_matched) { | ||
| 268 | while (fgets(buf, sizeof(buf), orig_fp) != NULL) { | ||
| 269 | /* next block */ | ||
| 270 | if (buf[0] == '[') { | ||
| 271 | TRY(fputc('\n', pfile->fp) != EOF && | ||
| 272 | fputs(buf, pfile->fp) != EOF); | ||
| 273 | break; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | while (fgets(buf, sizeof(buf), orig_fp) != NULL) | ||
| 277 | TRY(fputs(buf, pfile->fp) != EOF); | ||
| 278 | } | ||
| 279 | |||
| 280 | if (orig_fp) fclose(orig_fp); | ||
| 281 | if (prefs_file_close(pfile) < 0) | ||
| 282 | g_warning(_("failed to write configuration to file\n")); | ||
| 283 | g_free(rcpath); | ||
| 284 | |||
| 285 | debug_print(_("Configuration is saved.\n")); | ||
| 286 | } | ||
| 287 | |||
| 288 | gint prefs_file_write_param(PrefFile *pfile, PrefParam *param) | ||
| 289 | { | ||
| 290 | gint i; | ||
| 291 | gchar buf[PREFSBUFSIZE]; | ||
| 292 | |||
| 293 | for (i = 0; param[i].name != NULL; i++) { | ||
| 294 | switch (param[i].type) { | ||
| 295 | case P_STRING: | ||
| 296 | g_snprintf(buf, sizeof(buf), "%s=%s\n", param[i].name, | ||
| 297 | *((gchar **)param[i].data) ? | ||
| 298 | *((gchar **)param[i].data) : ""); | ||
| 299 | break; | ||
| 300 | case P_INT: | ||
| 301 | g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, | ||
| 302 | *((gint *)param[i].data)); | ||
| 303 | break; | ||
| 304 | case P_BOOL: | ||
| 305 | g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, | ||
| 306 | *((gboolean *)param[i].data)); | ||
| 307 | break; | ||
| 308 | case P_ENUM: | ||
| 309 | g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, | ||
| 310 | *((DummyEnum *)param[i].data)); | ||
| 311 | break; | ||
| 312 | case P_USHORT: | ||
| 313 | g_snprintf(buf, sizeof(buf), "%s=%d\n", param[i].name, | ||
| 314 | *((gushort *)param[i].data)); | ||
| 315 | break; | ||
| 316 | default: | ||
| 317 | buf[0] = '\0'; | ||
| 318 | } | ||
| 319 | |||
| 320 | if (buf[0] != '\0') { | ||
| 321 | if (fputs(buf, pfile->fp) == EOF) { | ||
| 322 | perror("fputs"); | ||
| 323 | return -1; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | PrefFile *prefs_file_open(const gchar *path) | ||
| 332 | { | ||
| 333 | PrefFilePrivate *pfile; | ||
| 334 | gchar *tmppath; | ||
| 335 | FILE *fp; | ||
| 336 | |||
| 337 | g_return_val_if_fail(path != NULL, NULL); | ||
| 338 | |||
| 339 | tmppath = g_strconcat(path, ".tmp", NULL); | ||
| 340 | if ((fp = g_fopen(tmppath, "wb")) == NULL) { | ||
| 341 | FILE_OP_ERROR(tmppath, "fopen"); | ||
| 342 | g_free(tmppath); | ||
| 343 | return NULL; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (change_file_mode_rw(fp, tmppath) < 0) | ||
| 347 | FILE_OP_ERROR(tmppath, "chmod"); | ||
| 348 | |||
| 349 | g_free(tmppath); | ||
| 350 | |||
| 351 | pfile = g_new(PrefFilePrivate, 1); | ||
| 352 | pfile->fp = fp; | ||
| 353 | pfile->path = g_strdup(path); | ||
| 354 | pfile->backup_generation = 4; | ||
| 355 | |||
| 356 | return (PrefFile *)pfile; | ||
| 357 | } | ||
| 358 | |||
| 359 | void prefs_file_set_backup_generation(PrefFile *pfile, gint generation) | ||
| 360 | { | ||
| 361 | PrefFilePrivate *priv = (PrefFilePrivate *)pfile; | ||
| 362 | |||
| 363 | g_return_if_fail(pfile != NULL); | ||
| 364 | |||
| 365 | priv->backup_generation = generation; | ||
| 366 | } | ||
| 367 | |||
| 368 | gint prefs_file_get_backup_generation(PrefFile *pfile) | ||
| 369 | { | ||
| 370 | PrefFilePrivate *priv = (PrefFilePrivate *)pfile; | ||
| 371 | |||
| 372 | g_return_val_if_fail(pfile != NULL, -1); | ||
| 373 | |||
| 374 | return priv->backup_generation; | ||
| 375 | } | ||
| 376 | |||
| 377 | gint prefs_file_close(PrefFile *pfile) | ||
| 378 | { | ||
| 379 | PrefFilePrivate *priv = (PrefFilePrivate *)pfile; | ||
| 380 | FILE *fp; | ||
| 381 | gchar *path; | ||
| 382 | gchar *tmppath; | ||
| 383 | gchar *bakpath = NULL; | ||
| 384 | gint generation; | ||
| 385 | gint ret = 0; | ||
| 386 | |||
| 387 | g_return_val_if_fail(pfile != NULL, -1); | ||
| 388 | |||
| 389 | fp = pfile->fp; | ||
| 390 | path = pfile->path; | ||
| 391 | generation = priv->backup_generation; | ||
| 392 | g_free(pfile); | ||
| 393 | |||
| 394 | tmppath = g_strconcat(path, ".tmp", NULL); | ||
| 395 | if (fflush(fp) == EOF) { | ||
| 396 | FILE_OP_ERROR(tmppath, "fflush"); | ||
| 397 | fclose(fp); | ||
| 398 | ret = -1; | ||
| 399 | goto finish; | ||
| 400 | } | ||
| 401 | #if HAVE_FSYNC | ||
| 402 | if (fsync(fileno(fp)) < 0) { | ||
| 403 | FILE_OP_ERROR(tmppath, "fsync"); | ||
| 404 | fclose(fp); | ||
| 405 | ret = -1; | ||
| 406 | goto finish; | ||
| 407 | } | ||
| 408 | #elif defined(G_OS_WIN32) | ||
| 409 | if (_commit(_fileno(fp)) < 0) { | ||
| 410 | FILE_OP_ERROR(tmppath, "_commit"); | ||
| 411 | fclose(fp); | ||
| 412 | ret = -1; | ||
| 413 | goto finish; | ||
| 414 | } | ||
| 415 | #endif | ||
| 416 | if (fclose(fp) == EOF) { | ||
| 417 | FILE_OP_ERROR(tmppath, "fclose"); | ||
| 418 | ret = -1; | ||
| 419 | goto finish; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (is_file_exist(path)) { | ||
| 423 | bakpath = g_strconcat(path, ".bak", NULL); | ||
| 424 | if (is_file_exist(bakpath)) { | ||
| 425 | gint i; | ||
| 426 | gchar *bakpath_n, *bakpath_p; | ||
| 427 | |||
| 428 | for (i = generation; i > 0; i--) { | ||
| 429 | bakpath_n = g_strdup_printf("%s.%d", bakpath, | ||
| 430 | i); | ||
| 431 | if (i == 1) | ||
| 432 | bakpath_p = g_strdup(bakpath); | ||
| 433 | else | ||
| 434 | bakpath_p = g_strdup_printf | ||
| 435 | ("%s.%d", bakpath, i - 1); | ||
| 436 | if (is_file_exist(bakpath_p)) { | ||
| 437 | if (rename_force(bakpath_p, bakpath_n) < 0) { | ||
| 438 | FILE_OP_ERROR(bakpath_p, | ||
| 439 | "rename"); | ||
| 440 | } | ||
| 441 | } | ||
| 442 | g_free(bakpath_p); | ||
| 443 | g_free(bakpath_n); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | if (rename_force(path, bakpath) < 0) { | ||
| 447 | FILE_OP_ERROR(path, "rename"); | ||
| 448 | ret = -1; | ||
| 449 | goto finish; | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | if (rename_force(tmppath, path) < 0) { | ||
| 454 | FILE_OP_ERROR(tmppath, "rename"); | ||
| 455 | ret = -1; | ||
| 456 | goto finish; | ||
| 457 | } | ||
| 458 | |||
| 459 | finish: | ||
| 460 | if (ret < 0) | ||
| 461 | g_unlink(tmppath); | ||
| 462 | g_free(path); | ||
| 463 | g_free(tmppath); | ||
| 464 | g_free(bakpath); | ||
| 465 | return ret; | ||
| 466 | } | ||
| 467 | |||
| 468 | gint prefs_file_close_revert(PrefFile *pfile) | ||
| 469 | { | ||
| 470 | gchar *tmppath; | ||
| 471 | |||
| 472 | g_return_val_if_fail(pfile != NULL, -1); | ||
| 473 | |||
| 474 | tmppath = g_strconcat(pfile->path, ".tmp", NULL); | ||
| 475 | fclose(pfile->fp); | ||
| 476 | if (g_unlink(tmppath) < 0) | ||
| 477 | FILE_OP_ERROR(tmppath, "unlink"); | ||
| 478 | g_free(tmppath); | ||
| 479 | g_free(pfile->path); | ||
| 480 | g_free(pfile); | ||
| 481 | |||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | void prefs_set_default(PrefParam *param) | ||
| 486 | { | ||
| 487 | gint i; | ||
| 488 | |||
| 489 | g_return_if_fail(param != NULL); | ||
| 490 | |||
| 491 | for (i = 0; param[i].name != NULL; i++) { | ||
| 492 | if (!param[i].data) continue; | ||
| 493 | |||
| 494 | switch (param[i].type) { | ||
| 495 | case P_STRING: | ||
| 496 | if (param[i].defval != NULL) { | ||
| 497 | if (!g_ascii_strncasecmp(param[i].defval, "ENV_", 4)) { | ||
| 498 | const gchar *envstr; | ||
| 499 | gchar *tmp = NULL; | ||
| 500 | |||
| 501 | envstr = g_getenv(param[i].defval + 4); | ||
| 502 | #ifdef G_OS_WIN32 | ||
| 503 | tmp = g_strdup(envstr); | ||
| 504 | #else | ||
| 505 | if (envstr) { | ||
| 506 | tmp = conv_codeset_strdup | ||
| 507 | (envstr, | ||
| 508 | conv_get_locale_charset_str(), | ||
| 509 | CS_UTF_8); | ||
| 510 | if (!tmp) { | ||
| 511 | g_warning("failed to convert character set."); | ||
| 512 | tmp = g_strdup(envstr); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | #endif | ||
| 516 | *((gchar **)param[i].data) = tmp; | ||
| 517 | } else if (param[i].defval[0] == '~') | ||
| 518 | *((gchar **)param[i].data) = | ||
| 519 | #ifdef G_OS_WIN32 | ||
| 520 | g_strconcat(get_rc_dir(), | ||
| 521 | #else | ||
| 522 | g_strconcat(get_home_dir(), | ||
| 523 | #endif | ||
| 524 | param[i].defval + 1, | ||
| 525 | NULL); | ||
| 526 | else if (param[i].defval[0] != '\0') | ||
| 527 | *((gchar **)param[i].data) = | ||
| 528 | g_strdup(param[i].defval); | ||
| 529 | else | ||
| 530 | *((gchar **)param[i].data) = NULL; | ||
| 531 | } else | ||
| 532 | *((gchar **)param[i].data) = NULL; | ||
| 533 | break; | ||
| 534 | case P_INT: | ||
| 535 | if (param[i].defval != NULL) | ||
| 536 | *((gint *)param[i].data) = | ||
| 537 | (gint)atoi(param[i].defval); | ||
| 538 | else | ||
| 539 | *((gint *)param[i].data) = 0; | ||
| 540 | break; | ||
| 541 | case P_BOOL: | ||
| 542 | if (param[i].defval != NULL) { | ||
| 543 | if (!g_ascii_strcasecmp(param[i].defval, "TRUE")) | ||
| 544 | *((gboolean *)param[i].data) = TRUE; | ||
| 545 | else | ||
| 546 | *((gboolean *)param[i].data) = | ||
| 547 | atoi(param[i].defval) ? TRUE : FALSE; | ||
| 548 | } else | ||
| 549 | *((gboolean *)param[i].data) = FALSE; | ||
| 550 | break; | ||
| 551 | case P_ENUM: | ||
| 552 | if (param[i].defval != NULL) | ||
| 553 | *((DummyEnum*)param[i].data) = | ||
| 554 | (DummyEnum)atoi(param[i].defval); | ||
| 555 | else | ||
| 556 | *((DummyEnum *)param[i].data) = 0; | ||
| 557 | break; | ||
| 558 | case P_USHORT: | ||
| 559 | if (param[i].defval != NULL) | ||
| 560 | *((gushort *)param[i].data) = | ||
| 561 | (gushort)atoi(param[i].defval); | ||
| 562 | else | ||
| 563 | *((gushort *)param[i].data) = 0; | ||
| 564 | break; | ||
| 565 | default: | ||
| 566 | break; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | void prefs_free(PrefParam *param) | ||
| 572 | { | ||
| 573 | gint i; | ||
| 574 | |||
| 575 | g_return_if_fail(param != NULL); | ||
| 576 | |||
| 577 | for (i = 0; param[i].name != NULL; i++) { | ||
| 578 | if (!param[i].data) continue; | ||
| 579 | |||
| 580 | switch (param[i].type) { | ||
| 581 | case P_STRING: | ||
| 582 | g_free(*((gchar **)param[i].data)); | ||
| 583 | break; | ||
| 584 | default: | ||
| 585 | break; | ||
| 586 | } | ||
| 587 | } | ||
| 588 | } | ||
diff --git a/libsylph/prefs.h b/libsylph/prefs.h new file mode 100644 index 0000000..48b1df2 --- /dev/null +++ b/libsylph/prefs.h | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2008 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PREFS_H__ | ||
| 21 | #define __PREFS_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | |||
| 26 | typedef struct _PrefParam PrefParam; | ||
| 27 | typedef struct _PrefFile PrefFile; | ||
| 28 | |||
| 29 | #define PREFSBUFSIZE 8192 | ||
| 30 | |||
| 31 | typedef enum | ||
| 32 | { | ||
| 33 | P_STRING, | ||
| 34 | P_INT, | ||
| 35 | P_BOOL, | ||
| 36 | P_ENUM, | ||
| 37 | P_USHORT, | ||
| 38 | P_OTHER | ||
| 39 | } PrefType; | ||
| 40 | |||
| 41 | typedef void (*DataSetFunc) (PrefParam *pparam); | ||
| 42 | typedef void (*WidgetSetFunc) (PrefParam *pparam); | ||
| 43 | |||
| 44 | struct _PrefParam { | ||
| 45 | gchar *name; | ||
| 46 | gchar *defval; | ||
| 47 | gpointer data; | ||
| 48 | PrefType type; | ||
| 49 | gpointer ui_data; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct _PrefFile { | ||
| 53 | FILE *fp; | ||
| 54 | gchar *path; | ||
| 55 | }; | ||
| 56 | |||
| 57 | GHashTable *prefs_param_table_get (PrefParam *param); | ||
| 58 | void prefs_param_table_destroy (GHashTable *param_table); | ||
| 59 | |||
| 60 | void prefs_read_config (PrefParam *param, | ||
| 61 | const gchar *label, | ||
| 62 | const gchar *rcfile, | ||
| 63 | const gchar *encoding); | ||
| 64 | void prefs_write_config (PrefParam *param, | ||
| 65 | const gchar *label, | ||
| 66 | const gchar *rcfile); | ||
| 67 | |||
| 68 | PrefFile *prefs_file_open (const gchar *path); | ||
| 69 | gint prefs_file_write_param (PrefFile *pfile, | ||
| 70 | PrefParam *param); | ||
| 71 | void prefs_file_set_backup_generation (PrefFile *pfile, | ||
| 72 | gint generation); | ||
| 73 | gint prefs_file_get_backup_generation (PrefFile *pfile); | ||
| 74 | gint prefs_file_close (PrefFile *pfile); | ||
| 75 | gint prefs_file_close_revert (PrefFile *pfile); | ||
| 76 | |||
| 77 | void prefs_set_default (PrefParam *param); | ||
| 78 | void prefs_free (PrefParam *param); | ||
| 79 | |||
| 80 | #endif /* __PREFS_H__ */ | ||
diff --git a/libsylph/prefs_account.c b/libsylph/prefs_account.c new file mode 100644 index 0000000..1aecba9 --- /dev/null +++ b/libsylph/prefs_account.c | |||
| @@ -0,0 +1,296 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | |||
| 32 | #include "prefs.h" | ||
| 33 | #include "prefs_account.h" | ||
| 34 | #include "customheader.h" | ||
| 35 | #include "account.h" | ||
| 36 | #include "utils.h" | ||
| 37 | |||
| 38 | static PrefsAccount tmp_ac_prefs; | ||
| 39 | |||
| 40 | static PrefParam param[] = { | ||
| 41 | /* Basic */ | ||
| 42 | {"account_name", NULL, &tmp_ac_prefs.account_name, P_STRING}, | ||
| 43 | {"is_default", "FALSE", &tmp_ac_prefs.is_default, P_BOOL}, | ||
| 44 | {"name", NULL, &tmp_ac_prefs.name, P_STRING}, | ||
| 45 | {"address", NULL, &tmp_ac_prefs.address, P_STRING}, | ||
| 46 | {"organization", NULL, &tmp_ac_prefs.organization, P_STRING}, | ||
| 47 | {"protocol", NULL, &tmp_ac_prefs.protocol, P_ENUM}, | ||
| 48 | {"receive_server", NULL, &tmp_ac_prefs.recv_server, P_STRING}, | ||
| 49 | {"smtp_server", NULL, &tmp_ac_prefs.smtp_server, P_STRING}, | ||
| 50 | {"nntp_server", NULL, &tmp_ac_prefs.nntp_server, P_STRING}, | ||
| 51 | {"use_nntp_auth", "FALSE", &tmp_ac_prefs.use_nntp_auth, P_BOOL}, | ||
| 52 | {"user_id", "ENV_USER", &tmp_ac_prefs.userid, P_STRING}, | ||
| 53 | {"password", NULL, &tmp_ac_prefs.passwd, P_STRING}, | ||
| 54 | {"inbox", "inbox", &tmp_ac_prefs.inbox, P_STRING}, | ||
| 55 | |||
| 56 | /* Receive */ | ||
| 57 | {"use_apop_auth", "FALSE", &tmp_ac_prefs.use_apop_auth, P_BOOL}, | ||
| 58 | {"remove_mail", "TRUE", &tmp_ac_prefs.rmmail, P_BOOL}, | ||
| 59 | {"message_leave_time", "7", &tmp_ac_prefs.msg_leave_time, P_INT}, | ||
| 60 | {"get_all_mail", "FALSE", &tmp_ac_prefs.getall, P_BOOL}, | ||
| 61 | {"enable_size_limit", "FALSE", &tmp_ac_prefs.enable_size_limit, P_BOOL}, | ||
| 62 | {"size_limit", "1024", &tmp_ac_prefs.size_limit, P_INT}, | ||
| 63 | {"filter_on_receive", "TRUE", &tmp_ac_prefs.filter_on_recv, P_BOOL}, | ||
| 64 | {"imap_check_inbox_only", "FALSE", &tmp_ac_prefs.imap_check_inbox_only, | ||
| 65 | P_BOOL}, | ||
| 66 | {"imap_filter_inbox_on_receive", "FALSE", | ||
| 67 | &tmp_ac_prefs.imap_filter_inbox_on_recv, P_BOOL}, | ||
| 68 | {"imap_auth_method", "0", &tmp_ac_prefs.imap_auth_type, P_ENUM}, | ||
| 69 | {"max_nntp_articles", "300", &tmp_ac_prefs.max_nntp_articles, P_INT}, | ||
| 70 | {"receive_at_get_all", "TRUE", &tmp_ac_prefs.recv_at_getall, P_BOOL}, | ||
| 71 | |||
| 72 | /* Send */ | ||
| 73 | {"add_date", "TRUE", &tmp_ac_prefs.add_date, P_BOOL}, | ||
| 74 | {"generate_msgid", "TRUE", &tmp_ac_prefs.gen_msgid, P_BOOL}, | ||
| 75 | {"add_custom_header", "FALSE", &tmp_ac_prefs.add_customhdr, P_BOOL}, | ||
| 76 | {"use_smtp_auth", "FALSE", &tmp_ac_prefs.use_smtp_auth, P_BOOL}, | ||
| 77 | {"smtp_auth_method", "0", &tmp_ac_prefs.smtp_auth_type, P_ENUM}, | ||
| 78 | {"smtp_user_id", NULL, &tmp_ac_prefs.smtp_userid, P_STRING}, | ||
| 79 | {"smtp_password", NULL, &tmp_ac_prefs.smtp_passwd, P_STRING}, | ||
| 80 | {"pop_before_smtp", "FALSE", &tmp_ac_prefs.pop_before_smtp, P_BOOL}, | ||
| 81 | |||
| 82 | /* Compose */ | ||
| 83 | {"signature_type", "2", &tmp_ac_prefs.sig_type, P_ENUM}, | ||
| 84 | {"signature_path", "~" G_DIR_SEPARATOR_S DEFAULT_SIGNATURE, | ||
| 85 | &tmp_ac_prefs.sig_path, P_STRING}, | ||
| 86 | {"signature_name", NULL, &tmp_ac_prefs.sig_names[0], P_STRING}, | ||
| 87 | {"signature_name2", NULL, &tmp_ac_prefs.sig_names[1], P_STRING}, | ||
| 88 | {"signature_name3", NULL, &tmp_ac_prefs.sig_names[2], P_STRING}, | ||
| 89 | {"signature_name4", NULL, &tmp_ac_prefs.sig_names[3], P_STRING}, | ||
| 90 | {"signature_name5", NULL, &tmp_ac_prefs.sig_names[4], P_STRING}, | ||
| 91 | {"signature_name6", NULL, &tmp_ac_prefs.sig_names[5], P_STRING}, | ||
| 92 | {"signature_name7", NULL, &tmp_ac_prefs.sig_names[6], P_STRING}, | ||
| 93 | {"signature_name8", NULL, &tmp_ac_prefs.sig_names[7], P_STRING}, | ||
| 94 | {"signature_name9", NULL, &tmp_ac_prefs.sig_names[8], P_STRING}, | ||
| 95 | {"signature_name10", NULL, &tmp_ac_prefs.sig_names[9], P_STRING}, | ||
| 96 | {"signature_text", NULL, &tmp_ac_prefs.sig_text, P_STRING}, | ||
| 97 | {"signature_text2", NULL, &tmp_ac_prefs.sig_texts[1], P_STRING}, | ||
| 98 | {"signature_text3", NULL, &tmp_ac_prefs.sig_texts[2], P_STRING}, | ||
| 99 | {"signature_text4", NULL, &tmp_ac_prefs.sig_texts[3], P_STRING}, | ||
| 100 | {"signature_text5", NULL, &tmp_ac_prefs.sig_texts[4], P_STRING}, | ||
| 101 | {"signature_text6", NULL, &tmp_ac_prefs.sig_texts[5], P_STRING}, | ||
| 102 | {"signature_text7", NULL, &tmp_ac_prefs.sig_texts[6], P_STRING}, | ||
| 103 | {"signature_text8", NULL, &tmp_ac_prefs.sig_texts[7], P_STRING}, | ||
| 104 | {"signature_text9", NULL, &tmp_ac_prefs.sig_texts[8], P_STRING}, | ||
| 105 | {"signature_text10", NULL, &tmp_ac_prefs.sig_texts[9], P_STRING}, | ||
| 106 | {"signature_before_quote", "FALSE", &tmp_ac_prefs.sig_before_quote, | ||
| 107 | P_BOOL}, | ||
| 108 | {"set_autocc", "FALSE", &tmp_ac_prefs.set_autocc, P_BOOL}, | ||
| 109 | {"auto_cc", NULL, &tmp_ac_prefs.auto_cc, P_STRING}, | ||
| 110 | {"set_autobcc", "FALSE", &tmp_ac_prefs.set_autobcc, P_BOOL}, | ||
| 111 | {"auto_bcc", NULL, &tmp_ac_prefs.auto_bcc, P_STRING}, | ||
| 112 | {"set_autoreplyto", "FALSE", &tmp_ac_prefs.set_autoreplyto, P_BOOL}, | ||
| 113 | {"auto_replyto", NULL, &tmp_ac_prefs.auto_replyto, P_STRING}, | ||
| 114 | |||
| 115 | /* Privacy */ | ||
| 116 | {"default_sign", "FALSE", &tmp_ac_prefs.default_sign, P_BOOL}, | ||
| 117 | {"default_encrypt", "FALSE", &tmp_ac_prefs.default_encrypt, P_BOOL}, | ||
| 118 | {"encrypt_reply", "TRUE", &tmp_ac_prefs.encrypt_reply, P_BOOL}, | ||
| 119 | {"encrypt_to_self", "TRUE", &tmp_ac_prefs.encrypt_to_self, P_BOOL}, | ||
| 120 | {"ascii_armored", "FALSE", &tmp_ac_prefs.ascii_armored, P_BOOL}, | ||
| 121 | {"clearsign", "FALSE", &tmp_ac_prefs.clearsign, P_BOOL}, | ||
| 122 | {"sign_key", NULL, &tmp_ac_prefs.sign_key, P_ENUM}, | ||
| 123 | {"sign_key_id", NULL, &tmp_ac_prefs.sign_key_id, P_STRING}, | ||
| 124 | |||
| 125 | /* SSL */ | ||
| 126 | {"ssl_pop", "0", &tmp_ac_prefs.ssl_pop, P_ENUM}, | ||
| 127 | {"ssl_imap", "0", &tmp_ac_prefs.ssl_imap, P_ENUM}, | ||
| 128 | {"ssl_nntp", "0", &tmp_ac_prefs.ssl_nntp, P_ENUM}, | ||
| 129 | {"ssl_smtp", "0", &tmp_ac_prefs.ssl_smtp, P_ENUM}, | ||
| 130 | {"use_nonblocking_ssl", "1", &tmp_ac_prefs.use_nonblocking_ssl, P_BOOL}, | ||
| 131 | |||
| 132 | /* SOCKS proxy */ | ||
| 133 | {"use_socks", "FALSE", &tmp_ac_prefs.use_socks, P_BOOL}, | ||
| 134 | {"use_socks_for_recv", "TRUE", &tmp_ac_prefs.use_socks_for_recv, P_BOOL}, | ||
| 135 | {"use_socks_for_send", "TRUE", &tmp_ac_prefs.use_socks_for_send, P_BOOL}, | ||
| 136 | {"socks_type", "1", &tmp_ac_prefs.socks_type, P_ENUM}, | ||
| 137 | {"proxy_host", NULL, &tmp_ac_prefs.proxy_host, P_STRING}, | ||
| 138 | {"proxy_port", "1080", &tmp_ac_prefs.proxy_port, P_USHORT}, | ||
| 139 | {"use_proxy_auth", "FALSE", &tmp_ac_prefs.use_proxy_auth, P_BOOL}, | ||
| 140 | {"proxy_name", NULL, &tmp_ac_prefs.proxy_name, P_STRING}, | ||
| 141 | {"proxy_pass", NULL, &tmp_ac_prefs.proxy_pass, P_STRING}, | ||
| 142 | |||
| 143 | /* Advanced */ | ||
| 144 | {"set_smtpport", "FALSE", &tmp_ac_prefs.set_smtpport, P_BOOL}, | ||
| 145 | {"smtp_port", "25", &tmp_ac_prefs.smtpport, P_USHORT}, | ||
| 146 | {"set_popport", "FALSE", &tmp_ac_prefs.set_popport, P_BOOL}, | ||
| 147 | {"pop_port", "110", &tmp_ac_prefs.popport, P_USHORT}, | ||
| 148 | {"set_imapport", "FALSE", &tmp_ac_prefs.set_imapport, P_BOOL}, | ||
| 149 | {"imap_port", "143", &tmp_ac_prefs.imapport, P_USHORT}, | ||
| 150 | {"set_nntpport", "FALSE", &tmp_ac_prefs.set_nntpport, P_BOOL}, | ||
| 151 | {"nntp_port", "119", &tmp_ac_prefs.nntpport, P_USHORT}, | ||
| 152 | {"set_domain", "FALSE", &tmp_ac_prefs.set_domain, P_BOOL}, | ||
| 153 | {"domain", NULL, &tmp_ac_prefs.domain, P_STRING}, | ||
| 154 | {"imap_directory", NULL, &tmp_ac_prefs.imap_dir, P_STRING}, | ||
| 155 | {"imap_clear_cache_on_exit", "FALSE", | ||
| 156 | &tmp_ac_prefs.imap_clear_cache_on_exit, P_BOOL}, | ||
| 157 | {"set_sent_folder", "FALSE", &tmp_ac_prefs.set_sent_folder, P_BOOL}, | ||
| 158 | {"sent_folder", NULL, &tmp_ac_prefs.sent_folder, P_STRING}, | ||
| 159 | {"set_draft_folder", "FALSE", &tmp_ac_prefs.set_draft_folder, P_BOOL}, | ||
| 160 | {"draft_folder", NULL, &tmp_ac_prefs.draft_folder, P_STRING}, | ||
| 161 | {"set_queue_folder", "FALSE", &tmp_ac_prefs.set_queue_folder, P_BOOL}, | ||
| 162 | {"queue_folder", NULL, &tmp_ac_prefs.queue_folder, P_STRING}, | ||
| 163 | {"set_trash_folder", "FALSE", &tmp_ac_prefs.set_trash_folder, P_BOOL}, | ||
| 164 | {"trash_folder", NULL, &tmp_ac_prefs.trash_folder, P_STRING}, | ||
| 165 | |||
| 166 | {NULL, NULL, NULL, P_OTHER} | ||
| 167 | }; | ||
| 168 | |||
| 169 | static gint prefs_account_get_new_id(void); | ||
| 170 | |||
| 171 | |||
| 172 | PrefsAccount *prefs_account_get_tmp_prefs(void) | ||
| 173 | { | ||
| 174 | return &tmp_ac_prefs; | ||
| 175 | } | ||
| 176 | |||
| 177 | void prefs_account_set_tmp_prefs(PrefsAccount *ac_prefs) | ||
| 178 | { | ||
| 179 | tmp_ac_prefs = *ac_prefs; | ||
| 180 | } | ||
| 181 | |||
| 182 | void prefs_account_apply_tmp_prefs(PrefsAccount *ac_prefs) | ||
| 183 | { | ||
| 184 | *ac_prefs = tmp_ac_prefs; | ||
| 185 | } | ||
| 186 | |||
| 187 | PrefParam *prefs_account_get_params(void) | ||
| 188 | { | ||
| 189 | return param; | ||
| 190 | } | ||
| 191 | |||
| 192 | PrefsAccount *prefs_account_new(void) | ||
| 193 | { | ||
| 194 | PrefsAccount *ac_prefs; | ||
| 195 | |||
| 196 | ac_prefs = g_new0(PrefsAccount, 1); | ||
| 197 | memset(&tmp_ac_prefs, 0, sizeof(PrefsAccount)); | ||
| 198 | prefs_set_default(param); | ||
| 199 | *ac_prefs = tmp_ac_prefs; | ||
| 200 | ac_prefs->account_id = prefs_account_get_new_id(); | ||
| 201 | |||
| 202 | return ac_prefs; | ||
| 203 | } | ||
| 204 | |||
| 205 | void prefs_account_read_config(PrefsAccount *ac_prefs, const gchar *label) | ||
| 206 | { | ||
| 207 | const gchar *p = label; | ||
| 208 | gchar *rcpath; | ||
| 209 | gint id; | ||
| 210 | |||
| 211 | g_return_if_fail(ac_prefs != NULL); | ||
| 212 | g_return_if_fail(label != NULL); | ||
| 213 | |||
| 214 | memset(&tmp_ac_prefs, 0, sizeof(PrefsAccount)); | ||
| 215 | |||
| 216 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL); | ||
| 217 | prefs_read_config(param, label, rcpath, NULL); | ||
| 218 | g_free(rcpath); | ||
| 219 | |||
| 220 | *ac_prefs = tmp_ac_prefs; | ||
| 221 | while (*p && !g_ascii_isdigit(*p)) p++; | ||
| 222 | id = atoi(p); | ||
| 223 | if (id < 0) g_warning("wrong account id: %d\n", id); | ||
| 224 | ac_prefs->account_id = id; | ||
| 225 | |||
| 226 | if (ac_prefs->protocol == A_APOP) { | ||
| 227 | debug_print("converting protocol A_APOP to new prefs.\n"); | ||
| 228 | ac_prefs->protocol = A_POP3; | ||
| 229 | ac_prefs->use_apop_auth = TRUE; | ||
| 230 | } | ||
| 231 | |||
| 232 | custom_header_read_config(ac_prefs); | ||
| 233 | } | ||
| 234 | |||
| 235 | void prefs_account_write_config_all(GList *account_list) | ||
| 236 | { | ||
| 237 | GList *cur; | ||
| 238 | gchar *rcpath; | ||
| 239 | PrefFile *pfile; | ||
| 240 | |||
| 241 | rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL); | ||
| 242 | if ((pfile = prefs_file_open(rcpath)) == NULL) { | ||
| 243 | g_free(rcpath); | ||
| 244 | return; | ||
| 245 | } | ||
| 246 | g_free(rcpath); | ||
| 247 | |||
| 248 | for (cur = account_list; cur != NULL; cur = cur->next) { | ||
| 249 | tmp_ac_prefs = *(PrefsAccount *)cur->data; | ||
| 250 | if (fprintf(pfile->fp, "[Account: %d]\n", | ||
| 251 | tmp_ac_prefs.account_id) <= 0 || | ||
| 252 | prefs_file_write_param(pfile, param) < 0) { | ||
| 253 | g_warning(_("failed to write configuration to file\n")); | ||
| 254 | prefs_file_close_revert(pfile); | ||
| 255 | return; | ||
| 256 | } | ||
| 257 | if (cur->next) { | ||
| 258 | if (fputc('\n', pfile->fp) == EOF) { | ||
| 259 | FILE_OP_ERROR(rcpath, "fputc"); | ||
| 260 | prefs_file_close_revert(pfile); | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | if (prefs_file_close(pfile) < 0) | ||
| 267 | g_warning(_("failed to write configuration to file\n")); | ||
| 268 | } | ||
| 269 | |||
| 270 | void prefs_account_free(PrefsAccount *ac_prefs) | ||
| 271 | { | ||
| 272 | if (!ac_prefs) return; | ||
| 273 | |||
| 274 | tmp_ac_prefs = *ac_prefs; | ||
| 275 | prefs_free(param); | ||
| 276 | |||
| 277 | if (ac_prefs->tmp_pass) | ||
| 278 | g_free(ac_prefs->tmp_pass); | ||
| 279 | g_free(ac_prefs); | ||
| 280 | } | ||
| 281 | |||
| 282 | static gint prefs_account_get_new_id(void) | ||
| 283 | { | ||
| 284 | GList *ac_list; | ||
| 285 | PrefsAccount *ac; | ||
| 286 | static gint last_id = 0; | ||
| 287 | |||
| 288 | for (ac_list = account_get_list(); ac_list != NULL; | ||
| 289 | ac_list = ac_list->next) { | ||
| 290 | ac = (PrefsAccount *)ac_list->data; | ||
| 291 | if (last_id < ac->account_id) | ||
| 292 | last_id = ac->account_id; | ||
| 293 | } | ||
| 294 | |||
| 295 | return last_id + 1; | ||
| 296 | } | ||
diff --git a/libsylph/prefs_account.h b/libsylph/prefs_account.h new file mode 100644 index 0000000..ad899f8 --- /dev/null +++ b/libsylph/prefs_account.h | |||
| @@ -0,0 +1,205 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PREFS_ACCOUNT_H__ | ||
| 21 | #define __PREFS_ACCOUNT_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | |||
| 29 | typedef struct _PrefsAccount PrefsAccount; | ||
| 30 | |||
| 31 | #include "folder.h" | ||
| 32 | #include "smtp.h" | ||
| 33 | #include "prefs.h" | ||
| 34 | |||
| 35 | typedef enum { | ||
| 36 | A_POP3, | ||
| 37 | A_APOP, /* deprecated */ | ||
| 38 | A_RPOP, /* deprecated */ | ||
| 39 | A_IMAP4, | ||
| 40 | A_NNTP, | ||
| 41 | A_LOCAL | ||
| 42 | } RecvProtocol; | ||
| 43 | |||
| 44 | typedef enum { | ||
| 45 | SIG_FILE, | ||
| 46 | SIG_COMMAND, | ||
| 47 | SIG_DIRECT | ||
| 48 | } SigType; | ||
| 49 | |||
| 50 | typedef enum { | ||
| 51 | SIGN_KEY_DEFAULT, | ||
| 52 | SIGN_KEY_BY_FROM, | ||
| 53 | SIGN_KEY_CUSTOM | ||
| 54 | } SignKeyType; | ||
| 55 | |||
| 56 | struct _PrefsAccount | ||
| 57 | { | ||
| 58 | gchar *account_name; | ||
| 59 | |||
| 60 | /* Personal info */ | ||
| 61 | gchar *name; | ||
| 62 | gchar *address; | ||
| 63 | gchar *organization; | ||
| 64 | |||
| 65 | /* Server info */ | ||
| 66 | RecvProtocol protocol; | ||
| 67 | gchar *recv_server; | ||
| 68 | gchar *smtp_server; | ||
| 69 | gchar *nntp_server; | ||
| 70 | gboolean use_nntp_auth; | ||
| 71 | gchar *userid; | ||
| 72 | gchar *passwd; | ||
| 73 | |||
| 74 | /* SSL */ | ||
| 75 | gint ssl_pop; | ||
| 76 | gint ssl_imap; | ||
| 77 | gint ssl_nntp; | ||
| 78 | gint ssl_smtp; | ||
| 79 | gboolean use_nonblocking_ssl; | ||
| 80 | |||
| 81 | /* Temporarily preserved password */ | ||
| 82 | gchar *tmp_pass; | ||
| 83 | |||
| 84 | /* Receive */ | ||
| 85 | gboolean use_apop_auth; | ||
| 86 | gboolean rmmail; | ||
| 87 | gint msg_leave_time; | ||
| 88 | gboolean getall; | ||
| 89 | gboolean enable_size_limit; | ||
| 90 | gint size_limit; | ||
| 91 | gboolean filter_on_recv; | ||
| 92 | gchar *inbox; | ||
| 93 | |||
| 94 | gboolean imap_check_inbox_only; | ||
| 95 | gboolean imap_filter_inbox_on_recv; | ||
| 96 | gint imap_auth_type; | ||
| 97 | |||
| 98 | gint max_nntp_articles; | ||
| 99 | |||
| 100 | gboolean recv_at_getall; | ||
| 101 | |||
| 102 | /* Send */ | ||
| 103 | gboolean add_date; | ||
| 104 | gboolean gen_msgid; | ||
| 105 | gboolean add_customhdr; | ||
| 106 | gboolean use_smtp_auth; | ||
| 107 | SMTPAuthType smtp_auth_type; | ||
| 108 | gchar *smtp_userid; | ||
| 109 | gchar *smtp_passwd; | ||
| 110 | |||
| 111 | /* Temporarily preserved password */ | ||
| 112 | gchar *tmp_smtp_pass; | ||
| 113 | |||
| 114 | gboolean pop_before_smtp; | ||
| 115 | |||
| 116 | GSList *customhdr_list; | ||
| 117 | |||
| 118 | /* Compose */ | ||
| 119 | SigType sig_type; | ||
| 120 | gchar *sig_path; | ||
| 121 | gchar *sig_text; | ||
| 122 | gboolean set_autocc; | ||
| 123 | gchar *auto_cc; | ||
| 124 | gboolean set_autobcc; | ||
| 125 | gchar *auto_bcc; | ||
| 126 | gboolean set_autoreplyto; | ||
| 127 | gchar *auto_replyto; | ||
| 128 | |||
| 129 | /* Privacy */ | ||
| 130 | gboolean default_sign; | ||
| 131 | gboolean default_encrypt; | ||
| 132 | gboolean ascii_armored; | ||
| 133 | gboolean clearsign; | ||
| 134 | gboolean encrypt_reply; | ||
| 135 | |||
| 136 | SignKeyType sign_key; | ||
| 137 | gchar *sign_key_id; | ||
| 138 | |||
| 139 | /* Advanced */ | ||
| 140 | gboolean set_smtpport; | ||
| 141 | gushort smtpport; | ||
| 142 | gboolean set_popport; | ||
| 143 | gushort popport; | ||
| 144 | gboolean set_imapport; | ||
| 145 | gushort imapport; | ||
| 146 | gboolean set_nntpport; | ||
| 147 | gushort nntpport; | ||
| 148 | gboolean set_domain; | ||
| 149 | gchar *domain; | ||
| 150 | |||
| 151 | gchar *imap_dir; | ||
| 152 | gboolean imap_clear_cache_on_exit; | ||
| 153 | |||
| 154 | gboolean set_sent_folder; | ||
| 155 | gchar *sent_folder; | ||
| 156 | gboolean set_draft_folder; | ||
| 157 | gchar *draft_folder; | ||
| 158 | gboolean set_queue_folder; | ||
| 159 | gchar *queue_folder; | ||
| 160 | gboolean set_trash_folder; | ||
| 161 | gchar *trash_folder; | ||
| 162 | |||
| 163 | /* Default or not */ | ||
| 164 | gboolean is_default; | ||
| 165 | /* Unique account ID */ | ||
| 166 | gint account_id; | ||
| 167 | |||
| 168 | RemoteFolder *folder; | ||
| 169 | |||
| 170 | /* Compose */ | ||
| 171 | gboolean sig_before_quote; | ||
| 172 | |||
| 173 | /* SOCKS proxy */ | ||
| 174 | gboolean use_socks; | ||
| 175 | gboolean use_socks_for_recv; | ||
| 176 | gboolean use_socks_for_send; | ||
| 177 | gint socks_type; | ||
| 178 | gchar *proxy_host; | ||
| 179 | gushort proxy_port; | ||
| 180 | gboolean use_proxy_auth; | ||
| 181 | gchar *proxy_name; | ||
| 182 | gchar *proxy_pass; | ||
| 183 | |||
| 184 | /* Privacy */ | ||
| 185 | gboolean encrypt_to_self; | ||
| 186 | |||
| 187 | /* Compose */ | ||
| 188 | gchar *sig_names[10]; | ||
| 189 | gchar *sig_texts[10]; | ||
| 190 | }; | ||
| 191 | |||
| 192 | PrefsAccount *prefs_account_new (void); | ||
| 193 | |||
| 194 | PrefsAccount *prefs_account_get_tmp_prefs (void); | ||
| 195 | void prefs_account_set_tmp_prefs (PrefsAccount *ac_prefs); | ||
| 196 | void prefs_account_apply_tmp_prefs (PrefsAccount *ac_prefs); | ||
| 197 | PrefParam *prefs_account_get_params (void); | ||
| 198 | |||
| 199 | void prefs_account_read_config (PrefsAccount *ac_prefs, | ||
| 200 | const gchar *label); | ||
| 201 | void prefs_account_write_config_all (GList *account_list); | ||
| 202 | |||
| 203 | void prefs_account_free (PrefsAccount *ac_prefs); | ||
| 204 | |||
| 205 | #endif /* __PREFS_ACCOUNT_H__ */ | ||
diff --git a/libsylph/prefs_common.c b/libsylph/prefs_common.c new file mode 100644 index 0000000..8ed74a3 --- /dev/null +++ b/libsylph/prefs_common.c | |||
| @@ -0,0 +1,634 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <errno.h> | ||
| 30 | |||
| 31 | #include "prefs.h" | ||
| 32 | #include "prefs_common.h" | ||
| 33 | #include "filter.h" | ||
| 34 | #include "codeconv.h" | ||
| 35 | #include "utils.h" | ||
| 36 | |||
| 37 | PrefsCommon prefs_common; | ||
| 38 | |||
| 39 | static PrefParam param[] = { | ||
| 40 | /* Receive */ | ||
| 41 | {"autochk_newmail", "FALSE", &prefs_common.autochk_newmail, P_BOOL}, | ||
| 42 | {"autochk_interval", "10", &prefs_common.autochk_itv, P_INT}, | ||
| 43 | {"check_on_startup", "FALSE", &prefs_common.chk_on_startup, P_BOOL}, | ||
| 44 | {"scan_all_after_inc", "FALSE", &prefs_common.scan_all_after_inc, | ||
| 45 | P_BOOL}, | ||
| 46 | {"enable_newmsg_notify", "FALSE", &prefs_common.enable_newmsg_notify, | ||
| 47 | P_BOOL}, | ||
| 48 | {"newmsg_notify_command", NULL, &prefs_common.newmsg_notify_cmd, | ||
| 49 | P_STRING}, | ||
| 50 | {"enable_newmsg_notify_sound", "FALSE", | ||
| 51 | &prefs_common.enable_newmsg_notify_sound, P_BOOL}, | ||
| 52 | {"newmsg_notify_sound", NULL, &prefs_common.newmsg_notify_sound, | ||
| 53 | P_STRING}, | ||
| 54 | {"enable_newmsg_notify_window", "TRUE", | ||
| 55 | &prefs_common.enable_newmsg_notify_window, P_BOOL}, | ||
| 56 | {"notify_window_period", "10", | ||
| 57 | &prefs_common.notify_window_period, P_INT}, | ||
| 58 | |||
| 59 | {"inc_local", "FALSE", &prefs_common.inc_local, P_BOOL}, | ||
| 60 | {"filter_on_inc_local", "TRUE", &prefs_common.filter_on_inc, P_BOOL}, | ||
| 61 | {"spool_path", DEFAULT_SPOOL_PATH, &prefs_common.spool_path, P_STRING}, | ||
| 62 | |||
| 63 | /* Send */ | ||
| 64 | {"save_message", "TRUE", &prefs_common.savemsg, P_BOOL}, | ||
| 65 | {"filter_sent_message", "FALSE", &prefs_common.filter_sent, P_BOOL}, | ||
| 66 | {"recipients_autoreg", "TRUE", &prefs_common.recipients_autoreg, | ||
| 67 | P_BOOL}, | ||
| 68 | {"show_send_dialog", "TRUE", &prefs_common.show_send_dialog, P_BOOL}, | ||
| 69 | |||
| 70 | {"encoding_method", "0", &prefs_common.encoding_method, P_ENUM}, | ||
| 71 | {"mime_filename_encoding_method", "0", | ||
| 72 | &prefs_common.mime_fencoding_method, P_ENUM}, | ||
| 73 | {"check_attach", "FALSE", &prefs_common.check_attach, P_BOOL}, | ||
| 74 | {"check_attach_str", NULL, &prefs_common.check_attach_str, P_STRING}, | ||
| 75 | {"check_recipients", "FALSE", &prefs_common.check_recipients, P_BOOL}, | ||
| 76 | {"check_recp_exclude", NULL, &prefs_common.check_recp_exclude, | ||
| 77 | P_STRING}, | ||
| 78 | |||
| 79 | {"allow_jisx0201_kana", "FALSE", &prefs_common.allow_jisx0201_kana, | ||
| 80 | P_BOOL}, | ||
| 81 | |||
| 82 | /* Compose */ | ||
| 83 | {"auto_signature", "TRUE", &prefs_common.auto_sig, P_BOOL}, | ||
| 84 | {"signature_separator", "-- ", &prefs_common.sig_sep, P_STRING}, | ||
| 85 | |||
| 86 | {"auto_ext_editor", "FALSE", &prefs_common.auto_exteditor, P_BOOL}, | ||
| 87 | |||
| 88 | {"undo_level", "50", &prefs_common.undolevels, P_INT}, | ||
| 89 | |||
| 90 | {"linewrap_length", "72", &prefs_common.linewrap_len, P_INT}, | ||
| 91 | {"linewrap_quotation", "FALSE", &prefs_common.linewrap_quote, P_BOOL}, | ||
| 92 | {"linewrap_auto", "FALSE", &prefs_common.autowrap, P_BOOL}, | ||
| 93 | |||
| 94 | {"enable_autosave", "FALSE", &prefs_common.enable_autosave, P_BOOL}, | ||
| 95 | {"autosave_interval", "5", &prefs_common.autosave_itv, P_INT}, | ||
| 96 | |||
| 97 | {"reply_with_quote", "TRUE", &prefs_common.reply_with_quote, P_BOOL}, | ||
| 98 | {"reply_account_autoselect", "TRUE", | ||
| 99 | &prefs_common.reply_account_autosel, P_BOOL}, | ||
| 100 | {"default_reply_list", "TRUE", &prefs_common.default_reply_list, | ||
| 101 | P_BOOL}, | ||
| 102 | {"inherit_recipient_on_self_reply", "FALSE", | ||
| 103 | &prefs_common.inherit_recipient_on_self_reply, P_BOOL}, | ||
| 104 | {"reply_address_only", "FALSE", | ||
| 105 | &prefs_common.reply_address_only, P_BOOL}, | ||
| 106 | |||
| 107 | {"show_ruler", "TRUE", &prefs_common.show_ruler, P_BOOL}, | ||
| 108 | |||
| 109 | /* Quote */ | ||
| 110 | {"reply_quote_mark", "> ", &prefs_common.quotemark, P_STRING}, | ||
| 111 | {"reply_quote_format", "On %d\\n%f wrote:\\n\\n%Q", | ||
| 112 | &prefs_common.quotefmt, P_STRING}, | ||
| 113 | |||
| 114 | {"forward_quote_mark", "> ", &prefs_common.fw_quotemark, P_STRING}, | ||
| 115 | {"forward_quote_format", | ||
| 116 | "\\n\\nBegin forwarded message:\\n\\n" | ||
| 117 | "?d{Date: %d\\n}?f{From: %f\\n}?t{To: %t\\n}?c{Cc: %c\\n}" | ||
| 118 | "?n{Newsgroups: %n\\n}?s{Subject: %s\\n}\\n\\n%M", | ||
| 119 | &prefs_common.fw_quotefmt, P_STRING}, | ||
| 120 | |||
| 121 | /* Spelling */ | ||
| 122 | {"check_spell", "FALSE", &prefs_common.check_spell, P_BOOL}, | ||
| 123 | {"spell_lang", "en", &prefs_common.spell_lang, P_STRING}, | ||
| 124 | |||
| 125 | /* Display */ | ||
| 126 | {"message_font_name", DEFAULT_MESSAGE_FONT, &prefs_common.textfont, | ||
| 127 | P_STRING}, | ||
| 128 | |||
| 129 | {"display_folder_unread_num", "TRUE", | ||
| 130 | &prefs_common.display_folder_unread, P_BOOL}, | ||
| 131 | {"display_folder_num_columns", "FALSE", | ||
| 132 | &prefs_common.display_folder_num_columns, P_BOOL}, | ||
| 133 | {"folder_col_show_new", "FALSE", | ||
| 134 | &prefs_common.folder_col_visible[1], P_BOOL}, | ||
| 135 | {"folder_col_show_unread", "FALSE", | ||
| 136 | &prefs_common.folder_col_visible[2], P_BOOL}, | ||
| 137 | {"folder_col_show_total", "FALSE", | ||
| 138 | &prefs_common.folder_col_visible[3], P_BOOL}, | ||
| 139 | |||
| 140 | {"newsgroup_abbrev_len", "16", &prefs_common.ng_abbrev_len, P_INT}, | ||
| 141 | |||
| 142 | {"translate_header", "TRUE", &prefs_common.trans_hdr, P_BOOL}, | ||
| 143 | |||
| 144 | /* Display: Summary View */ | ||
| 145 | {"enable_swap_from", "FALSE", &prefs_common.swap_from, P_BOOL}, | ||
| 146 | {"date_format", "%y/%m/%d(%a) %H:%M", &prefs_common.date_format, | ||
| 147 | P_STRING}, | ||
| 148 | {"expand_thread", "TRUE", &prefs_common.expand_thread, P_BOOL}, | ||
| 149 | |||
| 150 | {"enable_rules_hint", "TRUE", &prefs_common.enable_rules_hint, P_BOOL}, | ||
| 151 | {"bold_unread", "TRUE", &prefs_common.bold_unread, P_BOOL}, | ||
| 152 | |||
| 153 | {"persist_qsearch_filter", "TRUE", &prefs_common.persist_qsearch_filter, | ||
| 154 | P_BOOL}, | ||
| 155 | |||
| 156 | {"toolbar_style", "4", &prefs_common.toolbar_style, P_ENUM}, | ||
| 157 | {"show_searchbar", "TRUE", &prefs_common.show_searchbar, P_BOOL}, | ||
| 158 | {"show_statusbar", "TRUE", &prefs_common.show_statusbar, P_BOOL}, | ||
| 159 | |||
| 160 | {"main_toolbar_setting", NULL, &prefs_common.main_toolbar_setting, | ||
| 161 | P_STRING}, | ||
| 162 | {"compose_toolbar_setting", NULL, | ||
| 163 | &prefs_common.compose_toolbar_setting, P_STRING}, | ||
| 164 | |||
| 165 | {"layout_type", "0", &prefs_common.layout_type, P_ENUM}, | ||
| 166 | |||
| 167 | {"summary_col_show_mark", "TRUE", | ||
| 168 | &prefs_common.summary_col_visible[S_COL_MARK], P_BOOL}, | ||
| 169 | {"summary_col_show_unread", "TRUE", | ||
| 170 | &prefs_common.summary_col_visible[S_COL_UNREAD], P_BOOL}, | ||
| 171 | {"summary_col_show_mime", "TRUE", | ||
| 172 | &prefs_common.summary_col_visible[S_COL_MIME], P_BOOL}, | ||
| 173 | {"summary_col_show_subject", "TRUE", | ||
| 174 | &prefs_common.summary_col_visible[S_COL_SUBJECT], P_BOOL}, | ||
| 175 | {"summary_col_show_from", "TRUE", | ||
| 176 | &prefs_common.summary_col_visible[S_COL_FROM], P_BOOL}, | ||
| 177 | {"summary_col_show_date", "TRUE", | ||
| 178 | &prefs_common.summary_col_visible[S_COL_DATE], P_BOOL}, | ||
| 179 | {"summary_col_show_size", "TRUE", | ||
| 180 | &prefs_common.summary_col_visible[S_COL_SIZE], P_BOOL}, | ||
| 181 | {"summary_col_show_number", "FALSE", | ||
| 182 | &prefs_common.summary_col_visible[S_COL_NUMBER], P_BOOL}, | ||
| 183 | {"summary_col_show_to", "FALSE", | ||
| 184 | &prefs_common.summary_col_visible[S_COL_TO], P_BOOL}, | ||
| 185 | |||
| 186 | {"summary_col_pos_mark", "0", | ||
| 187 | &prefs_common.summary_col_pos[S_COL_MARK], P_INT}, | ||
| 188 | {"summary_col_pos_unread", "1", | ||
| 189 | &prefs_common.summary_col_pos[S_COL_UNREAD], P_INT}, | ||
| 190 | {"summary_col_pos_mime", "2", | ||
| 191 | &prefs_common.summary_col_pos[S_COL_MIME], P_INT}, | ||
| 192 | {"summary_col_pos_subject", "3", | ||
| 193 | &prefs_common.summary_col_pos[S_COL_SUBJECT], P_INT}, | ||
| 194 | {"summary_col_pos_from", "4", | ||
| 195 | &prefs_common.summary_col_pos[S_COL_FROM], P_INT}, | ||
| 196 | {"summary_col_pos_date", "5", | ||
| 197 | &prefs_common.summary_col_pos[S_COL_DATE], P_INT}, | ||
| 198 | {"summary_col_pos_size", "6", | ||
| 199 | &prefs_common.summary_col_pos[S_COL_SIZE], P_INT}, | ||
| 200 | {"summary_col_pos_number", "7", | ||
| 201 | &prefs_common.summary_col_pos[S_COL_NUMBER], P_INT}, | ||
| 202 | {"summary_col_pos_to", "8", | ||
| 203 | &prefs_common.summary_col_pos[S_COL_TO], P_INT}, | ||
| 204 | |||
| 205 | {"summary_sent_col_show_mark", "TRUE", | ||
| 206 | &prefs_common.summary_sent_col_visible[S_COL_MARK], P_BOOL}, | ||
| 207 | {"summary_sent_col_show_unread", "TRUE", | ||
| 208 | &prefs_common.summary_sent_col_visible[S_COL_UNREAD], P_BOOL}, | ||
| 209 | {"summary_sent_col_show_mime", "TRUE", | ||
| 210 | &prefs_common.summary_sent_col_visible[S_COL_MIME], P_BOOL}, | ||
| 211 | {"summary_sent_col_show_subject", "TRUE", | ||
| 212 | &prefs_common.summary_sent_col_visible[S_COL_SUBJECT], P_BOOL}, | ||
| 213 | {"summary_sent_col_show_from", "FALSE", | ||
| 214 | &prefs_common.summary_sent_col_visible[S_COL_FROM], P_BOOL}, | ||
| 215 | {"summary_sent_col_show_date", "TRUE", | ||
| 216 | &prefs_common.summary_sent_col_visible[S_COL_DATE], P_BOOL}, | ||
| 217 | {"summary_sent_col_show_size", "TRUE", | ||
| 218 | &prefs_common.summary_sent_col_visible[S_COL_SIZE], P_BOOL}, | ||
| 219 | {"summary_sent_col_show_number", "FALSE", | ||
| 220 | &prefs_common.summary_sent_col_visible[S_COL_NUMBER], P_BOOL}, | ||
| 221 | {"summary_sent_col_show_to", "TRUE", | ||
| 222 | &prefs_common.summary_sent_col_visible[S_COL_TO], P_BOOL}, | ||
| 223 | |||
| 224 | {"summary_sent_col_pos_mark", "0", | ||
| 225 | &prefs_common.summary_sent_col_pos[S_COL_MARK], P_INT}, | ||
| 226 | {"summary_sent_col_pos_unread", "1", | ||
| 227 | &prefs_common.summary_sent_col_pos[S_COL_UNREAD], P_INT}, | ||
| 228 | {"summary_sent_col_pos_mime", "2", | ||
| 229 | &prefs_common.summary_sent_col_pos[S_COL_MIME], P_INT}, | ||
| 230 | {"summary_sent_col_pos_subject", "3", | ||
| 231 | &prefs_common.summary_sent_col_pos[S_COL_SUBJECT], P_INT}, | ||
| 232 | {"summary_sent_col_pos_from", "8", | ||
| 233 | &prefs_common.summary_sent_col_pos[S_COL_FROM], P_INT}, | ||
| 234 | {"summary_sent_col_pos_date", "5", | ||
| 235 | &prefs_common.summary_sent_col_pos[S_COL_DATE], P_INT}, | ||
| 236 | {"summary_sent_col_pos_size", "6", | ||
| 237 | &prefs_common.summary_sent_col_pos[S_COL_SIZE], P_INT}, | ||
| 238 | {"summary_sent_col_pos_number", "7", | ||
| 239 | &prefs_common.summary_sent_col_pos[S_COL_NUMBER], P_INT}, | ||
| 240 | {"summary_sent_col_pos_to", "4", | ||
| 241 | &prefs_common.summary_sent_col_pos[S_COL_TO], P_INT}, | ||
| 242 | |||
| 243 | {"summary_col_size_mark", "10", | ||
| 244 | &prefs_common.summary_col_size[S_COL_MARK], P_INT}, | ||
| 245 | {"summary_col_size_unread", "13", | ||
| 246 | &prefs_common.summary_col_size[S_COL_UNREAD], P_INT}, | ||
| 247 | {"summary_col_size_mime", "10", | ||
| 248 | &prefs_common.summary_col_size[S_COL_MIME], P_INT}, | ||
| 249 | {"summary_col_size_subject", "200", | ||
| 250 | &prefs_common.summary_col_size[S_COL_SUBJECT], P_INT}, | ||
| 251 | {"summary_col_size_from", "120", | ||
| 252 | &prefs_common.summary_col_size[S_COL_FROM], P_INT}, | ||
| 253 | {"summary_col_size_date", "118", | ||
| 254 | &prefs_common.summary_col_size[S_COL_DATE], P_INT}, | ||
| 255 | {"summary_col_size_size", "45", | ||
| 256 | &prefs_common.summary_col_size[S_COL_SIZE], P_INT}, | ||
| 257 | {"summary_col_size_number", "40", | ||
| 258 | &prefs_common.summary_col_size[S_COL_NUMBER], P_INT}, | ||
| 259 | {"summary_col_size_to", "120", | ||
| 260 | &prefs_common.summary_col_size[S_COL_TO], P_INT}, | ||
| 261 | |||
| 262 | /* Widget size */ | ||
| 263 | {"folderwin_x", "16", &prefs_common.folderwin_x, P_INT}, | ||
| 264 | {"folderwin_y", "16", &prefs_common.folderwin_y, P_INT}, | ||
| 265 | {"folderview_width", "179", &prefs_common.folderview_width, P_INT}, | ||
| 266 | {"folderview_height", "450", &prefs_common.folderview_height, P_INT}, | ||
| 267 | {"folderview_visible", "TRUE", &prefs_common.folderview_visible, | ||
| 268 | P_BOOL}, | ||
| 269 | |||
| 270 | {"folder_col_folder", "150", &prefs_common.folder_col_folder, P_INT}, | ||
| 271 | {"folder_col_new", "32", &prefs_common.folder_col_new, P_INT}, | ||
| 272 | {"folder_col_unread", "32", &prefs_common.folder_col_unread, P_INT}, | ||
| 273 | {"folder_col_total", "32", &prefs_common.folder_col_total, P_INT}, | ||
| 274 | |||
| 275 | {"summaryview_width", "600", &prefs_common.summaryview_width, P_INT}, | ||
| 276 | {"summaryview_height", "180", &prefs_common.summaryview_height, P_INT}, | ||
| 277 | {"summaryview_vwidth", "300", &prefs_common.summaryview_vwidth, P_INT}, | ||
| 278 | {"summaryview_vheight", "600", &prefs_common.summaryview_vheight, | ||
| 279 | P_INT}, | ||
| 280 | |||
| 281 | {"main_messagewin_x", "256", &prefs_common.main_msgwin_x, P_INT}, | ||
| 282 | {"main_messagewin_y", "210", &prefs_common.main_msgwin_y, P_INT}, | ||
| 283 | {"messageview_width", "600", &prefs_common.msgview_width, P_INT}, | ||
| 284 | {"messageview_height", "300", &prefs_common.msgview_height, P_INT}, | ||
| 285 | {"messageview_vwidth", "300", &prefs_common.msgview_vwidth, P_INT}, | ||
| 286 | {"messageview_vheight", "600", &prefs_common.msgview_vheight, P_INT}, | ||
| 287 | {"messageview_visible", "TRUE", &prefs_common.msgview_visible, P_BOOL}, | ||
| 288 | |||
| 289 | {"mainview_x", "64", &prefs_common.mainview_x, P_INT}, | ||
| 290 | {"mainview_y", "64", &prefs_common.mainview_y, P_INT}, | ||
| 291 | {"mainview_width", "600", &prefs_common.mainview_width, P_INT}, | ||
| 292 | {"mainview_height", "600", &prefs_common.mainview_height, P_INT}, | ||
| 293 | {"mainwin_x", "64", &prefs_common.mainwin_x, P_INT}, | ||
| 294 | {"mainwin_y", "64", &prefs_common.mainwin_y, P_INT}, | ||
| 295 | {"mainwin_width", "800", &prefs_common.mainwin_width, P_INT}, | ||
| 296 | {"mainwin_height", "600", &prefs_common.mainwin_height, P_INT}, | ||
| 297 | {"messagewin_width", "600", &prefs_common.msgwin_width, P_INT}, | ||
| 298 | {"messagewin_height", "540", &prefs_common.msgwin_height, P_INT}, | ||
| 299 | |||
| 300 | {"mainwin_maximized", "FALSE", &prefs_common.mainwin_maximized, P_BOOL}, | ||
| 301 | |||
| 302 | {"sourcewin_width", "600", &prefs_common.sourcewin_width, P_INT}, | ||
| 303 | {"sourcewin_height", "500", &prefs_common.sourcewin_height, P_INT}, | ||
| 304 | |||
| 305 | {"compose_x", "32", &prefs_common.compose_x, P_INT}, | ||
| 306 | {"compose_y", "32", &prefs_common.compose_y, P_INT}, | ||
| 307 | {"compose_width", "600", &prefs_common.compose_width, P_INT}, | ||
| 308 | {"compose_height", "560", &prefs_common.compose_height, P_INT}, | ||
| 309 | |||
| 310 | {"compose_maximized", "FALSE", &prefs_common.compose_maximized, P_BOOL}, | ||
| 311 | |||
| 312 | {"addressbook_x", "32", &prefs_common.addressbook_x, P_INT}, | ||
| 313 | {"addressbook_y", "32", &prefs_common.addressbook_y, P_INT}, | ||
| 314 | {"addressbook_width", "620", &prefs_common.addressbook_width, P_INT}, | ||
| 315 | {"addressbook_height", "360", &prefs_common.addressbook_height, P_INT}, | ||
| 316 | {"addressbook_folder_width", "190", &prefs_common.addressbook_folder_width, P_INT}, | ||
| 317 | {"addressbook_col_name", "164", &prefs_common.addressbook_col_name, P_INT}, | ||
| 318 | {"addressbook_col_addr", "156", &prefs_common.addressbook_col_addr, P_INT}, | ||
| 319 | {"addressbook_col_nickname", "120", &prefs_common.addressbook_col_nickname, P_INT}, | ||
| 320 | {"addressbook_col_rem", "100", &prefs_common.addressbook_col_rem, P_INT}, | ||
| 321 | |||
| 322 | /* Message */ | ||
| 323 | {"enable_color", "TRUE", &prefs_common.enable_color, P_BOOL}, | ||
| 324 | |||
| 325 | {"quote_level1_color", "179", &prefs_common.quote_level1_col, P_INT}, | ||
| 326 | {"quote_level2_color", "179", &prefs_common.quote_level2_col, P_INT}, | ||
| 327 | {"quote_level3_color", "179", &prefs_common.quote_level3_col, P_INT}, | ||
| 328 | {"uri_color", "32512", &prefs_common.uri_col, P_INT}, | ||
| 329 | {"signature_color", "0", &prefs_common.sig_col, P_USHORT}, | ||
| 330 | {"recycle_quote_colors", "FALSE", &prefs_common.recycle_quote_colors, | ||
| 331 | P_BOOL}, | ||
| 332 | |||
| 333 | {"convert_mb_alnum", "FALSE", &prefs_common.conv_mb_alnum, P_BOOL}, | ||
| 334 | {"display_header_pane", "TRUE", &prefs_common.display_header_pane, | ||
| 335 | P_BOOL}, | ||
| 336 | {"show_attach_tab", "FALSE", &prefs_common.show_attach_tab, P_BOOL}, | ||
| 337 | {"show_attached_files_first", "TRUE", | ||
| 338 | &prefs_common.show_attached_files_first, P_BOOL}, | ||
| 339 | {"attach_toolbutton_pos", "0", &prefs_common.attach_toolbutton_pos, | ||
| 340 | P_INT}, | ||
| 341 | {"display_header", "TRUE", &prefs_common.display_header, P_BOOL}, | ||
| 342 | {"render_html", "TRUE", &prefs_common.render_html, P_BOOL}, | ||
| 343 | {"alt_prefer_html", "FALSE", &prefs_common.alt_prefer_html, P_BOOL}, | ||
| 344 | {"html_only_as_attach", "FALSE", &prefs_common.html_only_as_attach, | ||
| 345 | P_BOOL}, | ||
| 346 | {"line_space", "2", &prefs_common.line_space, P_INT}, | ||
| 347 | |||
| 348 | {"textview_cursor_visible", "FALSE", | ||
| 349 | &prefs_common.textview_cursor_visible, P_BOOL}, | ||
| 350 | |||
| 351 | {"enable_smooth_scroll", "FALSE", &prefs_common.enable_smooth_scroll, | ||
| 352 | P_BOOL}, | ||
| 353 | {"scroll_step", "1", &prefs_common.scroll_step, P_INT}, | ||
| 354 | {"scroll_half_page", "FALSE", &prefs_common.scroll_halfpage, P_BOOL}, | ||
| 355 | |||
| 356 | {"resize_image", "TRUE", &prefs_common.resize_image, P_BOOL}, | ||
| 357 | {"inline_image", "TRUE", &prefs_common.inline_image, P_BOOL}, | ||
| 358 | |||
| 359 | /* Encoding */ | ||
| 360 | {"default_encoding", NULL, &prefs_common.default_encoding, P_STRING}, | ||
| 361 | {"outgoing_charset", NULL, &prefs_common.outgoing_charset, P_STRING}, | ||
| 362 | |||
| 363 | {"show_other_header", "FALSE", &prefs_common.show_other_header, P_BOOL}, | ||
| 364 | |||
| 365 | /* MIME viewer */ | ||
| 366 | {"mime_image_viewer", NULL, &prefs_common.mime_image_viewer, P_STRING}, | ||
| 367 | {"mime_audio_player", NULL, &prefs_common.mime_audio_player, P_STRING}, | ||
| 368 | #ifdef G_OS_WIN32 | ||
| 369 | {"mime_open_command", "notepad '%s'", &prefs_common.mime_open_cmd, | ||
| 370 | #else | ||
| 371 | {"mime_open_command", "gedit '%s'", &prefs_common.mime_open_cmd, | ||
| 372 | #endif | ||
| 373 | P_STRING}, | ||
| 374 | {"mime_command", NULL, &prefs_common.mime_cmd, P_STRING}, | ||
| 375 | |||
| 376 | /* Junk mail */ | ||
| 377 | {"enable_junk", "FALSE", &prefs_common.enable_junk, P_BOOL}, | ||
| 378 | #ifdef G_OS_WIN32 | ||
| 379 | {"junk_learn_command", "sylfilter -j", | ||
| 380 | &prefs_common.junk_learncmd, P_STRING}, | ||
| 381 | {"nojunk_learn_command", "sylfilter -c", | ||
| 382 | &prefs_common.nojunk_learncmd, P_STRING}, | ||
| 383 | {"junk_classify_command", "sylfilter", | ||
| 384 | &prefs_common.junk_classify_cmd, P_STRING}, | ||
| 385 | #else | ||
| 386 | {"junk_learn_command", "bogofilter -N -s -I", | ||
| 387 | &prefs_common.junk_learncmd, P_STRING}, | ||
| 388 | {"nojunk_learn_command", "bogofilter -n -S -I", | ||
| 389 | &prefs_common.nojunk_learncmd, P_STRING}, | ||
| 390 | {"junk_classify_command", "bogofilter -I", | ||
| 391 | &prefs_common.junk_classify_cmd, P_STRING}, | ||
| 392 | #endif | ||
| 393 | {"junk_folder", NULL, &prefs_common.junk_folder, P_STRING}, | ||
| 394 | {"filter_junk_on_receive", "FALSE", &prefs_common.filter_junk_on_recv, | ||
| 395 | P_BOOL}, | ||
| 396 | {"filter_junk_before", "FALSE", &prefs_common.filter_junk_before, | ||
| 397 | P_BOOL}, | ||
| 398 | {"delete_junk_on_receive", "TRUE", &prefs_common.delete_junk_on_recv, | ||
| 399 | P_BOOL}, | ||
| 400 | {"nofilter_junk_sender_in_book", "TRUE", | ||
| 401 | &prefs_common.nofilter_junk_sender_in_book, P_BOOL}, | ||
| 402 | {"mark_junk_as_read", "FALSE", &prefs_common.mark_junk_as_read, P_BOOL}, | ||
| 403 | |||
| 404 | /* Privacy */ | ||
| 405 | {"auto_check_signatures", "TRUE", &prefs_common.auto_check_signatures, | ||
| 406 | P_BOOL}, | ||
| 407 | {"gpg_signature_popup", "FALSE", &prefs_common.gpg_signature_popup, | ||
| 408 | P_BOOL}, | ||
| 409 | {"store_passphrase", "FALSE", &prefs_common.store_passphrase, P_BOOL}, | ||
| 410 | {"store_passphrase_timeout", "0", | ||
| 411 | &prefs_common.store_passphrase_timeout, P_INT}, | ||
| 412 | {"passphrase_grab", "FALSE", &prefs_common.passphrase_grab, P_BOOL}, | ||
| 413 | #ifdef G_OS_WIN32 | ||
| 414 | {"show_gpg_warning", "FALSE", &prefs_common.gpg_warning, P_BOOL}, | ||
| 415 | #else | ||
| 416 | {"show_gpg_warning", "TRUE", &prefs_common.gpg_warning, P_BOOL}, | ||
| 417 | #endif | ||
| 418 | |||
| 419 | /* Interface */ | ||
| 420 | {"separate_folder", "FALSE", &prefs_common.sep_folder, P_BOOL}, | ||
| 421 | {"separate_message", "FALSE", &prefs_common.sep_msg, P_BOOL}, | ||
| 422 | |||
| 423 | {"always_show_message_when_selected", "TRUE", | ||
| 424 | &prefs_common.always_show_msg, P_BOOL}, | ||
| 425 | {"open_unread_on_enter", "FALSE", &prefs_common.open_unread_on_enter, | ||
| 426 | P_BOOL}, | ||
| 427 | {"always_mark_read_on_show_msg", "FALSE", | ||
| 428 | &prefs_common.always_mark_read_on_show_msg, P_BOOL}, | ||
| 429 | {"remember_last_selected", "FALSE", | ||
| 430 | &prefs_common.remember_last_selected, P_BOOL}, | ||
| 431 | {"mark_as_read_on_new_window", "FALSE", | ||
| 432 | &prefs_common.mark_as_read_on_new_window, P_BOOL}, | ||
| 433 | {"open_inbox_on_inc", "FALSE", &prefs_common.open_inbox_on_inc, P_BOOL}, | ||
| 434 | {"open_inbox_on_startup", "FALSE", &prefs_common.open_inbox_on_startup, | ||
| 435 | P_BOOL}, | ||
| 436 | {"change_account_on_folder_selection", "TRUE", | ||
| 437 | &prefs_common.change_account_on_folder_sel, P_BOOL}, | ||
| 438 | {"immediate_execution", "TRUE", &prefs_common.immediate_exec, P_BOOL}, | ||
| 439 | |||
| 440 | #ifdef G_OS_WIN32 | ||
| 441 | {"comply_gnome_hig", "FALSE", &prefs_common.comply_gnome_hig, P_BOOL}, | ||
| 442 | #else | ||
| 443 | {"comply_gnome_hig", "TRUE", &prefs_common.comply_gnome_hig, P_BOOL}, | ||
| 444 | #endif | ||
| 445 | |||
| 446 | {"show_trayicon", "TRUE", &prefs_common.show_trayicon, P_BOOL}, | ||
| 447 | {"minimize_to_tray", "FALSE", &prefs_common.minimize_to_tray, P_BOOL}, | ||
| 448 | #ifdef G_OS_WIN32 | ||
| 449 | {"toggle_window_on_trayicon_click", "FALSE", | ||
| 450 | #else | ||
| 451 | {"toggle_window_on_trayicon_click", "TRUE", | ||
| 452 | #endif | ||
| 453 | &prefs_common.toggle_window_on_trayicon_click, P_BOOL}, | ||
| 454 | |||
| 455 | /* Other */ | ||
| 456 | {"receive_dialog_mode", "1", &prefs_common.recv_dialog_mode, P_ENUM}, | ||
| 457 | {"no_receive_error_panel", "FALSE", &prefs_common.no_recv_err_panel, | ||
| 458 | P_BOOL}, | ||
| 459 | {"close_receive_dialog", "TRUE", &prefs_common.close_recv_dialog, | ||
| 460 | P_BOOL}, | ||
| 461 | |||
| 462 | {"add_address_by_click", "FALSE", &prefs_common.add_address_by_click, | ||
| 463 | P_BOOL}, | ||
| 464 | {"enable_address_completion", "TRUE", | ||
| 465 | &prefs_common.enable_address_completion, P_BOOL}, | ||
| 466 | {"fullauto_completion_mode", "TRUE", | ||
| 467 | &prefs_common.fullauto_completion_mode, P_BOOL}, | ||
| 468 | {"always_add_address_only", "FALSE", | ||
| 469 | &prefs_common.always_add_address_only, P_BOOL}, | ||
| 470 | |||
| 471 | {"confirm_on_exit", "FALSE", &prefs_common.confirm_on_exit, P_BOOL}, | ||
| 472 | {"clean_trash_on_exit", "FALSE", &prefs_common.clean_on_exit, P_BOOL}, | ||
| 473 | {"ask_on_cleaning", "TRUE", &prefs_common.ask_on_clean, P_BOOL}, | ||
| 474 | {"warn_queued_on_exit", "TRUE", &prefs_common.warn_queued_on_exit, | ||
| 475 | P_BOOL}, | ||
| 476 | |||
| 477 | {"logwindow_line_limit", "1000", &prefs_common.logwin_line_limit, | ||
| 478 | P_INT}, | ||
| 479 | |||
| 480 | {"online_mode", "TRUE", &prefs_common.online_mode, P_BOOL}, | ||
| 481 | {"startup_online_mode", "1", &prefs_common.startup_online_mode, P_INT}, | ||
| 482 | |||
| 483 | /* External commands */ | ||
| 484 | #ifdef G_OS_WIN32 | ||
| 485 | {"uri_open_command", NULL, &prefs_common.uri_cmd, | ||
| 486 | #else | ||
| 487 | {"uri_open_command", DEFAULT_BROWSER_CMD, &prefs_common.uri_cmd, | ||
| 488 | #endif | ||
| 489 | P_STRING}, | ||
| 490 | #ifdef G_OS_WIN32 | ||
| 491 | {"ext_editor_command", "notepad '%s'", &prefs_common.ext_editor_cmd, | ||
| 492 | #else | ||
| 493 | {"ext_editor_command", "gedit %s", &prefs_common.ext_editor_cmd, | ||
| 494 | #endif | ||
| 495 | P_STRING}, | ||
| 496 | |||
| 497 | {"use_print_command", "FALSE", &prefs_common.use_print_cmd, P_BOOL}, | ||
| 498 | #ifdef G_OS_WIN32 | ||
| 499 | {"print_command", NULL, &prefs_common.print_cmd, P_STRING}, | ||
| 500 | #else | ||
| 501 | {"print_command", "lpr %s", &prefs_common.print_cmd, P_STRING}, | ||
| 502 | #endif | ||
| 503 | |||
| 504 | {"use_ext_inc", "FALSE", &prefs_common.use_extinc, P_BOOL}, | ||
| 505 | {"ext_inc_path", DEFAULT_INC_PATH, &prefs_common.extinc_cmd, P_STRING}, | ||
| 506 | {"use_ext_sendmail", "FALSE", &prefs_common.use_extsend, P_BOOL}, | ||
| 507 | {"ext_sendmail_cmd", DEFAULT_SENDMAIL_CMD, &prefs_common.extsend_cmd, | ||
| 508 | P_STRING}, | ||
| 509 | |||
| 510 | /* Update check */ | ||
| 511 | {"auto_update_check", "TRUE", &prefs_common.auto_update_check, P_BOOL}, | ||
| 512 | {"use_http_proxy", "FALSE", &prefs_common.use_http_proxy, P_BOOL}, | ||
| 513 | {"http_proxy_host", NULL, &prefs_common.http_proxy_host, P_STRING}, | ||
| 514 | |||
| 515 | /* Advanced */ | ||
| 516 | {"strict_cache_check", "FALSE", &prefs_common.strict_cache_check, | ||
| 517 | P_BOOL}, | ||
| 518 | {"io_timeout_secs", "60", &prefs_common.io_timeout_secs, P_INT}, | ||
| 519 | |||
| 520 | /* File selector */ | ||
| 521 | {"filesel_prev_open_dir", NULL, &prefs_common.prev_open_dir, P_STRING}, | ||
| 522 | {"filesel_prev_save_dir", NULL, &prefs_common.prev_save_dir, P_STRING}, | ||
| 523 | {"filesel_prev_folder_dir", NULL, &prefs_common.prev_folder_dir, P_STRING}, | ||
| 524 | {"filesel_save_file_type", "0", &prefs_common.save_file_type, P_INT}, | ||
| 525 | |||
| 526 | {NULL, NULL, NULL, P_OTHER} | ||
| 527 | }; | ||
| 528 | |||
| 529 | |||
| 530 | PrefsCommon *prefs_common_get(void) | ||
| 531 | { | ||
| 532 | return &prefs_common; | ||
| 533 | } | ||
| 534 | |||
| 535 | PrefParam *prefs_common_get_params(void) | ||
| 536 | { | ||
| 537 | return param; | ||
| 538 | } | ||
| 539 | |||
| 540 | void prefs_common_read_config(void) | ||
| 541 | { | ||
| 542 | FILE *fp; | ||
| 543 | gchar *path; | ||
| 544 | gchar buf[PREFSBUFSIZE]; | ||
| 545 | |||
| 546 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL); | ||
| 547 | |||
| 548 | prefs_read_config(param, "Common", path, NULL); | ||
| 549 | |||
| 550 | g_free(path); | ||
| 551 | |||
| 552 | //prefs_common.online_mode = TRUE; | ||
| 553 | |||
| 554 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMAND_HISTORY, | ||
| 555 | NULL); | ||
| 556 | if ((fp = g_fopen(path, "rb")) == NULL) { | ||
| 557 | if (ENOENT != errno) FILE_OP_ERROR(path, "fopen"); | ||
| 558 | g_free(path); | ||
| 559 | return; | ||
| 560 | } | ||
| 561 | g_free(path); | ||
| 562 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 563 | g_strstrip(buf); | ||
| 564 | if (buf[0] == '\0') continue; | ||
| 565 | prefs_common.mime_open_cmd_history = | ||
| 566 | add_history(prefs_common.mime_open_cmd_history, buf); | ||
| 567 | } | ||
| 568 | fclose(fp); | ||
| 569 | |||
| 570 | prefs_common.mime_open_cmd_history = | ||
| 571 | g_list_reverse(prefs_common.mime_open_cmd_history); | ||
| 572 | } | ||
| 573 | |||
| 574 | void prefs_common_write_config(void) | ||
| 575 | { | ||
| 576 | GList *cur; | ||
| 577 | FILE *fp; | ||
| 578 | gchar *path; | ||
| 579 | |||
| 580 | prefs_write_config(param, "Common", COMMON_RC); | ||
| 581 | |||
| 582 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMAND_HISTORY, | ||
| 583 | NULL); | ||
| 584 | if ((fp = g_fopen(path, "wb")) == NULL) { | ||
| 585 | FILE_OP_ERROR(path, "fopen"); | ||
| 586 | g_free(path); | ||
| 587 | return; | ||
| 588 | } | ||
| 589 | |||
| 590 | for (cur = prefs_common.mime_open_cmd_history; | ||
| 591 | cur != NULL; cur = cur->next) { | ||
| 592 | fputs((gchar *)cur->data, fp); | ||
| 593 | fputc('\n', fp); | ||
| 594 | } | ||
| 595 | |||
| 596 | fclose(fp); | ||
| 597 | g_free(path); | ||
| 598 | } | ||
| 599 | |||
| 600 | void prefs_common_junk_filter_list_set(void) | ||
| 601 | { | ||
| 602 | } | ||
| 603 | |||
| 604 | void prefs_common_junk_folder_rename_path(const gchar *old_path, | ||
| 605 | const gchar *new_path) | ||
| 606 | { | ||
| 607 | gint len; | ||
| 608 | gchar *base; | ||
| 609 | gchar *dest_path; | ||
| 610 | |||
| 611 | g_return_if_fail(old_path != NULL); | ||
| 612 | g_return_if_fail(new_path != NULL); | ||
| 613 | |||
| 614 | if (!prefs_common.junk_folder) | ||
| 615 | return; | ||
| 616 | |||
| 617 | len = strlen(old_path); | ||
| 618 | |||
| 619 | if (!strncmp(old_path, prefs_common.junk_folder, len)) { | ||
| 620 | base = prefs_common.junk_folder + len; | ||
| 621 | if (*base != '/' && *base != '\0') | ||
| 622 | return; | ||
| 623 | while (*base == '/') base++; | ||
| 624 | if (*base == '\0') | ||
| 625 | dest_path = g_strdup(new_path); | ||
| 626 | else | ||
| 627 | dest_path = g_strconcat(new_path, "/", base, NULL); | ||
| 628 | debug_print("prefs_common_junk_folder_rename_path(): " | ||
| 629 | "renaming %s -> %s\n", prefs_common.junk_folder, | ||
| 630 | dest_path); | ||
| 631 | g_free(prefs_common.junk_folder); | ||
| 632 | prefs_common.junk_folder = dest_path; | ||
| 633 | } | ||
| 634 | } | ||
diff --git a/libsylph/prefs_common.h b/libsylph/prefs_common.h new file mode 100644 index 0000000..ba9ddb5 --- /dev/null +++ b/libsylph/prefs_common.h | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PREFS_COMMON_H__ | ||
| 21 | #define __PREFS_COMMON_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | |||
| 29 | typedef struct _PrefsCommon PrefsCommon; | ||
| 30 | |||
| 31 | #include "enums.h" | ||
| 32 | #include "prefs.h" | ||
| 33 | |||
| 34 | typedef enum { | ||
| 35 | RECV_DIALOG_ALWAYS, | ||
| 36 | RECV_DIALOG_MANUAL, | ||
| 37 | RECV_DIALOG_NEVER | ||
| 38 | } RecvDialogMode; | ||
| 39 | |||
| 40 | typedef enum { | ||
| 41 | CTE_AUTO, | ||
| 42 | CTE_BASE64, | ||
| 43 | CTE_QUOTED_PRINTABLE, | ||
| 44 | CTE_8BIT | ||
| 45 | } TransferEncodingMethod; | ||
| 46 | |||
| 47 | typedef enum { | ||
| 48 | FENC_MIME, | ||
| 49 | FENC_RFC2231, | ||
| 50 | FENC_NONE | ||
| 51 | } MIMEFilenameEncodingMethod; | ||
| 52 | |||
| 53 | struct _PrefsCommon | ||
| 54 | { | ||
| 55 | /* Receive */ | ||
| 56 | gboolean scan_all_after_inc; | ||
| 57 | gboolean autochk_newmail; | ||
| 58 | gint autochk_itv; | ||
| 59 | gboolean chk_on_startup; | ||
| 60 | gboolean enable_newmsg_notify; | ||
| 61 | gchar *newmsg_notify_cmd; | ||
| 62 | |||
| 63 | gboolean inc_local; | ||
| 64 | gboolean filter_on_inc; | ||
| 65 | gchar *spool_path; | ||
| 66 | |||
| 67 | /* Send */ | ||
| 68 | gboolean savemsg; | ||
| 69 | gboolean filter_sent; | ||
| 70 | TransferEncodingMethod encoding_method; | ||
| 71 | MIMEFilenameEncodingMethod mime_fencoding_method; | ||
| 72 | |||
| 73 | gboolean check_attach; | ||
| 74 | gchar *check_attach_str; | ||
| 75 | gboolean check_recipients; | ||
| 76 | gchar *check_recp_exclude; | ||
| 77 | |||
| 78 | gboolean allow_jisx0201_kana; | ||
| 79 | |||
| 80 | /* Compose */ | ||
| 81 | gboolean auto_sig; | ||
| 82 | gchar *sig_sep; | ||
| 83 | gint undolevels; | ||
| 84 | gint linewrap_len; | ||
| 85 | gboolean linewrap_quote; | ||
| 86 | gboolean autowrap; | ||
| 87 | gboolean auto_exteditor; | ||
| 88 | gboolean enable_autosave; | ||
| 89 | gint autosave_itv; | ||
| 90 | gboolean reply_account_autosel; | ||
| 91 | gboolean default_reply_list; | ||
| 92 | gboolean inherit_recipient_on_self_reply; | ||
| 93 | |||
| 94 | gboolean show_ruler; | ||
| 95 | |||
| 96 | /* Quote */ | ||
| 97 | gboolean reply_with_quote; | ||
| 98 | gchar *quotemark; | ||
| 99 | gchar *quotefmt; | ||
| 100 | gchar *fw_quotemark; | ||
| 101 | gchar *fw_quotefmt; | ||
| 102 | |||
| 103 | /* Spelling */ | ||
| 104 | gboolean check_spell; | ||
| 105 | gchar *spell_lang; | ||
| 106 | |||
| 107 | /* Display */ | ||
| 108 | gchar *textfont; | ||
| 109 | |||
| 110 | gboolean trans_hdr; | ||
| 111 | gboolean display_folder_unread; | ||
| 112 | gboolean display_folder_num_columns; | ||
| 113 | gint ng_abbrev_len; | ||
| 114 | |||
| 115 | gboolean swap_from; | ||
| 116 | gboolean expand_thread; | ||
| 117 | gchar *date_format; | ||
| 118 | |||
| 119 | gboolean enable_rules_hint; | ||
| 120 | gboolean bold_unread; | ||
| 121 | |||
| 122 | gboolean persist_qsearch_filter; | ||
| 123 | |||
| 124 | ToolbarStyle toolbar_style; | ||
| 125 | gboolean show_searchbar; | ||
| 126 | gboolean show_statusbar; | ||
| 127 | |||
| 128 | gchar *main_toolbar_setting; | ||
| 129 | gchar *compose_toolbar_setting; | ||
| 130 | |||
| 131 | LayoutType layout_type; | ||
| 132 | |||
| 133 | /* Summary columns visibility, position and size */ | ||
| 134 | gboolean summary_col_visible[N_SUMMARY_COLS]; | ||
| 135 | gint summary_col_pos[N_SUMMARY_COLS]; | ||
| 136 | gboolean summary_sent_col_visible[N_SUMMARY_COLS]; | ||
| 137 | gint summary_sent_col_pos[N_SUMMARY_COLS]; | ||
| 138 | gint summary_col_size[N_SUMMARY_COLS]; | ||
| 139 | |||
| 140 | /* Widget visibility, position and size */ | ||
| 141 | gint folderwin_x; | ||
| 142 | gint folderwin_y; | ||
| 143 | gint folderview_width; | ||
| 144 | gint folderview_height; | ||
| 145 | gboolean folderview_visible; | ||
| 146 | |||
| 147 | gint folder_col_folder; | ||
| 148 | gint folder_col_new; | ||
| 149 | gint folder_col_unread; | ||
| 150 | gint folder_col_total; | ||
| 151 | |||
| 152 | gint summaryview_width; | ||
| 153 | gint summaryview_height; | ||
| 154 | gint summaryview_vwidth; | ||
| 155 | gint summaryview_vheight; | ||
| 156 | |||
| 157 | gint main_msgwin_x; | ||
| 158 | gint main_msgwin_y; | ||
| 159 | gint msgview_width; | ||
| 160 | gint msgview_height; | ||
| 161 | gint msgview_vwidth; | ||
| 162 | gint msgview_vheight; | ||
| 163 | gboolean msgview_visible; | ||
| 164 | |||
| 165 | gint mainview_x; | ||
| 166 | gint mainview_y; | ||
| 167 | gint mainview_width; | ||
| 168 | gint mainview_height; | ||
| 169 | gint mainwin_x; | ||
| 170 | gint mainwin_y; | ||
| 171 | gint mainwin_width; | ||
| 172 | gint mainwin_height; | ||
| 173 | |||
| 174 | gint msgwin_width; | ||
| 175 | gint msgwin_height; | ||
| 176 | |||
| 177 | gboolean mainwin_maximized; | ||
| 178 | |||
| 179 | gint sourcewin_width; | ||
| 180 | gint sourcewin_height; | ||
| 181 | |||
| 182 | gint compose_x; | ||
| 183 | gint compose_y; | ||
| 184 | gint compose_width; | ||
| 185 | gint compose_height; | ||
| 186 | |||
| 187 | gboolean compose_maximized; | ||
| 188 | |||
| 189 | gint addressbook_x; | ||
| 190 | gint addressbook_y; | ||
| 191 | gint addressbook_width; | ||
| 192 | gint addressbook_height; | ||
| 193 | |||
| 194 | /* Message */ | ||
| 195 | gboolean enable_color; | ||
| 196 | gint quote_level1_col; | ||
| 197 | gint quote_level2_col; | ||
| 198 | gint quote_level3_col; | ||
| 199 | gint uri_col; | ||
| 200 | gushort sig_col; | ||
| 201 | gboolean recycle_quote_colors; | ||
| 202 | gboolean conv_mb_alnum; | ||
| 203 | gboolean display_header_pane; | ||
| 204 | gboolean display_header; | ||
| 205 | gint line_space; | ||
| 206 | gboolean render_html; | ||
| 207 | gboolean html_only_as_attach; | ||
| 208 | gboolean textview_cursor_visible; | ||
| 209 | gboolean enable_smooth_scroll; | ||
| 210 | gint scroll_step; | ||
| 211 | gboolean scroll_halfpage; | ||
| 212 | |||
| 213 | gboolean resize_image; | ||
| 214 | gboolean inline_image; | ||
| 215 | |||
| 216 | /* Encoding */ | ||
| 217 | gchar *force_charset; | ||
| 218 | gchar *default_encoding; | ||
| 219 | gchar *outgoing_charset; | ||
| 220 | |||
| 221 | gboolean show_other_header; | ||
| 222 | GSList *disphdr_list; | ||
| 223 | |||
| 224 | /* MIME viewer */ | ||
| 225 | gchar *mime_image_viewer; | ||
| 226 | gchar *mime_audio_player; | ||
| 227 | gchar *mime_open_cmd; | ||
| 228 | gchar *mime_cmd; | ||
| 229 | |||
| 230 | GList *mime_open_cmd_history; | ||
| 231 | |||
| 232 | /* Junk Mail */ | ||
| 233 | gboolean enable_junk; | ||
| 234 | gchar *junk_learncmd; | ||
| 235 | gchar *nojunk_learncmd; | ||
| 236 | gchar *junk_classify_cmd; | ||
| 237 | gchar *junk_folder; | ||
| 238 | gboolean filter_junk_on_recv; | ||
| 239 | gboolean filter_junk_before; | ||
| 240 | gboolean delete_junk_on_recv; | ||
| 241 | gboolean mark_junk_as_read; | ||
| 242 | |||
| 243 | /* Privacy */ | ||
| 244 | gboolean auto_check_signatures; | ||
| 245 | gboolean gpg_signature_popup; | ||
| 246 | gboolean store_passphrase; | ||
| 247 | gint store_passphrase_timeout; | ||
| 248 | gboolean passphrase_grab; | ||
| 249 | gboolean gpg_warning; | ||
| 250 | |||
| 251 | /* Interface */ | ||
| 252 | gboolean sep_folder; | ||
| 253 | gboolean sep_msg; | ||
| 254 | gboolean always_show_msg; | ||
| 255 | gboolean open_unread_on_enter; | ||
| 256 | gboolean remember_last_selected; | ||
| 257 | gboolean mark_as_read_on_new_window; | ||
| 258 | gboolean open_inbox_on_inc; | ||
| 259 | gboolean open_inbox_on_startup; | ||
| 260 | gboolean immediate_exec; | ||
| 261 | gboolean comply_gnome_hig; | ||
| 262 | gboolean show_trayicon; | ||
| 263 | gboolean minimize_to_tray; | ||
| 264 | gboolean toggle_window_on_trayicon_click; | ||
| 265 | |||
| 266 | /* Other */ | ||
| 267 | RecvDialogMode recv_dialog_mode; | ||
| 268 | gboolean no_recv_err_panel; | ||
| 269 | gboolean close_recv_dialog; | ||
| 270 | |||
| 271 | gboolean add_address_by_click; | ||
| 272 | |||
| 273 | gboolean confirm_on_exit; | ||
| 274 | gboolean clean_on_exit; | ||
| 275 | gboolean ask_on_clean; | ||
| 276 | gboolean warn_queued_on_exit; | ||
| 277 | |||
| 278 | gint logwin_line_limit; | ||
| 279 | |||
| 280 | /* External commands */ | ||
| 281 | gchar *uri_cmd; | ||
| 282 | gchar *ext_editor_cmd; | ||
| 283 | |||
| 284 | gboolean use_print_cmd; | ||
| 285 | gchar *print_cmd; | ||
| 286 | |||
| 287 | gboolean use_extinc; | ||
| 288 | gchar *extinc_cmd; | ||
| 289 | gboolean use_extsend; | ||
| 290 | gchar *extsend_cmd; | ||
| 291 | |||
| 292 | /* Update check */ | ||
| 293 | gboolean auto_update_check; | ||
| 294 | gboolean use_http_proxy; | ||
| 295 | gchar *http_proxy_host; | ||
| 296 | |||
| 297 | /* Advanced */ | ||
| 298 | gboolean strict_cache_check; | ||
| 299 | gint io_timeout_secs; | ||
| 300 | |||
| 301 | /* Filtering */ | ||
| 302 | GSList *fltlist; | ||
| 303 | |||
| 304 | /* deprecated: do not use */ | ||
| 305 | GSList *junk_fltlist; | ||
| 306 | GSList *manual_junk_fltlist; | ||
| 307 | |||
| 308 | /* Actions */ | ||
| 309 | GSList *actions_list; | ||
| 310 | |||
| 311 | /* Online / Offline */ | ||
| 312 | gboolean online_mode; | ||
| 313 | |||
| 314 | /* Append new members here */ | ||
| 315 | gboolean folder_col_visible[4]; /* Display */ | ||
| 316 | gboolean reply_address_only; /* Compose */ | ||
| 317 | gboolean recipients_autoreg; /* Send */ | ||
| 318 | gboolean enable_address_completion; /* Compose */ | ||
| 319 | gboolean fullauto_completion_mode; /* Compose */ | ||
| 320 | |||
| 321 | gchar *user_agent_str; | ||
| 322 | |||
| 323 | gboolean change_account_on_folder_sel; /* Interface */ | ||
| 324 | gboolean always_mark_read_on_show_msg; /* Interface */ | ||
| 325 | |||
| 326 | gboolean always_add_address_only; /* Compose */ | ||
| 327 | gboolean show_send_dialog; /* Send */ | ||
| 328 | |||
| 329 | gint addressbook_folder_width; | ||
| 330 | gint addressbook_col_name; | ||
| 331 | gint addressbook_col_addr; | ||
| 332 | gint addressbook_col_rem; | ||
| 333 | |||
| 334 | gchar *prev_open_dir; | ||
| 335 | gchar *prev_save_dir; | ||
| 336 | gchar *prev_folder_dir; | ||
| 337 | |||
| 338 | gboolean enable_newmsg_notify_sound; /* Receive */ | ||
| 339 | gchar *newmsg_notify_sound; /* Receive */ | ||
| 340 | |||
| 341 | gboolean show_attach_tab; /* Message - Attachment */ | ||
| 342 | gboolean show_attached_files_first; /* Message - Attachment */ | ||
| 343 | gint attach_toolbutton_pos; /* Message - Attachment */ | ||
| 344 | |||
| 345 | gboolean enable_newmsg_notify_window; /* Receive */ | ||
| 346 | |||
| 347 | gboolean nofilter_junk_sender_in_book; /* Junk Mail */ | ||
| 348 | |||
| 349 | gboolean alt_prefer_html; /* Message */ | ||
| 350 | |||
| 351 | gint save_file_type; | ||
| 352 | |||
| 353 | gint notify_window_period; /* Receive */ | ||
| 354 | |||
| 355 | gint startup_online_mode; /* Online */ | ||
| 356 | |||
| 357 | gint addressbook_col_nickname; | ||
| 358 | }; | ||
| 359 | |||
| 360 | extern PrefsCommon prefs_common; | ||
| 361 | |||
| 362 | PrefsCommon *prefs_common_get (void); | ||
| 363 | |||
| 364 | PrefParam *prefs_common_get_params (void); | ||
| 365 | |||
| 366 | void prefs_common_read_config (void); | ||
| 367 | void prefs_common_write_config (void); | ||
| 368 | |||
| 369 | /* deprecated */ | ||
| 370 | void prefs_common_junk_filter_list_set (void); | ||
| 371 | |||
| 372 | void prefs_common_junk_folder_rename_path (const gchar *old_path, | ||
| 373 | const gchar *new_path); | ||
| 374 | |||
| 375 | #endif /* __PREFS_COMMON_H__ */ | ||
diff --git a/libsylph/procheader.c b/libsylph/procheader.c new file mode 100644 index 0000000..1dcffa2 --- /dev/null +++ b/libsylph/procheader.c | |||
| @@ -0,0 +1,979 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2013 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <glib/gi18n.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <string.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <time.h> | ||
| 30 | #include <sys/stat.h> | ||
| 31 | |||
| 32 | #include "procheader.h" | ||
| 33 | #include "procmsg.h" | ||
| 34 | #include "codeconv.h" | ||
| 35 | #include "displayheader.h" | ||
| 36 | #include "prefs_common.h" | ||
| 37 | #include "utils.h" | ||
| 38 | |||
| 39 | #define BUFFSIZE 8192 | ||
| 40 | |||
| 41 | gint procheader_get_one_field(gchar *buf, size_t len, FILE *fp, | ||
| 42 | HeaderEntry hentry[]) | ||
| 43 | { | ||
| 44 | gint nexthead; | ||
| 45 | gint hnum = 0; | ||
| 46 | HeaderEntry *hp = NULL; | ||
| 47 | |||
| 48 | if (hentry != NULL) { | ||
| 49 | /* skip non-required headers */ | ||
| 50 | do { | ||
| 51 | do { | ||
| 52 | if (fgets(buf, len, fp) == NULL) | ||
| 53 | return -1; | ||
| 54 | if (buf[0] == '\r' || buf[0] == '\n') | ||
| 55 | return -1; | ||
| 56 | } while (buf[0] == ' ' || buf[0] == '\t'); | ||
| 57 | |||
| 58 | for (hp = hentry, hnum = 0; hp->name != NULL; | ||
| 59 | hp++, hnum++) { | ||
| 60 | if (!g_ascii_strncasecmp(hp->name, buf, | ||
| 61 | strlen(hp->name))) | ||
| 62 | break; | ||
| 63 | } | ||
| 64 | } while (hp->name == NULL); | ||
| 65 | } else { | ||
| 66 | if (fgets(buf, len, fp) == NULL) return -1; | ||
| 67 | if (buf[0] == '\r' || buf[0] == '\n') return -1; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* unfold the specified folded line */ | ||
| 71 | if (hp && hp->unfold) { | ||
| 72 | gboolean folded = FALSE; | ||
| 73 | gchar *bufp = buf + strlen(buf); | ||
| 74 | |||
| 75 | for (; bufp > buf && | ||
| 76 | (*(bufp - 1) == '\n' || *(bufp - 1) == '\r'); | ||
| 77 | bufp--) | ||
| 78 | *(bufp - 1) = '\0'; | ||
| 79 | |||
| 80 | while (1) { | ||
| 81 | nexthead = fgetc(fp); | ||
| 82 | |||
| 83 | /* folded */ | ||
| 84 | if (nexthead == ' ' || nexthead == '\t') | ||
| 85 | folded = TRUE; | ||
| 86 | else if (nexthead == EOF) | ||
| 87 | break; | ||
| 88 | else if (folded == TRUE) { | ||
| 89 | if ((len - (bufp - buf)) <= 2) break; | ||
| 90 | |||
| 91 | if (nexthead == '\n') { | ||
| 92 | folded = FALSE; | ||
| 93 | continue; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* replace return code on the tail end | ||
| 97 | with space */ | ||
| 98 | *bufp++ = ' '; | ||
| 99 | *bufp++ = nexthead; | ||
| 100 | *bufp = '\0'; | ||
| 101 | |||
| 102 | /* concatenate next line */ | ||
| 103 | if (fgets(bufp, len - (bufp - buf), fp) | ||
| 104 | == NULL) break; | ||
| 105 | bufp += strlen(bufp); | ||
| 106 | |||
| 107 | for (; bufp > buf && | ||
| 108 | (*(bufp - 1) == '\n' || *(bufp - 1) == '\r'); | ||
| 109 | bufp--) | ||
| 110 | *(bufp - 1) = '\0'; | ||
| 111 | |||
| 112 | folded = FALSE; | ||
| 113 | } else { | ||
| 114 | ungetc(nexthead, fp); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | return hnum; | ||
| 120 | } | ||
| 121 | |||
| 122 | while (1) { | ||
| 123 | nexthead = fgetc(fp); | ||
| 124 | if (nexthead == ' ' || nexthead == '\t') { | ||
| 125 | size_t buflen = strlen(buf); | ||
| 126 | |||
| 127 | /* concatenate next line */ | ||
| 128 | if ((len - buflen) > 2) { | ||
| 129 | gchar *p = buf + buflen; | ||
| 130 | |||
| 131 | *p++ = nexthead; | ||
| 132 | *p = '\0'; | ||
| 133 | buflen++; | ||
| 134 | if (fgets(p, len - buflen, fp) == NULL) | ||
| 135 | break; | ||
| 136 | } else | ||
| 137 | break; | ||
| 138 | } else { | ||
| 139 | if (nexthead != EOF) | ||
| 140 | ungetc(nexthead, fp); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | /* remove trailing return code */ | ||
| 146 | strretchomp(buf); | ||
| 147 | |||
| 148 | return hnum; | ||
| 149 | } | ||
| 150 | |||
| 151 | gchar *procheader_get_unfolded_line(gchar *buf, size_t len, FILE *fp) | ||
| 152 | { | ||
| 153 | gboolean folded = FALSE; | ||
| 154 | gint nexthead; | ||
| 155 | gchar *bufp; | ||
| 156 | |||
| 157 | if (fgets(buf, len, fp) == NULL) return NULL; | ||
| 158 | if (buf[0] == '\r' || buf[0] == '\n') return NULL; | ||
| 159 | bufp = buf + strlen(buf); | ||
| 160 | |||
| 161 | for (; bufp > buf && | ||
| 162 | (*(bufp - 1) == '\n' || *(bufp - 1) == '\r'); | ||
| 163 | bufp--) | ||
| 164 | *(bufp - 1) = '\0'; | ||
| 165 | |||
| 166 | while (1) { | ||
| 167 | nexthead = fgetc(fp); | ||
| 168 | |||
| 169 | /* folded */ | ||
| 170 | if (nexthead == ' ' || nexthead == '\t') | ||
| 171 | folded = TRUE; | ||
| 172 | else if (nexthead == EOF) | ||
| 173 | break; | ||
| 174 | else if (folded == TRUE) { | ||
| 175 | if ((len - (bufp - buf)) <= 2) break; | ||
| 176 | |||
| 177 | if (nexthead == '\n') { | ||
| 178 | folded = FALSE; | ||
| 179 | continue; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* replace return code on the tail end | ||
| 183 | with space */ | ||
| 184 | *bufp++ = ' '; | ||
| 185 | *bufp++ = nexthead; | ||
| 186 | *bufp = '\0'; | ||
| 187 | |||
| 188 | /* concatenate next line */ | ||
| 189 | if (fgets(bufp, len - (bufp - buf), fp) | ||
| 190 | == NULL) break; | ||
| 191 | bufp += strlen(bufp); | ||
| 192 | |||
| 193 | for (; bufp > buf && | ||
| 194 | (*(bufp - 1) == '\n' || *(bufp - 1) == '\r'); | ||
| 195 | bufp--) | ||
| 196 | *(bufp - 1) = '\0'; | ||
| 197 | |||
| 198 | folded = FALSE; | ||
| 199 | } else { | ||
| 200 | ungetc(nexthead, fp); | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | /* remove trailing return code */ | ||
| 206 | strretchomp(buf); | ||
| 207 | |||
| 208 | return buf; | ||
| 209 | } | ||
| 210 | |||
| 211 | GSList *procheader_get_header_list_from_file(const gchar *file) | ||
| 212 | { | ||
| 213 | FILE *fp; | ||
| 214 | GSList *hlist; | ||
| 215 | |||
| 216 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 217 | FILE_OP_ERROR(file, "procheader_get_header_list_from_file: fopen"); | ||
| 218 | return NULL; | ||
| 219 | } | ||
| 220 | |||
| 221 | hlist = procheader_get_header_list(fp); | ||
| 222 | |||
| 223 | fclose(fp); | ||
| 224 | return hlist; | ||
| 225 | } | ||
| 226 | |||
| 227 | GSList *procheader_get_header_list(FILE *fp) | ||
| 228 | { | ||
| 229 | gchar buf[BUFFSIZE]; | ||
| 230 | gchar *p; | ||
| 231 | GSList *hlist = NULL; | ||
| 232 | Header *header; | ||
| 233 | |||
| 234 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 235 | |||
| 236 | while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) { | ||
| 237 | if (*buf == ':') continue; | ||
| 238 | for (p = buf; *p && *p != ' '; p++) { | ||
| 239 | if (*p == ':') { | ||
| 240 | header = g_new(Header, 1); | ||
| 241 | header->name = g_strndup(buf, p - buf); | ||
| 242 | p++; | ||
| 243 | while (*p == ' ' || *p == '\t') p++; | ||
| 244 | header->body = conv_unmime_header(p, NULL); | ||
| 245 | |||
| 246 | hlist = g_slist_append(hlist, header); | ||
| 247 | break; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | return hlist; | ||
| 253 | } | ||
| 254 | |||
| 255 | GSList *procheader_get_header_list_from_msginfo(MsgInfo *msginfo) | ||
| 256 | { | ||
| 257 | GSList *hlist = NULL; | ||
| 258 | |||
| 259 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 260 | |||
| 261 | if (msginfo->subject) | ||
| 262 | hlist = procheader_add_header_list(hlist, "Subject", | ||
| 263 | msginfo->subject); | ||
| 264 | if (msginfo->from) | ||
| 265 | hlist = procheader_add_header_list(hlist, "From", | ||
| 266 | msginfo->from); | ||
| 267 | if (msginfo->to) | ||
| 268 | hlist = procheader_add_header_list(hlist, "To", msginfo->to); | ||
| 269 | if (msginfo->cc) | ||
| 270 | hlist = procheader_add_header_list(hlist, "Cc", msginfo->cc); | ||
| 271 | if (msginfo->newsgroups) | ||
| 272 | hlist = procheader_add_header_list(hlist, "Newsgroups", | ||
| 273 | msginfo->newsgroups); | ||
| 274 | if (msginfo->date) | ||
| 275 | hlist = procheader_add_header_list(hlist, "Date", | ||
| 276 | msginfo->date); | ||
| 277 | |||
| 278 | return hlist; | ||
| 279 | } | ||
| 280 | |||
| 281 | GSList *procheader_add_header_list(GSList *hlist, const gchar *header_name, | ||
| 282 | const gchar *body) | ||
| 283 | { | ||
| 284 | Header *header; | ||
| 285 | |||
| 286 | g_return_val_if_fail(header_name != NULL, hlist); | ||
| 287 | |||
| 288 | header = g_new(Header, 1); | ||
| 289 | header->name = g_strdup(header_name); | ||
| 290 | header->body = g_strdup(body); | ||
| 291 | |||
| 292 | return g_slist_append(hlist, header); | ||
| 293 | } | ||
| 294 | |||
| 295 | GSList *procheader_copy_header_list(GSList *hlist) | ||
| 296 | { | ||
| 297 | GSList *newlist = NULL, *cur; | ||
| 298 | |||
| 299 | for (cur = hlist; cur != NULL; cur = cur->next) { | ||
| 300 | Header *header = (Header *)cur->data; | ||
| 301 | newlist = procheader_add_header_list(newlist, header->name, | ||
| 302 | header->body); | ||
| 303 | } | ||
| 304 | |||
| 305 | return newlist; | ||
| 306 | } | ||
| 307 | |||
| 308 | GSList *procheader_merge_header_list(GSList *hlist1, GSList *hlist2) | ||
| 309 | { | ||
| 310 | GSList *cur; | ||
| 311 | |||
| 312 | for (cur = hlist2; cur != NULL; cur = cur->next) { | ||
| 313 | Header *header = (Header *)cur->data; | ||
| 314 | if (procheader_find_header_list(hlist1, header->name) < 0) | ||
| 315 | hlist1 = g_slist_append(hlist1, header); | ||
| 316 | } | ||
| 317 | |||
| 318 | return hlist1; | ||
| 319 | } | ||
| 320 | |||
| 321 | GSList *procheader_merge_header_list_dup(GSList *hlist1, GSList *hlist2) | ||
| 322 | { | ||
| 323 | GSList *list, *cur; | ||
| 324 | |||
| 325 | list = procheader_copy_header_list(hlist1); | ||
| 326 | |||
| 327 | for (cur = hlist2; cur != NULL; cur = cur->next) { | ||
| 328 | Header *header = (Header *)cur->data; | ||
| 329 | if (procheader_find_header_list(list, header->name) < 0) | ||
| 330 | list = procheader_add_header_list(list, header->name, | ||
| 331 | header->body); | ||
| 332 | } | ||
| 333 | |||
| 334 | return list; | ||
| 335 | } | ||
| 336 | |||
| 337 | gint procheader_find_header_list(GSList *hlist, const gchar *header_name) | ||
| 338 | { | ||
| 339 | GSList *cur; | ||
| 340 | gint index = 0; | ||
| 341 | Header *header; | ||
| 342 | |||
| 343 | g_return_val_if_fail(header_name != NULL, -1); | ||
| 344 | |||
| 345 | for (cur = hlist; cur != NULL; cur = cur->next, index++) { | ||
| 346 | header = (Header *)cur->data; | ||
| 347 | if (g_ascii_strcasecmp(header->name, header_name) == 0) | ||
| 348 | return index; | ||
| 349 | } | ||
| 350 | |||
| 351 | return -1; | ||
| 352 | } | ||
| 353 | |||
| 354 | GPtrArray *procheader_get_header_array(FILE *fp, const gchar *encoding) | ||
| 355 | { | ||
| 356 | gchar buf[BUFFSIZE]; | ||
| 357 | gchar *p; | ||
| 358 | GPtrArray *headers; | ||
| 359 | Header *header; | ||
| 360 | |||
| 361 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 362 | |||
| 363 | headers = g_ptr_array_new(); | ||
| 364 | |||
| 365 | while (procheader_get_unfolded_line(buf, sizeof(buf), fp) != NULL) { | ||
| 366 | if (*buf == ':') continue; | ||
| 367 | for (p = buf; *p && *p != ' '; p++) { | ||
| 368 | if (*p == ':') { | ||
| 369 | header = g_new(Header, 1); | ||
| 370 | header->name = g_strndup(buf, p - buf); | ||
| 371 | p++; | ||
| 372 | while (*p == ' ' || *p == '\t') p++; | ||
| 373 | header->body = conv_unmime_header(p, encoding); | ||
| 374 | |||
| 375 | g_ptr_array_add(headers, header); | ||
| 376 | break; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | return headers; | ||
| 382 | } | ||
| 383 | |||
| 384 | GPtrArray *procheader_get_header_array_asis(FILE *fp, const gchar *encoding) | ||
| 385 | { | ||
| 386 | gchar buf[BUFFSIZE]; | ||
| 387 | gchar *p; | ||
| 388 | GPtrArray *headers; | ||
| 389 | Header *header; | ||
| 390 | |||
| 391 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 392 | |||
| 393 | headers = g_ptr_array_new(); | ||
| 394 | |||
| 395 | while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) { | ||
| 396 | if (*buf == ':') continue; | ||
| 397 | for (p = buf; *p && *p != ' '; p++) { | ||
| 398 | if (*p == ':') { | ||
| 399 | header = g_new(Header, 1); | ||
| 400 | header->name = g_strndup(buf, p - buf); | ||
| 401 | p++; | ||
| 402 | header->body = conv_unmime_header(p, encoding); | ||
| 403 | |||
| 404 | g_ptr_array_add(headers, header); | ||
| 405 | break; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | } | ||
| 409 | |||
| 410 | return headers; | ||
| 411 | } | ||
| 412 | |||
| 413 | GPtrArray *procheader_get_header_array_for_display(FILE *fp, | ||
| 414 | const gchar *encoding) | ||
| 415 | { | ||
| 416 | GPtrArray *headers, *sorted_headers; | ||
| 417 | GSList *disphdr_list; | ||
| 418 | Header *header; | ||
| 419 | gint i; | ||
| 420 | |||
| 421 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 422 | |||
| 423 | headers = procheader_get_header_array_asis(fp, encoding); | ||
| 424 | |||
| 425 | sorted_headers = g_ptr_array_new(); | ||
| 426 | |||
| 427 | for (disphdr_list = prefs_common.disphdr_list; disphdr_list != NULL; | ||
| 428 | disphdr_list = disphdr_list->next) { | ||
| 429 | DisplayHeaderProp *dp = | ||
| 430 | (DisplayHeaderProp *)disphdr_list->data; | ||
| 431 | |||
| 432 | for (i = 0; i < headers->len; i++) { | ||
| 433 | header = g_ptr_array_index(headers, i); | ||
| 434 | |||
| 435 | if (!g_ascii_strcasecmp(header->name, dp->name)) { | ||
| 436 | if (dp->hidden) | ||
| 437 | procheader_header_free(header); | ||
| 438 | else | ||
| 439 | g_ptr_array_add(sorted_headers, header); | ||
| 440 | |||
| 441 | g_ptr_array_remove_index(headers, i); | ||
| 442 | i--; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | if (prefs_common.show_other_header) { | ||
| 448 | for (i = 0; i < headers->len; i++) { | ||
| 449 | header = g_ptr_array_index(headers, i); | ||
| 450 | g_ptr_array_add(sorted_headers, header); | ||
| 451 | } | ||
| 452 | g_ptr_array_free(headers, TRUE); | ||
| 453 | } else | ||
| 454 | procheader_header_array_destroy(headers); | ||
| 455 | |||
| 456 | return sorted_headers; | ||
| 457 | } | ||
| 458 | |||
| 459 | void procheader_header_list_destroy(GSList *hlist) | ||
| 460 | { | ||
| 461 | Header *header; | ||
| 462 | |||
| 463 | while (hlist != NULL) { | ||
| 464 | header = hlist->data; | ||
| 465 | procheader_header_free(header); | ||
| 466 | hlist = g_slist_remove(hlist, header); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | void procheader_header_array_destroy(GPtrArray *harray) | ||
| 471 | { | ||
| 472 | gint i; | ||
| 473 | Header *header; | ||
| 474 | |||
| 475 | for (i = 0; i < harray->len; i++) { | ||
| 476 | header = g_ptr_array_index(harray, i); | ||
| 477 | procheader_header_free(header); | ||
| 478 | } | ||
| 479 | |||
| 480 | g_ptr_array_free(harray, TRUE); | ||
| 481 | } | ||
| 482 | |||
| 483 | void procheader_header_free(Header *header) | ||
| 484 | { | ||
| 485 | if (!header) return; | ||
| 486 | |||
| 487 | g_free(header->name); | ||
| 488 | g_free(header->body); | ||
| 489 | g_free(header); | ||
| 490 | } | ||
| 491 | |||
| 492 | void procheader_get_header_fields(FILE *fp, HeaderEntry hentry[]) | ||
| 493 | { | ||
| 494 | gchar buf[BUFFSIZE]; | ||
| 495 | HeaderEntry *hp; | ||
| 496 | gint hnum; | ||
| 497 | gchar *p; | ||
| 498 | |||
| 499 | if (hentry == NULL) return; | ||
| 500 | |||
| 501 | while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) | ||
| 502 | != -1) { | ||
| 503 | hp = hentry + hnum; | ||
| 504 | |||
| 505 | p = buf + strlen(hp->name); | ||
| 506 | while (*p == ' ' || *p == '\t') p++; | ||
| 507 | |||
| 508 | if (hp->body == NULL) | ||
| 509 | hp->body = g_strdup(p); | ||
| 510 | else if (!g_ascii_strcasecmp(hp->name, "To:") || | ||
| 511 | !g_ascii_strcasecmp(hp->name, "Cc:")) { | ||
| 512 | gchar *tp = hp->body; | ||
| 513 | hp->body = g_strconcat(tp, ", ", p, NULL); | ||
| 514 | g_free(tp); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | MsgInfo *procheader_parse_file(const gchar *file, MsgFlags flags, | ||
| 520 | gboolean full) | ||
| 521 | { | ||
| 522 | GStatBuf s; | ||
| 523 | FILE *fp; | ||
| 524 | MsgInfo *msginfo; | ||
| 525 | |||
| 526 | if (g_stat(file, &s) < 0) { | ||
| 527 | FILE_OP_ERROR(file, "stat"); | ||
| 528 | return NULL; | ||
| 529 | } | ||
| 530 | if (!S_ISREG(s.st_mode)) | ||
| 531 | return NULL; | ||
| 532 | |||
| 533 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 534 | FILE_OP_ERROR(file, "procheader_parse_file: fopen"); | ||
| 535 | return NULL; | ||
| 536 | } | ||
| 537 | |||
| 538 | msginfo = procheader_parse_stream(fp, flags, full); | ||
| 539 | fclose(fp); | ||
| 540 | |||
| 541 | if (msginfo) { | ||
| 542 | msginfo->size = s.st_size; | ||
| 543 | msginfo->mtime = s.st_mtime; | ||
| 544 | } | ||
| 545 | |||
| 546 | return msginfo; | ||
| 547 | } | ||
| 548 | |||
| 549 | MsgInfo *procheader_parse_str(const gchar *str, MsgFlags flags, gboolean full) | ||
| 550 | { | ||
| 551 | FILE *fp; | ||
| 552 | MsgInfo *msginfo; | ||
| 553 | |||
| 554 | if ((fp = str_open_as_stream(str)) == NULL) | ||
| 555 | return NULL; | ||
| 556 | |||
| 557 | msginfo = procheader_parse_stream(fp, flags, full); | ||
| 558 | fclose(fp); | ||
| 559 | return msginfo; | ||
| 560 | } | ||
| 561 | |||
| 562 | enum | ||
| 563 | { | ||
| 564 | H_DATE = 0, | ||
| 565 | H_FROM = 1, | ||
| 566 | H_TO = 2, | ||
| 567 | H_NEWSGROUPS = 3, | ||
| 568 | H_SUBJECT = 4, | ||
| 569 | H_MSG_ID = 5, | ||
| 570 | H_REFERENCES = 6, | ||
| 571 | H_IN_REPLY_TO = 7, | ||
| 572 | H_CONTENT_TYPE = 8, | ||
| 573 | H_SEEN = 9, | ||
| 574 | H_CC = 10, | ||
| 575 | H_X_FACE = 11 | ||
| 576 | }; | ||
| 577 | |||
| 578 | MsgInfo *procheader_parse_stream(FILE *fp, MsgFlags flags, gboolean full) | ||
| 579 | { | ||
| 580 | static HeaderEntry hentry_full[] = {{"Date:", NULL, FALSE}, | ||
| 581 | {"From:", NULL, TRUE}, | ||
| 582 | {"To:", NULL, TRUE}, | ||
| 583 | {"Newsgroups:", NULL, TRUE}, | ||
| 584 | {"Subject:", NULL, TRUE}, | ||
| 585 | {"Message-Id:", NULL, FALSE}, | ||
| 586 | {"References:", NULL, FALSE}, | ||
| 587 | {"In-Reply-To:", NULL, FALSE}, | ||
| 588 | {"Content-Type:", NULL, FALSE}, | ||
| 589 | {"Seen:", NULL, FALSE}, | ||
| 590 | {"Cc:", NULL, TRUE}, | ||
| 591 | {"X-Face:", NULL, FALSE}, | ||
| 592 | {NULL, NULL, FALSE}}; | ||
| 593 | |||
| 594 | static HeaderEntry hentry_short[] = {{"Date:", NULL, FALSE}, | ||
| 595 | {"From:", NULL, TRUE}, | ||
| 596 | {"To:", NULL, TRUE}, | ||
| 597 | {"Newsgroups:", NULL, TRUE}, | ||
| 598 | {"Subject:", NULL, TRUE}, | ||
| 599 | {"Message-Id:", NULL, FALSE}, | ||
| 600 | {"References:", NULL, FALSE}, | ||
| 601 | {"In-Reply-To:", NULL, FALSE}, | ||
| 602 | {"Content-Type:", NULL, FALSE}, | ||
| 603 | {"Seen:", NULL, FALSE}, | ||
| 604 | {NULL, NULL, FALSE}}; | ||
| 605 | |||
| 606 | MsgInfo *msginfo; | ||
| 607 | gchar buf[BUFFSIZE]; | ||
| 608 | gchar *p; | ||
| 609 | gchar *hp; | ||
| 610 | HeaderEntry *hentry; | ||
| 611 | gint hnum; | ||
| 612 | gchar *from = NULL, *to = NULL, *subject = NULL, *cc = NULL; | ||
| 613 | gchar *charset = NULL; | ||
| 614 | |||
| 615 | hentry = full ? hentry_full : hentry_short; | ||
| 616 | |||
| 617 | if (MSG_IS_QUEUED(flags)) { | ||
| 618 | while (fgets(buf, sizeof(buf), fp) != NULL) | ||
| 619 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 620 | } | ||
| 621 | |||
| 622 | msginfo = g_new0(MsgInfo, 1); | ||
| 623 | msginfo->flags = flags; | ||
| 624 | msginfo->references = NULL; | ||
| 625 | msginfo->inreplyto = NULL; | ||
| 626 | |||
| 627 | while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) | ||
| 628 | != -1) { | ||
| 629 | hp = buf + strlen(hentry[hnum].name); | ||
| 630 | while (*hp == ' ' || *hp == '\t') hp++; | ||
| 631 | |||
| 632 | switch (hnum) { | ||
| 633 | case H_DATE: | ||
| 634 | if (msginfo->date) break; | ||
| 635 | msginfo->date_t = | ||
| 636 | procheader_date_parse(NULL, hp, 0); | ||
| 637 | msginfo->date = g_strdup(hp); | ||
| 638 | break; | ||
| 639 | case H_FROM: | ||
| 640 | if (from) break; | ||
| 641 | from = g_strdup(hp); | ||
| 642 | break; | ||
| 643 | case H_TO: | ||
| 644 | if (to) { | ||
| 645 | p = to; | ||
| 646 | to = g_strconcat(p, ", ", hp, NULL); | ||
| 647 | g_free(p); | ||
| 648 | } else | ||
| 649 | to = g_strdup(hp); | ||
| 650 | break; | ||
| 651 | case H_NEWSGROUPS: | ||
| 652 | if (msginfo->newsgroups) { | ||
| 653 | p = msginfo->newsgroups; | ||
| 654 | msginfo->newsgroups = | ||
| 655 | g_strconcat(p, ",", hp, NULL); | ||
| 656 | g_free(p); | ||
| 657 | } else | ||
| 658 | msginfo->newsgroups = g_strdup(buf + 12); | ||
| 659 | break; | ||
| 660 | case H_SUBJECT: | ||
| 661 | if (msginfo->subject) break; | ||
| 662 | subject = g_strdup(hp); | ||
| 663 | break; | ||
| 664 | case H_MSG_ID: | ||
| 665 | if (msginfo->msgid) break; | ||
| 666 | |||
| 667 | extract_parenthesis(hp, '<', '>'); | ||
| 668 | remove_space(hp); | ||
| 669 | msginfo->msgid = g_strdup(hp); | ||
| 670 | break; | ||
| 671 | case H_REFERENCES: | ||
| 672 | msginfo->references = | ||
| 673 | references_list_prepend(msginfo->references, | ||
| 674 | hp); | ||
| 675 | break; | ||
| 676 | case H_IN_REPLY_TO: | ||
| 677 | if (msginfo->inreplyto) break; | ||
| 678 | |||
| 679 | eliminate_parenthesis(hp, '(', ')'); | ||
| 680 | if ((p = strrchr(hp, '<')) != NULL && | ||
| 681 | strchr(p + 1, '>') != NULL) { | ||
| 682 | extract_parenthesis(p, '<', '>'); | ||
| 683 | remove_space(p); | ||
| 684 | if (*p != '\0') | ||
| 685 | msginfo->inreplyto = g_strdup(p); | ||
| 686 | } | ||
| 687 | break; | ||
| 688 | case H_CONTENT_TYPE: | ||
| 689 | if (!g_ascii_strncasecmp(hp, "multipart", 9)) { | ||
| 690 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME); | ||
| 691 | } else { | ||
| 692 | if (!g_ascii_strncasecmp(hp, "text/html", 9)) { | ||
| 693 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME_HTML); | ||
| 694 | } | ||
| 695 | if (!charset) { | ||
| 696 | procmime_scan_content_type_str | ||
| 697 | (hp, NULL, &charset, NULL, NULL); | ||
| 698 | } | ||
| 699 | } | ||
| 700 | break; | ||
| 701 | case H_SEEN: | ||
| 702 | /* mnews Seen header */ | ||
| 703 | MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW|MSG_UNREAD); | ||
| 704 | break; | ||
| 705 | case H_CC: | ||
| 706 | if (cc) { | ||
| 707 | p = cc; | ||
| 708 | cc = g_strconcat(p, ", ", hp, NULL); | ||
| 709 | g_free(p); | ||
| 710 | } else | ||
| 711 | cc = g_strdup(hp); | ||
| 712 | break; | ||
| 713 | case H_X_FACE: | ||
| 714 | if (msginfo->xface) break; | ||
| 715 | msginfo->xface = g_strdup(hp); | ||
| 716 | break; | ||
| 717 | default: | ||
| 718 | break; | ||
| 719 | } | ||
| 720 | } | ||
| 721 | |||
| 722 | if (from) { | ||
| 723 | msginfo->from = conv_unmime_header(from, charset); | ||
| 724 | subst_control(msginfo->from, ' '); | ||
| 725 | msginfo->fromname = procheader_get_fromname(msginfo->from); | ||
| 726 | g_free(from); | ||
| 727 | } | ||
| 728 | if (to) { | ||
| 729 | msginfo->to = conv_unmime_header(to, charset); | ||
| 730 | subst_control(msginfo->to, ' '); | ||
| 731 | g_free(to); | ||
| 732 | } | ||
| 733 | if (subject) { | ||
| 734 | msginfo->subject = conv_unmime_header(subject, charset); | ||
| 735 | subst_control(msginfo->subject, ' '); | ||
| 736 | g_free(subject); | ||
| 737 | } | ||
| 738 | if (cc) { | ||
| 739 | msginfo->cc = conv_unmime_header(cc, charset); | ||
| 740 | subst_control(msginfo->cc, ' '); | ||
| 741 | g_free(cc); | ||
| 742 | } | ||
| 743 | |||
| 744 | if (!msginfo->inreplyto && msginfo->references) | ||
| 745 | msginfo->inreplyto = | ||
| 746 | g_strdup((gchar *)msginfo->references->data); | ||
| 747 | |||
| 748 | if (MSG_IS_MIME(msginfo->flags)) { | ||
| 749 | MimeInfo *mimeinfo, *part; | ||
| 750 | gboolean has_html = FALSE; | ||
| 751 | |||
| 752 | part = mimeinfo = procmime_scan_message_stream(fp); | ||
| 753 | while (part) { | ||
| 754 | if (part->mime_type != MIME_TEXT && | ||
| 755 | part->mime_type != MIME_TEXT_HTML && | ||
| 756 | part->mime_type != MIME_MULTIPART) | ||
| 757 | break; | ||
| 758 | if (part->mime_type == MIME_TEXT_HTML) | ||
| 759 | has_html = TRUE; | ||
| 760 | part = procmime_mimeinfo_next(part); | ||
| 761 | } | ||
| 762 | |||
| 763 | if (has_html && !part) { | ||
| 764 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MIME_HTML); | ||
| 765 | } | ||
| 766 | |||
| 767 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 768 | } | ||
| 769 | |||
| 770 | g_free(charset); | ||
| 771 | |||
| 772 | return msginfo; | ||
| 773 | } | ||
| 774 | |||
| 775 | gchar *procheader_get_fromname(const gchar *str) | ||
| 776 | { | ||
| 777 | gchar *tmp, *name; | ||
| 778 | |||
| 779 | tmp = g_strdup(str); | ||
| 780 | |||
| 781 | if (*tmp == '\"') { | ||
| 782 | extract_quote_with_escape(tmp, '\"'); | ||
| 783 | g_strstrip(tmp); | ||
| 784 | } else if (strchr(tmp, '<')) { | ||
| 785 | eliminate_parenthesis(tmp, '<', '>'); | ||
| 786 | g_strstrip(tmp); | ||
| 787 | if (*tmp == '\0') { | ||
| 788 | strcpy(tmp, str); | ||
| 789 | extract_parenthesis(tmp, '<', '>'); | ||
| 790 | g_strstrip(tmp); | ||
| 791 | } | ||
| 792 | } else if (strchr(tmp, '(')) { | ||
| 793 | extract_parenthesis_with_escape(tmp, '(', ')'); | ||
| 794 | g_strstrip(tmp); | ||
| 795 | } | ||
| 796 | |||
| 797 | if (*tmp == '\0') { | ||
| 798 | g_free(tmp); | ||
| 799 | name = g_strdup(str); | ||
| 800 | } else | ||
| 801 | name = tmp; | ||
| 802 | |||
| 803 | return name; | ||
| 804 | } | ||
| 805 | |||
| 806 | gchar *procheader_get_toname(const gchar *str) | ||
| 807 | { | ||
| 808 | GSList *addr_list, *cur; | ||
| 809 | GString *toname; | ||
| 810 | gchar *name; | ||
| 811 | |||
| 812 | if (strchr(str, ',') == NULL) | ||
| 813 | return procheader_get_fromname(str); | ||
| 814 | |||
| 815 | addr_list = address_list_append_orig(NULL, str); | ||
| 816 | toname = g_string_new(NULL); | ||
| 817 | |||
| 818 | for (cur = addr_list; cur != NULL; cur = cur->next) { | ||
| 819 | name = procheader_get_fromname((gchar *)cur->data); | ||
| 820 | g_string_append(toname, name); | ||
| 821 | g_free(name); | ||
| 822 | if (cur->next) | ||
| 823 | g_string_append(toname, ", "); | ||
| 824 | } | ||
| 825 | |||
| 826 | slist_free_strings(addr_list); | ||
| 827 | |||
| 828 | return g_string_free(toname, FALSE); | ||
| 829 | } | ||
| 830 | |||
| 831 | static gint procheader_scan_date_string(const gchar *str, | ||
| 832 | gchar *weekday, gint *day, | ||
| 833 | gchar *month, gint *year, | ||
| 834 | gint *hh, gint *mm, gint *ss, | ||
| 835 | gchar *zone) | ||
| 836 | { | ||
| 837 | gint result; | ||
| 838 | |||
| 839 | *zone = '\0'; | ||
| 840 | result = sscanf(str, "%10s %d %9s %d %2d:%2d:%2d %5s", | ||
| 841 | weekday, day, month, year, hh, mm, ss, zone); | ||
| 842 | if (result >= 7) return 0; | ||
| 843 | |||
| 844 | result = sscanf(str, "%3s,%d %9s %d %2d:%2d:%2d %5s", | ||
| 845 | weekday, day, month, year, hh, mm, ss, zone); | ||
| 846 | if (result >= 7) return 0; | ||
| 847 | |||
| 848 | result = sscanf(str, "%3s,%d %9s %d %2d.%2d.%2d %5s", | ||
| 849 | weekday, day, month, year, hh, mm, ss, zone); | ||
| 850 | if (result >= 7) return 0; | ||
| 851 | |||
| 852 | result = sscanf(str, "%3s %d, %9s %d %2d:%2d:%2d %5s", | ||
| 853 | weekday, day, month, year, hh, mm, ss, zone); | ||
| 854 | if (result >= 7) return 0; | ||
| 855 | |||
| 856 | result = sscanf(str, "%d %9s %d %2d:%2d:%2d %5s", | ||
| 857 | day, month, year, hh, mm, ss, zone); | ||
| 858 | if (result >= 6) return 0; | ||
| 859 | |||
| 860 | result = sscanf(str, "%d-%2s-%2d %2d:%2d:%2d", | ||
| 861 | year, month, day, hh, mm, ss); | ||
| 862 | if (result == 6) return 0; | ||
| 863 | |||
| 864 | *ss = 0; | ||
| 865 | result = sscanf(str, "%10s %d %9s %d %2d:%2d %5s", | ||
| 866 | weekday, day, month, year, hh, mm, zone); | ||
| 867 | if (result >= 6) return 0; | ||
| 868 | |||
| 869 | result = sscanf(str, "%d %9s %d %2d:%2d %5s", | ||
| 870 | day, month, year, hh, mm, zone); | ||
| 871 | if (result >= 5) return 0; | ||
| 872 | |||
| 873 | return -1; | ||
| 874 | } | ||
| 875 | |||
| 876 | stime_t procheader_date_parse(gchar *dest, const gchar *src, gint len) | ||
| 877 | { | ||
| 878 | static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; | ||
| 879 | gchar weekday[11]; | ||
| 880 | gint day; | ||
| 881 | gchar month[10]; | ||
| 882 | gint year; | ||
| 883 | gint hh, mm, ss; | ||
| 884 | gchar zone[6]; | ||
| 885 | GDateMonth dmonth = G_DATE_BAD_MONTH; | ||
| 886 | struct tm t; | ||
| 887 | gchar *p; | ||
| 888 | time_t timer_; | ||
| 889 | time_t tz_offset; | ||
| 890 | stime_t timer; | ||
| 891 | |||
| 892 | if (procheader_scan_date_string(src, weekday, &day, month, &year, | ||
| 893 | &hh, &mm, &ss, zone) < 0) { | ||
| 894 | g_warning("procheader_scan_date_string: date parse failed: %s", src); | ||
| 895 | if (dest && len > 0) | ||
| 896 | strncpy2(dest, src, len); | ||
| 897 | return 0; | ||
| 898 | } | ||
| 899 | |||
| 900 | /* Y2K compliant :) */ | ||
| 901 | if (year < 1000) { | ||
| 902 | if (year < 50) | ||
| 903 | year += 2000; | ||
| 904 | else | ||
| 905 | year += 1900; | ||
| 906 | } | ||
| 907 | |||
| 908 | month[3] = '\0'; | ||
| 909 | if (g_ascii_isdigit(month[0])) { | ||
| 910 | dmonth = atoi(month); | ||
| 911 | } else { | ||
| 912 | for (p = monthstr; *p != '\0'; p += 3) { | ||
| 913 | if (!g_ascii_strncasecmp(p, month, 3)) { | ||
| 914 | dmonth = (gint)(p - monthstr) / 3 + 1; | ||
| 915 | break; | ||
| 916 | } | ||
| 917 | } | ||
| 918 | } | ||
| 919 | |||
| 920 | t.tm_sec = ss; | ||
| 921 | t.tm_min = mm; | ||
| 922 | t.tm_hour = hh; | ||
| 923 | t.tm_mday = day; | ||
| 924 | t.tm_mon = dmonth - 1; | ||
| 925 | t.tm_year = year - 1900; | ||
| 926 | t.tm_wday = 0; | ||
| 927 | t.tm_yday = 0; | ||
| 928 | t.tm_isdst = -1; | ||
| 929 | |||
| 930 | timer_ = mktime(&t); | ||
| 931 | if (timer_ == -1) { | ||
| 932 | if (year >= 2038) { | ||
| 933 | g_warning("mktime: date overflow: %s", src); | ||
| 934 | timer_ = G_MAXINT - 12 * 3600; | ||
| 935 | } else { | ||
| 936 | g_warning("mktime: can't convert date: %s", src); | ||
| 937 | if (dest) | ||
| 938 | dest[0] = '\0'; | ||
| 939 | return 0; | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | timer = timer_; | ||
| 944 | if (timer < G_MAXINT - 12 * 3600) { | ||
| 945 | tz_offset = remote_tzoffset_sec(zone); | ||
| 946 | if (tz_offset != -1) | ||
| 947 | timer += tzoffset_sec(&timer) - tz_offset; | ||
| 948 | } | ||
| 949 | |||
| 950 | if (dest) | ||
| 951 | procheader_date_get_localtime(dest, len, timer); | ||
| 952 | |||
| 953 | return timer; | ||
| 954 | } | ||
| 955 | |||
| 956 | void procheader_date_get_localtime(gchar *dest, gint len, const stime_t timer) | ||
| 957 | { | ||
| 958 | time_t timer_ = timer; | ||
| 959 | struct tm *lt; | ||
| 960 | gchar *default_format = "%y/%m/%d(%a) %H:%M"; | ||
| 961 | gchar *buf; | ||
| 962 | gchar tmp[BUFFSIZE]; | ||
| 963 | |||
| 964 | lt = localtime(&timer_); | ||
| 965 | if (!lt) { | ||
| 966 | g_warning("can't get localtime of %ld\n", timer); | ||
| 967 | dest[0] = '\0'; | ||
| 968 | return; | ||
| 969 | } | ||
| 970 | |||
| 971 | if (prefs_common.date_format) | ||
| 972 | strftime(tmp, sizeof(tmp), prefs_common.date_format, lt); | ||
| 973 | else | ||
| 974 | strftime(tmp, sizeof(tmp), default_format, lt); | ||
| 975 | |||
| 976 | buf = conv_localetodisp(tmp, NULL); | ||
| 977 | strncpy2(dest, buf, len); | ||
| 978 | g_free(buf); | ||
| 979 | } | ||
diff --git a/libsylph/procheader.h b/libsylph/procheader.h new file mode 100644 index 0000000..fa8095a --- /dev/null +++ b/libsylph/procheader.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2007 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PROCHEADER_H__ | ||
| 21 | #define __PROCHEADER_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <time.h> | ||
| 26 | |||
| 27 | #include "procmsg.h" | ||
| 28 | |||
| 29 | typedef struct _HeaderEntry HeaderEntry; | ||
| 30 | typedef struct _Header Header; | ||
| 31 | |||
| 32 | struct _HeaderEntry | ||
| 33 | { | ||
| 34 | gchar *name; | ||
| 35 | gchar *body; | ||
| 36 | gboolean unfold; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct _Header | ||
| 40 | { | ||
| 41 | gchar *name; | ||
| 42 | gchar *body; | ||
| 43 | }; | ||
| 44 | |||
| 45 | gint procheader_get_one_field (gchar *buf, | ||
| 46 | size_t len, | ||
| 47 | FILE *fp, | ||
| 48 | HeaderEntry hentry[]); | ||
| 49 | gchar *procheader_get_unfolded_line (gchar *buf, | ||
| 50 | size_t len, | ||
| 51 | FILE *fp); | ||
| 52 | |||
| 53 | GSList *procheader_get_header_list_from_file (const gchar *file); | ||
| 54 | GSList *procheader_get_header_list (FILE *fp); | ||
| 55 | GSList *procheader_get_header_list_from_msginfo (MsgInfo *msginfo); | ||
| 56 | GSList *procheader_add_header_list (GSList *hlist, | ||
| 57 | const gchar *header_name, | ||
| 58 | const gchar *body); | ||
| 59 | GSList *procheader_copy_header_list (GSList *hlist); | ||
| 60 | GSList *procheader_merge_header_list (GSList *hlist1, | ||
| 61 | GSList *hlist2); | ||
| 62 | GSList *procheader_merge_header_list_dup (GSList *hlist1, | ||
| 63 | GSList *hlist2); | ||
| 64 | gint procheader_find_header_list (GSList *hlist, | ||
| 65 | const gchar *header_name); | ||
| 66 | void procheader_header_list_destroy (GSList *hlist); | ||
| 67 | |||
| 68 | GPtrArray *procheader_get_header_array (FILE *fp, | ||
| 69 | const gchar *encoding); | ||
| 70 | GPtrArray *procheader_get_header_array_asis (FILE *fp, | ||
| 71 | const gchar *encoding); | ||
| 72 | GPtrArray *procheader_get_header_array_for_display | ||
| 73 | (FILE *fp, | ||
| 74 | const gchar *encoding); | ||
| 75 | void procheader_header_array_destroy (GPtrArray *harray); | ||
| 76 | |||
| 77 | void procheader_header_free (Header *header); | ||
| 78 | |||
| 79 | void procheader_get_header_fields (FILE *fp, | ||
| 80 | HeaderEntry hentry[]); | ||
| 81 | MsgInfo *procheader_parse_file (const gchar *file, | ||
| 82 | MsgFlags flags, | ||
| 83 | gboolean full); | ||
| 84 | MsgInfo *procheader_parse_str (const gchar *str, | ||
| 85 | MsgFlags flags, | ||
| 86 | gboolean full); | ||
| 87 | MsgInfo *procheader_parse_stream (FILE *fp, | ||
| 88 | MsgFlags flags, | ||
| 89 | gboolean full); | ||
| 90 | |||
| 91 | gchar *procheader_get_fromname (const gchar *str); | ||
| 92 | gchar *procheader_get_toname (const gchar *str); | ||
| 93 | |||
| 94 | stime_t procheader_date_parse (gchar *dest, | ||
| 95 | const gchar *src, | ||
| 96 | gint len); | ||
| 97 | void procheader_date_get_localtime (gchar *dest, | ||
| 98 | gint len, | ||
| 99 | const stime_t timer); | ||
| 100 | |||
| 101 | #endif /* __PROCHEADER_H__ */ | ||
diff --git a/libsylph/procmime.c b/libsylph/procmime.c new file mode 100644 index 0000000..4c3a90c --- /dev/null +++ b/libsylph/procmime.c | |||
| @@ -0,0 +1,1955 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <locale.h> | ||
| 31 | #include <ctype.h> | ||
| 32 | |||
| 33 | #include "procmime.h" | ||
| 34 | #include "procheader.h" | ||
| 35 | #include "base64.h" | ||
| 36 | #include "quoted-printable.h" | ||
| 37 | #include "uuencode.h" | ||
| 38 | #include "html.h" | ||
| 39 | #include "codeconv.h" | ||
| 40 | #include "utils.h" | ||
| 41 | #include "prefs_common.h" | ||
| 42 | |||
| 43 | #undef MIME_DEBUG | ||
| 44 | /* #define MIME_DEBUG */ | ||
| 45 | #ifdef MIME_DEBUG | ||
| 46 | # define mime_debug_print debug_print | ||
| 47 | #else | ||
| 48 | # define mime_debug_print 1 ? (void)0 : debug_print | ||
| 49 | #endif | ||
| 50 | |||
| 51 | #define MAX_MIME_LEVEL 64 | ||
| 52 | |||
| 53 | static GHashTable *procmime_get_mime_type_table (void); | ||
| 54 | static GList *procmime_get_mime_type_list (const gchar *file); | ||
| 55 | |||
| 56 | |||
| 57 | MimeInfo *procmime_mimeinfo_new(void) | ||
| 58 | { | ||
| 59 | MimeInfo *mimeinfo; | ||
| 60 | |||
| 61 | mimeinfo = g_new0(MimeInfo, 1); | ||
| 62 | mimeinfo->mime_type = MIME_UNKNOWN; | ||
| 63 | mimeinfo->encoding_type = ENC_UNKNOWN; | ||
| 64 | |||
| 65 | return mimeinfo; | ||
| 66 | } | ||
| 67 | |||
| 68 | void procmime_mimeinfo_free_all(MimeInfo *mimeinfo) | ||
| 69 | { | ||
| 70 | while (mimeinfo != NULL) { | ||
| 71 | MimeInfo *next; | ||
| 72 | |||
| 73 | g_free(mimeinfo->encoding); | ||
| 74 | g_free(mimeinfo->content_type); | ||
| 75 | g_free(mimeinfo->charset); | ||
| 76 | g_free(mimeinfo->name); | ||
| 77 | g_free(mimeinfo->boundary); | ||
| 78 | g_free(mimeinfo->content_disposition); | ||
| 79 | g_free(mimeinfo->filename); | ||
| 80 | |||
| 81 | g_free(mimeinfo->sigstatus); | ||
| 82 | g_free(mimeinfo->sigstatus_full); | ||
| 83 | |||
| 84 | procmime_mimeinfo_free_all(mimeinfo->sub); | ||
| 85 | procmime_mimeinfo_free_all(mimeinfo->children); | ||
| 86 | procmime_mimeinfo_free_all(mimeinfo->plaintext); | ||
| 87 | |||
| 88 | next = mimeinfo->next; | ||
| 89 | g_free(mimeinfo); | ||
| 90 | mimeinfo = next; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | MimeInfo *procmime_mimeinfo_insert(MimeInfo *parent, MimeInfo *mimeinfo) | ||
| 95 | { | ||
| 96 | MimeInfo *child = parent->children; | ||
| 97 | |||
| 98 | if (!child) | ||
| 99 | parent->children = mimeinfo; | ||
| 100 | else { | ||
| 101 | while (child->next != NULL) | ||
| 102 | child = child->next; | ||
| 103 | |||
| 104 | child->next = mimeinfo; | ||
| 105 | } | ||
| 106 | |||
| 107 | mimeinfo->parent = parent; | ||
| 108 | mimeinfo->level = parent->level + 1; | ||
| 109 | |||
| 110 | return mimeinfo; | ||
| 111 | } | ||
| 112 | |||
| 113 | #if 0 | ||
| 114 | void procmime_mimeinfo_replace(MimeInfo *old, MimeInfo *new) | ||
| 115 | { | ||
| 116 | MimeInfo *parent = old->parent; | ||
| 117 | MimeInfo *child; | ||
| 118 | |||
| 119 | g_return_if_fail(parent != NULL); | ||
| 120 | g_return_if_fail(new->next == NULL); | ||
| 121 | |||
| 122 | for (child = parent->children; child && child != old; | ||
| 123 | child = child->next) | ||
| 124 | ; | ||
| 125 | if (!child) { | ||
| 126 | g_warning("oops: parent can't find it's own child"); | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | procmime_mimeinfo_free_all(old); | ||
| 130 | |||
| 131 | if (child == parent->children) { | ||
| 132 | new->next = parent->children->next; | ||
| 133 | parent->children = new; | ||
| 134 | } else { | ||
| 135 | new->next = child->next; | ||
| 136 | child = new; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | #endif | ||
| 140 | |||
| 141 | MimeInfo *procmime_mimeinfo_next(MimeInfo *mimeinfo) | ||
| 142 | { | ||
| 143 | if (!mimeinfo) return NULL; | ||
| 144 | |||
| 145 | if (mimeinfo->children) | ||
| 146 | return mimeinfo->children; | ||
| 147 | if (mimeinfo->sub) | ||
| 148 | return mimeinfo->sub; | ||
| 149 | if (mimeinfo->next) | ||
| 150 | return mimeinfo->next; | ||
| 151 | |||
| 152 | if (mimeinfo->main) { | ||
| 153 | mimeinfo = mimeinfo->main; | ||
| 154 | if (mimeinfo->next) | ||
| 155 | return mimeinfo->next; | ||
| 156 | } | ||
| 157 | |||
| 158 | for (mimeinfo = mimeinfo->parent; mimeinfo != NULL; | ||
| 159 | mimeinfo = mimeinfo->parent) { | ||
| 160 | if (mimeinfo->next) | ||
| 161 | return mimeinfo->next; | ||
| 162 | if (mimeinfo->main) { | ||
| 163 | mimeinfo = mimeinfo->main; | ||
| 164 | if (mimeinfo->next) | ||
| 165 | return mimeinfo->next; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | return NULL; | ||
| 170 | } | ||
| 171 | |||
| 172 | #if 0 | ||
| 173 | void procmime_dump_mimeinfo(MimeInfo *mimeinfo) | ||
| 174 | { | ||
| 175 | gint i; | ||
| 176 | |||
| 177 | g_print("\n"); | ||
| 178 | |||
| 179 | for (; mimeinfo != NULL; mimeinfo = procmime_mimeinfo_next(mimeinfo)) { | ||
| 180 | for (i = 0; i < mimeinfo->level; i++) | ||
| 181 | g_print(" "); | ||
| 182 | g_print("%s%s\n", mimeinfo->main ? "sub: " : "", | ||
| 183 | mimeinfo->content_type); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | #endif | ||
| 187 | |||
| 188 | MimeInfo *procmime_scan_message(MsgInfo *msginfo) | ||
| 189 | { | ||
| 190 | FILE *fp; | ||
| 191 | MimeInfo *mimeinfo; | ||
| 192 | |||
| 193 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 194 | |||
| 195 | if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL) | ||
| 196 | return NULL; | ||
| 197 | |||
| 198 | if (mimeinfo) { | ||
| 199 | mimeinfo->size = msginfo->size; | ||
| 200 | mimeinfo->content_size = get_left_file_size(fp); | ||
| 201 | if (mimeinfo->encoding_type == ENC_BASE64) | ||
| 202 | mimeinfo->content_size = mimeinfo->content_size / 4 * 3; | ||
| 203 | if (mimeinfo->mime_type == MIME_MULTIPART || | ||
| 204 | mimeinfo->mime_type == MIME_MESSAGE_RFC822) | ||
| 205 | procmime_scan_multipart_message(mimeinfo, fp); | ||
| 206 | } | ||
| 207 | |||
| 208 | fclose(fp); | ||
| 209 | |||
| 210 | return mimeinfo; | ||
| 211 | } | ||
| 212 | |||
| 213 | MimeInfo *procmime_scan_message_stream(FILE *fp) | ||
| 214 | { | ||
| 215 | MimeInfo *mimeinfo; | ||
| 216 | glong fpos; | ||
| 217 | |||
| 218 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 219 | |||
| 220 | if (fseek(fp, 0L, SEEK_SET) < 0) { | ||
| 221 | FILE_OP_ERROR("procmime_scan_message_stream()", "fseek"); | ||
| 222 | return NULL; | ||
| 223 | } | ||
| 224 | |||
| 225 | mimeinfo = procmime_scan_mime_header(fp); | ||
| 226 | |||
| 227 | if (mimeinfo) { | ||
| 228 | fpos = ftell(fp); | ||
| 229 | mimeinfo->content_size = get_left_file_size(fp); | ||
| 230 | mimeinfo->size = fpos + mimeinfo->content_size; | ||
| 231 | if (mimeinfo->encoding_type == ENC_BASE64) | ||
| 232 | mimeinfo->content_size = mimeinfo->content_size / 4 * 3; | ||
| 233 | if (mimeinfo->mime_type == MIME_MULTIPART || | ||
| 234 | mimeinfo->mime_type == MIME_MESSAGE_RFC822) | ||
| 235 | procmime_scan_multipart_message(mimeinfo, fp); | ||
| 236 | } | ||
| 237 | |||
| 238 | return mimeinfo; | ||
| 239 | } | ||
| 240 | |||
| 241 | void procmime_scan_multipart_message(MimeInfo *mimeinfo, FILE *fp) | ||
| 242 | { | ||
| 243 | gchar *p; | ||
| 244 | gchar *boundary; | ||
| 245 | gint boundary_len = 0; | ||
| 246 | gchar *buf; | ||
| 247 | glong fpos, prev_fpos; | ||
| 248 | |||
| 249 | g_return_if_fail(mimeinfo != NULL); | ||
| 250 | g_return_if_fail(mimeinfo->mime_type == MIME_MULTIPART || | ||
| 251 | mimeinfo->mime_type == MIME_MESSAGE_RFC822); | ||
| 252 | |||
| 253 | if (mimeinfo->mime_type == MIME_MULTIPART) { | ||
| 254 | g_return_if_fail(mimeinfo->boundary != NULL); | ||
| 255 | g_return_if_fail(mimeinfo->sub == NULL); | ||
| 256 | } | ||
| 257 | g_return_if_fail(fp != NULL); | ||
| 258 | |||
| 259 | buf = g_malloc(BUFFSIZE); | ||
| 260 | |||
| 261 | boundary = mimeinfo->boundary; | ||
| 262 | |||
| 263 | if (boundary) { | ||
| 264 | boundary_len = strlen(boundary); | ||
| 265 | |||
| 266 | /* look for first boundary */ | ||
| 267 | while ((p = fgets(buf, BUFFSIZE, fp)) != NULL) | ||
| 268 | if (IS_BOUNDARY(buf, boundary, boundary_len)) break; | ||
| 269 | if (!p) { | ||
| 270 | g_free(buf); | ||
| 271 | return; | ||
| 272 | } | ||
| 273 | } else if (mimeinfo->parent && mimeinfo->parent->boundary) { | ||
| 274 | boundary = mimeinfo->parent->boundary; | ||
| 275 | boundary_len = strlen(boundary); | ||
| 276 | } | ||
| 277 | |||
| 278 | if ((fpos = ftell(fp)) < 0) { | ||
| 279 | perror("ftell"); | ||
| 280 | g_free(buf); | ||
| 281 | return; | ||
| 282 | } | ||
| 283 | |||
| 284 | mime_debug_print("==== enter part\n"); | ||
| 285 | mime_debug_print("level = %d\n", mimeinfo->level); | ||
| 286 | |||
| 287 | for (;;) { | ||
| 288 | MimeInfo *partinfo; | ||
| 289 | gboolean eom = FALSE; | ||
| 290 | glong content_pos; | ||
| 291 | gboolean is_base64; | ||
| 292 | gint len; | ||
| 293 | guint b64_content_len = 0; | ||
| 294 | gint b64_pad_len = 0; | ||
| 295 | |||
| 296 | prev_fpos = fpos; | ||
| 297 | mime_debug_print("prev_fpos: %ld\n", fpos); | ||
| 298 | |||
| 299 | /* scan part header */ | ||
| 300 | if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) { | ||
| 301 | MimeInfo *sub; | ||
| 302 | |||
| 303 | mimeinfo->sub = sub = procmime_scan_mime_header(fp); | ||
| 304 | if (!sub) break; | ||
| 305 | |||
| 306 | mime_debug_print("message/rfc822 part (content-type: %s)\n", | ||
| 307 | sub->content_type); | ||
| 308 | sub->level = mimeinfo->level + 1; | ||
| 309 | sub->parent = mimeinfo->parent; | ||
| 310 | sub->main = mimeinfo; | ||
| 311 | |||
| 312 | partinfo = sub; | ||
| 313 | } else { | ||
| 314 | partinfo = procmime_scan_mime_header(fp); | ||
| 315 | if (!partinfo) break; | ||
| 316 | procmime_mimeinfo_insert(mimeinfo, partinfo); | ||
| 317 | mime_debug_print("content-type: %s\n", | ||
| 318 | partinfo->content_type); | ||
| 319 | if (partinfo->filename) | ||
| 320 | mime_debug_print("filename: %s\n", partinfo->filename); | ||
| 321 | else if (partinfo->name) | ||
| 322 | mime_debug_print("name: %s\n", partinfo->name); | ||
| 323 | } | ||
| 324 | |||
| 325 | /* begin content */ | ||
| 326 | content_pos = ftell(fp); | ||
| 327 | mime_debug_print("content_pos: %ld\n", content_pos); | ||
| 328 | |||
| 329 | if (partinfo->mime_type == MIME_MULTIPART || | ||
| 330 | partinfo->mime_type == MIME_MESSAGE_RFC822) { | ||
| 331 | if (partinfo->level < MAX_MIME_LEVEL) { | ||
| 332 | mime_debug_print("\n"); | ||
| 333 | mime_debug_print("enter to child part:\n"); | ||
| 334 | procmime_scan_multipart_message(partinfo, fp); | ||
| 335 | } | ||
| 336 | } | ||
| 337 | |||
| 338 | /* look for next boundary */ | ||
| 339 | buf[0] = '\0'; | ||
| 340 | is_base64 = partinfo->encoding_type == ENC_BASE64; | ||
| 341 | while ((p = fgets(buf, BUFFSIZE, fp)) != NULL) { | ||
| 342 | if (IS_BOUNDARY(buf, boundary, boundary_len)) { | ||
| 343 | if (buf[2 + boundary_len] == '-' && | ||
| 344 | buf[2 + boundary_len + 1] == '-') | ||
| 345 | eom = TRUE; | ||
| 346 | break; | ||
| 347 | } else if (is_base64) { | ||
| 348 | const gchar *s; | ||
| 349 | for (s = buf; *s && *s != '\r' && *s != '\n'; | ||
| 350 | ++s) | ||
| 351 | if (*s == '=') | ||
| 352 | ++b64_pad_len; | ||
| 353 | b64_content_len += s - buf; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | if (p == NULL) { | ||
| 357 | /* broken MIME, or single part MIME message */ | ||
| 358 | buf[0] = '\0'; | ||
| 359 | eom = TRUE; | ||
| 360 | } | ||
| 361 | mime_debug_print("boundary: %s\n", buf); | ||
| 362 | |||
| 363 | fpos = ftell(fp); | ||
| 364 | mime_debug_print("fpos: %ld\n", fpos); | ||
| 365 | |||
| 366 | len = strlen(buf); | ||
| 367 | partinfo->size = fpos - prev_fpos - len; | ||
| 368 | if (is_base64) | ||
| 369 | partinfo->content_size = | ||
| 370 | b64_content_len / 4 * 3 - b64_pad_len; | ||
| 371 | else | ||
| 372 | partinfo->content_size = fpos - content_pos - len; | ||
| 373 | mime_debug_print("partinfo->size: %d\n", partinfo->size); | ||
| 374 | mime_debug_print("partinfo->content_size: %d\n", | ||
| 375 | partinfo->content_size); | ||
| 376 | if (partinfo->sub && !partinfo->sub->sub && | ||
| 377 | !partinfo->sub->children) { | ||
| 378 | partinfo->sub->size = | ||
| 379 | fpos - partinfo->sub->fpos - strlen(buf); | ||
| 380 | mime_debug_print("partinfo->sub->size: %d\n", | ||
| 381 | partinfo->sub->size); | ||
| 382 | } | ||
| 383 | |||
| 384 | if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) { | ||
| 385 | if (len > 0 && fseek(fp, fpos - len, SEEK_SET) < 0) | ||
| 386 | perror("fseek"); | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | |||
| 390 | if (eom) break; | ||
| 391 | } | ||
| 392 | |||
| 393 | g_free(buf); | ||
| 394 | mime_debug_print("==== leave part\n"); | ||
| 395 | } | ||
| 396 | |||
| 397 | void procmime_scan_encoding(MimeInfo *mimeinfo, const gchar *encoding) | ||
| 398 | { | ||
| 399 | gchar *enc; | ||
| 400 | |||
| 401 | g_free(mimeinfo->encoding); | ||
| 402 | enc = mimeinfo->encoding = g_strstrip(g_strdup(encoding)); | ||
| 403 | |||
| 404 | if (!g_ascii_strncasecmp(enc, "7bit", 4)) | ||
| 405 | mimeinfo->encoding_type = ENC_7BIT; | ||
| 406 | else if (!g_ascii_strncasecmp(enc, "8bit", 4)) | ||
| 407 | mimeinfo->encoding_type = ENC_8BIT; | ||
| 408 | else if (!g_ascii_strncasecmp(enc, "quoted-printable", 16)) | ||
| 409 | mimeinfo->encoding_type = ENC_QUOTED_PRINTABLE; | ||
| 410 | else if (!g_ascii_strncasecmp(enc, "base64", 6)) | ||
| 411 | mimeinfo->encoding_type = ENC_BASE64; | ||
| 412 | else if (!g_ascii_strncasecmp(enc, "x-uuencode", 10)) | ||
| 413 | mimeinfo->encoding_type = ENC_X_UUENCODE; | ||
| 414 | else | ||
| 415 | mimeinfo->encoding_type = ENC_UNKNOWN; | ||
| 416 | |||
| 417 | } | ||
| 418 | |||
| 419 | void procmime_scan_content_type(MimeInfo *mimeinfo, const gchar *content_type) | ||
| 420 | { | ||
| 421 | g_free(mimeinfo->content_type); | ||
| 422 | g_free(mimeinfo->charset); | ||
| 423 | g_free(mimeinfo->name); | ||
| 424 | g_free(mimeinfo->boundary); | ||
| 425 | mimeinfo->content_type = NULL; | ||
| 426 | mimeinfo->charset = NULL; | ||
| 427 | mimeinfo->name = NULL; | ||
| 428 | mimeinfo->boundary = NULL; | ||
| 429 | |||
| 430 | procmime_scan_content_type_str(content_type, &mimeinfo->content_type, | ||
| 431 | &mimeinfo->charset, &mimeinfo->name, | ||
| 432 | &mimeinfo->boundary); | ||
| 433 | |||
| 434 | mimeinfo->mime_type = procmime_scan_mime_type(mimeinfo->content_type); | ||
| 435 | if (mimeinfo->mime_type == MIME_MULTIPART && !mimeinfo->boundary) | ||
| 436 | mimeinfo->mime_type = MIME_TEXT; | ||
| 437 | } | ||
| 438 | |||
| 439 | typedef struct | ||
| 440 | { | ||
| 441 | gchar *name; | ||
| 442 | gchar *value; | ||
| 443 | } MimeParam; | ||
| 444 | |||
| 445 | typedef struct | ||
| 446 | { | ||
| 447 | gchar *hvalue; | ||
| 448 | GSList *plist; | ||
| 449 | } MimeParams; | ||
| 450 | |||
| 451 | static gchar *procmime_find_parameter_delimiter(const gchar *param, | ||
| 452 | const gchar **eq) | ||
| 453 | { | ||
| 454 | register const gchar *p = param; | ||
| 455 | gboolean quoted = FALSE; | ||
| 456 | const gchar *delim = NULL; | ||
| 457 | |||
| 458 | while (*p) { | ||
| 459 | if (*p == '=') | ||
| 460 | break; | ||
| 461 | else if (*p == ';' || *p == '\r' || *p == '\n') { | ||
| 462 | delim = p; | ||
| 463 | break; | ||
| 464 | } | ||
| 465 | ++p; | ||
| 466 | } | ||
| 467 | if (*p != '=') { | ||
| 468 | *eq = NULL; | ||
| 469 | return (gchar *)delim; | ||
| 470 | } | ||
| 471 | *eq = p; | ||
| 472 | |||
| 473 | ++p; | ||
| 474 | while (g_ascii_isspace(*p)) | ||
| 475 | ++p; | ||
| 476 | if (*p == '"') { | ||
| 477 | quoted = TRUE; | ||
| 478 | ++p; | ||
| 479 | } | ||
| 480 | |||
| 481 | while (*p) { | ||
| 482 | if (quoted == TRUE) { | ||
| 483 | if (*p == '"') | ||
| 484 | quoted = FALSE; | ||
| 485 | } else if (*p == ';' || *p == '\r' || *p == '\n') { | ||
| 486 | delim = p; | ||
| 487 | break; | ||
| 488 | } | ||
| 489 | ++p; | ||
| 490 | } | ||
| 491 | |||
| 492 | return (gchar *)delim; | ||
| 493 | } | ||
| 494 | |||
| 495 | static gchar *procmime_convert_value(const gchar *value, const gchar *charset) | ||
| 496 | { | ||
| 497 | if (charset) { | ||
| 498 | gchar *utf8_value; | ||
| 499 | |||
| 500 | utf8_value = conv_codeset_strdup(value, charset, CS_INTERNAL); | ||
| 501 | if (utf8_value) | ||
| 502 | return utf8_value; | ||
| 503 | } | ||
| 504 | |||
| 505 | return g_strdup(value); | ||
| 506 | } | ||
| 507 | |||
| 508 | static MimeParams *procmime_parse_mime_parameter(const gchar *str) | ||
| 509 | { | ||
| 510 | ConvADType ad_type; | ||
| 511 | gchar *tmp_param = NULL; | ||
| 512 | gchar *hvalue; | ||
| 513 | gchar *param, *name, *value; | ||
| 514 | gchar *charset = NULL, *lang = NULL; | ||
| 515 | const gchar *p, *delim; | ||
| 516 | gint count, prev_count; | ||
| 517 | gchar *cont_name; | ||
| 518 | gchar *cont_value; | ||
| 519 | MimeParam *mparam; | ||
| 520 | MimeParams *mparams; | ||
| 521 | GSList *plist = NULL; | ||
| 522 | |||
| 523 | if ((p = strchr(str, ';'))) | ||
| 524 | hvalue = g_strndup(str, p - str); | ||
| 525 | else | ||
| 526 | hvalue = g_strdup(str); | ||
| 527 | |||
| 528 | g_strstrip(hvalue); | ||
| 529 | |||
| 530 | mparams = g_new(MimeParams, 1); | ||
| 531 | mparams->hvalue = hvalue; | ||
| 532 | mparams->plist = NULL; | ||
| 533 | |||
| 534 | if (!p) | ||
| 535 | return mparams; | ||
| 536 | ++p; | ||
| 537 | |||
| 538 | /* workaround for raw-JIS filename (Eudora etc.) */ | ||
| 539 | ad_type = conv_get_autodetect_type(); | ||
| 540 | if ((ad_type == C_AD_JAPANESE || | ||
| 541 | (ad_type == C_AD_BY_LOCALE && conv_is_ja_locale())) && | ||
| 542 | strstr(p, "\033$") != NULL) { | ||
| 543 | CodeConvFunc conv_func; | ||
| 544 | conv_func = conv_get_code_conv_func(NULL, NULL); | ||
| 545 | tmp_param = conv_func(p, NULL); | ||
| 546 | p = tmp_param; | ||
| 547 | debug_print("procmime_parse_mime_parameter(): raw-JIS header body detected: %s\n", str); | ||
| 548 | } | ||
| 549 | |||
| 550 | count = prev_count = -1; | ||
| 551 | cont_name = cont_value = NULL; | ||
| 552 | |||
| 553 | for (;;) { | ||
| 554 | gboolean encoded = FALSE; | ||
| 555 | gchar *begin; | ||
| 556 | gchar *dec_value; | ||
| 557 | const gchar *eq; | ||
| 558 | gchar *ast = NULL; | ||
| 559 | |||
| 560 | while (*p == ';' || g_ascii_isspace(*p)) | ||
| 561 | ++p; | ||
| 562 | if (*p == '\0') | ||
| 563 | break; | ||
| 564 | |||
| 565 | delim = procmime_find_parameter_delimiter(p, &eq); | ||
| 566 | if (!eq) | ||
| 567 | break; | ||
| 568 | if (delim) | ||
| 569 | param = g_strndup(p, delim - p); | ||
| 570 | else | ||
| 571 | param = g_strdup(p); | ||
| 572 | |||
| 573 | name = g_strndup(p, eq - p); | ||
| 574 | g_strchomp(name); | ||
| 575 | if (*name != '*' && (ast = strchr(name, '*'))) { | ||
| 576 | const gchar *next = ast + 1; | ||
| 577 | |||
| 578 | if (*next == '\0') { | ||
| 579 | encoded = TRUE; | ||
| 580 | } else if (g_ascii_isdigit(*next)) { | ||
| 581 | count = atoi(next); | ||
| 582 | while (g_ascii_isdigit(*next)) | ||
| 583 | ++next; | ||
| 584 | if (*next == '*') | ||
| 585 | encoded = TRUE; | ||
| 586 | if (prev_count + 1 != count) { | ||
| 587 | g_warning("procmime_parse_mime_parameter(): invalid count: %s\n", str); | ||
| 588 | g_free(name); | ||
| 589 | g_free(param); | ||
| 590 | break; | ||
| 591 | } | ||
| 592 | } else { | ||
| 593 | g_warning("procmime_parse_mime_parameter(): invalid name: %s\n", str); | ||
| 594 | g_free(name); | ||
| 595 | g_free(param); | ||
| 596 | break; | ||
| 597 | } | ||
| 598 | |||
| 599 | *ast = '\0'; | ||
| 600 | } | ||
| 601 | |||
| 602 | value = g_strdup(param + (eq - p) + 1); | ||
| 603 | g_strstrip(value); | ||
| 604 | if (*value == '"') | ||
| 605 | extract_quote(value, '"'); | ||
| 606 | |||
| 607 | begin = value; | ||
| 608 | |||
| 609 | if (encoded) { | ||
| 610 | gchar *sq1, *sq2; | ||
| 611 | |||
| 612 | if ((sq1 = strchr(value, '\''))) { | ||
| 613 | if (sq1 > value) { | ||
| 614 | if (charset) | ||
| 615 | g_free(charset); | ||
| 616 | charset = g_strndup(value, sq1 - value); | ||
| 617 | } | ||
| 618 | if ((sq2 = strchr(sq1 + 1, '\''))) { | ||
| 619 | if (sq2 > sq1 + 1) { | ||
| 620 | if (lang) | ||
| 621 | g_free(lang); | ||
| 622 | lang = g_strndup(sq1 + 1, | ||
| 623 | sq2 - sq1 - 1); | ||
| 624 | } | ||
| 625 | begin = sq2 + 1; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | #define CONCAT_CONT_VALUE(s) \ | ||
| 631 | { \ | ||
| 632 | if (cont_value) { \ | ||
| 633 | gchar *tmp; \ | ||
| 634 | tmp = g_strconcat(cont_value, s, NULL); \ | ||
| 635 | g_free(cont_value); \ | ||
| 636 | cont_value = tmp; \ | ||
| 637 | } else \ | ||
| 638 | cont_value = g_strdup(s); \ | ||
| 639 | } | ||
| 640 | |||
| 641 | if (count >= 0) { | ||
| 642 | if (count > 0 && cont_name) { | ||
| 643 | if (strcmp(cont_name, name) != 0) { | ||
| 644 | g_warning("procmime_parse_mime_parameter(): mismatch parameter name: %s\n", str); | ||
| 645 | g_free(name); | ||
| 646 | g_free(value); | ||
| 647 | g_free(param); | ||
| 648 | break; | ||
| 649 | } | ||
| 650 | } else | ||
| 651 | cont_name = g_strdup(name); | ||
| 652 | |||
| 653 | if (encoded) { | ||
| 654 | dec_value = g_malloc(strlen(begin) + 1); | ||
| 655 | decode_xdigit_encoded_str(dec_value, begin); | ||
| 656 | CONCAT_CONT_VALUE(dec_value); | ||
| 657 | g_free(dec_value); | ||
| 658 | } else { | ||
| 659 | CONCAT_CONT_VALUE(begin); | ||
| 660 | } | ||
| 661 | } | ||
| 662 | |||
| 663 | #undef CONCAT_CONT_VALUE | ||
| 664 | |||
| 665 | if (count == -1 && cont_name && cont_value) { | ||
| 666 | mparam = g_new(MimeParam, 1); | ||
| 667 | mparam->name = cont_name; | ||
| 668 | cont_name = NULL; | ||
| 669 | mparam->value = procmime_convert_value | ||
| 670 | (cont_value, charset); | ||
| 671 | g_free(cont_value); | ||
| 672 | cont_value = NULL; | ||
| 673 | plist = g_slist_prepend(plist, mparam); | ||
| 674 | } | ||
| 675 | |||
| 676 | if (count == -1) { | ||
| 677 | mparam = g_new(MimeParam, 1); | ||
| 678 | mparam->name = name; | ||
| 679 | if (encoded) { | ||
| 680 | dec_value = g_malloc(strlen(begin) + 1); | ||
| 681 | decode_xdigit_encoded_str(dec_value, begin); | ||
| 682 | mparam->value = procmime_convert_value | ||
| 683 | (dec_value, charset); | ||
| 684 | g_free(dec_value); | ||
| 685 | } else { | ||
| 686 | if (!ast && | ||
| 687 | (!g_ascii_strcasecmp(name, "name") || | ||
| 688 | !g_ascii_strcasecmp(name, "filename"))) | ||
| 689 | mparam->value = | ||
| 690 | conv_unmime_header(begin, NULL); | ||
| 691 | else | ||
| 692 | mparam->value = g_strdup(begin); | ||
| 693 | } | ||
| 694 | name = NULL; | ||
| 695 | plist = g_slist_prepend(plist, mparam); | ||
| 696 | } | ||
| 697 | |||
| 698 | g_free(name); | ||
| 699 | g_free(value); | ||
| 700 | g_free(param); | ||
| 701 | |||
| 702 | prev_count = count; | ||
| 703 | count = -1; | ||
| 704 | |||
| 705 | if (delim) | ||
| 706 | p = delim + 1; | ||
| 707 | else | ||
| 708 | break; | ||
| 709 | } | ||
| 710 | |||
| 711 | if (cont_name && cont_value) { | ||
| 712 | mparam = g_new(MimeParam, 1); | ||
| 713 | mparam->name = cont_name; | ||
| 714 | cont_name = NULL; | ||
| 715 | mparam->value = procmime_convert_value(cont_value, charset); | ||
| 716 | plist = g_slist_prepend(plist, mparam); | ||
| 717 | } | ||
| 718 | |||
| 719 | g_free(cont_name); | ||
| 720 | g_free(cont_value); | ||
| 721 | g_free(lang); | ||
| 722 | g_free(charset); | ||
| 723 | if (tmp_param) | ||
| 724 | g_free(tmp_param); | ||
| 725 | |||
| 726 | plist = g_slist_reverse(plist); | ||
| 727 | mparams->plist = plist; | ||
| 728 | |||
| 729 | return mparams; | ||
| 730 | } | ||
| 731 | |||
| 732 | static void procmime_mime_params_free(MimeParams *mparams) | ||
| 733 | { | ||
| 734 | GSList *cur; | ||
| 735 | |||
| 736 | if (!mparams) | ||
| 737 | return; | ||
| 738 | |||
| 739 | g_free(mparams->hvalue); | ||
| 740 | for (cur = mparams->plist; cur != NULL; cur = cur->next) { | ||
| 741 | MimeParam *mparam = (MimeParam *)cur->data; | ||
| 742 | g_free(mparam->name); | ||
| 743 | g_free(mparam->value); | ||
| 744 | g_free(mparam); | ||
| 745 | } | ||
| 746 | g_slist_free(mparams->plist); | ||
| 747 | g_free(mparams); | ||
| 748 | } | ||
| 749 | |||
| 750 | void procmime_scan_content_type_str(const gchar *content_type, | ||
| 751 | gchar **mime_type, gchar **charset, | ||
| 752 | gchar **name, gchar **boundary) | ||
| 753 | { | ||
| 754 | MimeParams *mparams; | ||
| 755 | GSList *cur; | ||
| 756 | |||
| 757 | mparams = procmime_parse_mime_parameter(content_type); | ||
| 758 | |||
| 759 | if (mime_type) | ||
| 760 | *mime_type = g_strdup(mparams->hvalue); | ||
| 761 | |||
| 762 | for (cur = mparams->plist; cur != NULL; cur = cur->next) { | ||
| 763 | MimeParam *param = (MimeParam *)cur->data; | ||
| 764 | if (charset && !g_ascii_strcasecmp(param->name, "charset")) { | ||
| 765 | *charset = g_strdup(param->value); | ||
| 766 | eliminate_parenthesis(*charset, '(', ')'); | ||
| 767 | g_strstrip(*charset); | ||
| 768 | charset = NULL; | ||
| 769 | } else if (name && !g_ascii_strcasecmp(param->name, "name")) { | ||
| 770 | *name = g_strdup(param->value); | ||
| 771 | name = NULL; | ||
| 772 | } else if (boundary && | ||
| 773 | !g_ascii_strcasecmp(param->name, "boundary")) { | ||
| 774 | *boundary = g_strdup(param->value); | ||
| 775 | boundary = NULL; | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | procmime_mime_params_free(mparams); | ||
| 780 | } | ||
| 781 | |||
| 782 | void procmime_scan_content_type_partial(const gchar *content_type, | ||
| 783 | gint *total, gchar **part_id, | ||
| 784 | gint *number) | ||
| 785 | { | ||
| 786 | MimeParams *mparams; | ||
| 787 | GSList *cur; | ||
| 788 | gchar *id_str = NULL; | ||
| 789 | gint t = 0, n = 0; | ||
| 790 | |||
| 791 | *total = 0; | ||
| 792 | *part_id = NULL; | ||
| 793 | *number = 0; | ||
| 794 | |||
| 795 | mparams = procmime_parse_mime_parameter(content_type); | ||
| 796 | |||
| 797 | if (!mparams->hvalue || | ||
| 798 | g_ascii_strcasecmp(mparams->hvalue, "message/partial") != 0) { | ||
| 799 | procmime_mime_params_free(mparams); | ||
| 800 | return; | ||
| 801 | } | ||
| 802 | |||
| 803 | for (cur = mparams->plist; cur != NULL; cur = cur->next) { | ||
| 804 | MimeParam *param = (MimeParam *)cur->data; | ||
| 805 | if (!g_ascii_strcasecmp(param->name, "total")) { | ||
| 806 | t = atoi(param->value); | ||
| 807 | } else if (!id_str && !g_ascii_strcasecmp(param->name, "id")) { | ||
| 808 | id_str = g_strdup(param->value); | ||
| 809 | } else if (!g_ascii_strcasecmp(param->name, "number")) { | ||
| 810 | n = atoi(param->value); | ||
| 811 | } | ||
| 812 | } | ||
| 813 | |||
| 814 | procmime_mime_params_free(mparams); | ||
| 815 | |||
| 816 | if (n > 0 && (t == 0 || t >= n) && id_str) { | ||
| 817 | *total = t; | ||
| 818 | *part_id = id_str; | ||
| 819 | *number = n; | ||
| 820 | } else { | ||
| 821 | g_free(id_str); | ||
| 822 | } | ||
| 823 | } | ||
| 824 | |||
| 825 | void procmime_scan_content_disposition(MimeInfo *mimeinfo, | ||
| 826 | const gchar *content_disposition) | ||
| 827 | { | ||
| 828 | MimeParams *mparams; | ||
| 829 | GSList *cur; | ||
| 830 | |||
| 831 | mparams = procmime_parse_mime_parameter(content_disposition); | ||
| 832 | |||
| 833 | mimeinfo->content_disposition = g_strdup(mparams->hvalue); | ||
| 834 | |||
| 835 | for (cur = mparams->plist; cur != NULL; cur = cur->next) { | ||
| 836 | MimeParam *param = (MimeParam *)cur->data; | ||
| 837 | if (!g_ascii_strcasecmp(param->name, "filename")) { | ||
| 838 | mimeinfo->filename = g_strdup(param->value); | ||
| 839 | break; | ||
| 840 | } | ||
| 841 | } | ||
| 842 | |||
| 843 | procmime_mime_params_free(mparams); | ||
| 844 | } | ||
| 845 | |||
| 846 | enum | ||
| 847 | { | ||
| 848 | H_CONTENT_TRANSFER_ENCODING = 0, | ||
| 849 | H_CONTENT_TYPE = 1, | ||
| 850 | H_CONTENT_DISPOSITION = 2 | ||
| 851 | }; | ||
| 852 | |||
| 853 | MimeInfo *procmime_scan_mime_header(FILE *fp) | ||
| 854 | { | ||
| 855 | static HeaderEntry hentry[] = {{"Content-Transfer-Encoding:", | ||
| 856 | NULL, FALSE}, | ||
| 857 | {"Content-Type:", NULL, TRUE}, | ||
| 858 | {"Content-Disposition:", | ||
| 859 | NULL, TRUE}, | ||
| 860 | {NULL, NULL, FALSE}}; | ||
| 861 | gchar buf[BUFFSIZE]; | ||
| 862 | gint hnum; | ||
| 863 | HeaderEntry *hp; | ||
| 864 | MimeInfo *mimeinfo; | ||
| 865 | |||
| 866 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 867 | |||
| 868 | mimeinfo = procmime_mimeinfo_new(); | ||
| 869 | mimeinfo->mime_type = MIME_TEXT; | ||
| 870 | mimeinfo->encoding_type = ENC_7BIT; | ||
| 871 | mimeinfo->fpos = ftell(fp); | ||
| 872 | |||
| 873 | while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) | ||
| 874 | != -1) { | ||
| 875 | hp = hentry + hnum; | ||
| 876 | |||
| 877 | if (H_CONTENT_TRANSFER_ENCODING == hnum) { | ||
| 878 | procmime_scan_encoding | ||
| 879 | (mimeinfo, buf + strlen(hp->name)); | ||
| 880 | } else if (H_CONTENT_TYPE == hnum) { | ||
| 881 | procmime_scan_content_type | ||
| 882 | (mimeinfo, buf + strlen(hp->name)); | ||
| 883 | } else if (H_CONTENT_DISPOSITION == hnum) { | ||
| 884 | procmime_scan_content_disposition | ||
| 885 | (mimeinfo, buf + strlen(hp->name)); | ||
| 886 | } | ||
| 887 | } | ||
| 888 | |||
| 889 | if (mimeinfo->mime_type == MIME_APPLICATION_OCTET_STREAM && | ||
| 890 | (mimeinfo->filename || mimeinfo->name)) { | ||
| 891 | const gchar *type; | ||
| 892 | type = procmime_get_mime_type | ||
| 893 | (mimeinfo->filename ? mimeinfo->filename | ||
| 894 | : mimeinfo->name); | ||
| 895 | if (type) | ||
| 896 | mimeinfo->mime_type = procmime_scan_mime_type(type); | ||
| 897 | } | ||
| 898 | |||
| 899 | if (!mimeinfo->content_type) | ||
| 900 | mimeinfo->content_type = g_strdup("text/plain"); | ||
| 901 | |||
| 902 | return mimeinfo; | ||
| 903 | } | ||
| 904 | |||
| 905 | static gint procmime_normalize_lbreak(FILE *infp, FILE *outfp) | ||
| 906 | { | ||
| 907 | gchar buf[BUFFSIZE]; | ||
| 908 | gint len; | ||
| 909 | |||
| 910 | g_return_val_if_fail(infp != NULL, -1); | ||
| 911 | g_return_val_if_fail(outfp != NULL, -1); | ||
| 912 | |||
| 913 | while (fgets(buf, sizeof(buf), infp) != NULL) { | ||
| 914 | len = strlen(buf); | ||
| 915 | if (len == sizeof(buf) - 1 && buf[len - 1] != '\n') { | ||
| 916 | if (buf[len - 1] == '\r') { | ||
| 917 | ungetc('\r', infp); | ||
| 918 | buf[len - 1] = '\0'; | ||
| 919 | } | ||
| 920 | fputs(buf, outfp); | ||
| 921 | continue; | ||
| 922 | } | ||
| 923 | #ifdef G_OS_WIN32 | ||
| 924 | strretchomp(buf); | ||
| 925 | fputs(buf, outfp); | ||
| 926 | fputs("\r\n", outfp); | ||
| 927 | #else | ||
| 928 | strcrchomp(buf); | ||
| 929 | fputs(buf, outfp); | ||
| 930 | #endif | ||
| 931 | } | ||
| 932 | |||
| 933 | return 0; | ||
| 934 | } | ||
| 935 | |||
| 936 | FILE *procmime_decode_content(FILE *outfp, FILE *infp, MimeInfo *mimeinfo) | ||
| 937 | { | ||
| 938 | gchar buf[BUFFSIZE]; | ||
| 939 | gchar *boundary = NULL; | ||
| 940 | gint boundary_len = 0; | ||
| 941 | gboolean tmp_file = FALSE; | ||
| 942 | gboolean normalize_lbreak = FALSE; | ||
| 943 | ContentType content_type; | ||
| 944 | |||
| 945 | g_return_val_if_fail(infp != NULL, NULL); | ||
| 946 | g_return_val_if_fail(mimeinfo != NULL, NULL); | ||
| 947 | |||
| 948 | if (!outfp) { | ||
| 949 | outfp = my_tmpfile(); | ||
| 950 | if (!outfp) { | ||
| 951 | perror("tmpfile"); | ||
| 952 | return NULL; | ||
| 953 | } | ||
| 954 | tmp_file = TRUE; | ||
| 955 | } | ||
| 956 | |||
| 957 | if (mimeinfo->parent && mimeinfo->parent->boundary) { | ||
| 958 | boundary = mimeinfo->parent->boundary; | ||
| 959 | boundary_len = strlen(boundary); | ||
| 960 | } | ||
| 961 | |||
| 962 | content_type = procmime_scan_mime_type(mimeinfo->content_type); | ||
| 963 | if (content_type == MIME_TEXT || | ||
| 964 | content_type == MIME_TEXT_HTML) { | ||
| 965 | normalize_lbreak = TRUE; | ||
| 966 | } | ||
| 967 | |||
| 968 | if (mimeinfo->encoding_type == ENC_QUOTED_PRINTABLE) { | ||
| 969 | FILE *tmpfp = outfp; | ||
| 970 | gchar prev_empty_line[3] = ""; | ||
| 971 | |||
| 972 | if (normalize_lbreak) { | ||
| 973 | tmpfp = my_tmpfile(); | ||
| 974 | if (!tmpfp) { | ||
| 975 | perror("tmpfile"); | ||
| 976 | if (tmp_file) fclose(outfp); | ||
| 977 | return NULL; | ||
| 978 | } | ||
| 979 | } | ||
| 980 | |||
| 981 | while (fgets(buf, sizeof(buf), infp) != NULL && | ||
| 982 | (!boundary || | ||
| 983 | !IS_BOUNDARY(buf, boundary, boundary_len))) { | ||
| 984 | gint len; | ||
| 985 | |||
| 986 | if (prev_empty_line[0]) { | ||
| 987 | fputs(prev_empty_line, tmpfp); | ||
| 988 | prev_empty_line[0] = '\0'; | ||
| 989 | } | ||
| 990 | |||
| 991 | if (buf[0] == '\n' || | ||
| 992 | (buf[0] == '\r' && buf[1] == '\n')) | ||
| 993 | strcpy(prev_empty_line, buf); | ||
| 994 | else { | ||
| 995 | len = qp_decode_line(buf); | ||
| 996 | fwrite(buf, len, 1, tmpfp); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | if (!boundary && prev_empty_line[0]) | ||
| 1000 | fputs(prev_empty_line, tmpfp); | ||
| 1001 | |||
| 1002 | if (normalize_lbreak) { | ||
| 1003 | if (fflush(tmpfp) == EOF) { | ||
| 1004 | perror("fflush"); | ||
| 1005 | fclose(tmpfp); | ||
| 1006 | if (tmp_file) fclose(outfp); | ||
| 1007 | return NULL; | ||
| 1008 | } | ||
| 1009 | rewind(tmpfp); | ||
| 1010 | procmime_normalize_lbreak(tmpfp, outfp); | ||
| 1011 | fclose(tmpfp); | ||
| 1012 | } | ||
| 1013 | } else if (mimeinfo->encoding_type == ENC_BASE64) { | ||
| 1014 | gchar outbuf[BUFFSIZE]; | ||
| 1015 | gint len; | ||
| 1016 | Base64Decoder *decoder; | ||
| 1017 | FILE *tmpfp = outfp; | ||
| 1018 | |||
| 1019 | if (normalize_lbreak) { | ||
| 1020 | tmpfp = my_tmpfile(); | ||
| 1021 | if (!tmpfp) { | ||
| 1022 | perror("tmpfile"); | ||
| 1023 | if (tmp_file) fclose(outfp); | ||
| 1024 | return NULL; | ||
| 1025 | } | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | decoder = base64_decoder_new(); | ||
| 1029 | while (fgets(buf, sizeof(buf), infp) != NULL && | ||
| 1030 | (!boundary || | ||
| 1031 | !IS_BOUNDARY(buf, boundary, boundary_len))) { | ||
| 1032 | len = base64_decoder_decode(decoder, buf, | ||
| 1033 | (guchar *)outbuf); | ||
| 1034 | if (len < 0) { | ||
| 1035 | g_warning("Bad BASE64 content\n"); | ||
| 1036 | break; | ||
| 1037 | } | ||
| 1038 | fwrite(outbuf, sizeof(gchar), len, tmpfp); | ||
| 1039 | } | ||
| 1040 | base64_decoder_free(decoder); | ||
| 1041 | |||
| 1042 | if (normalize_lbreak) { | ||
| 1043 | if (fflush(tmpfp) == EOF) { | ||
| 1044 | perror("fflush"); | ||
| 1045 | fclose(tmpfp); | ||
| 1046 | if (tmp_file) fclose(outfp); | ||
| 1047 | return NULL; | ||
| 1048 | } | ||
| 1049 | rewind(tmpfp); | ||
| 1050 | procmime_normalize_lbreak(tmpfp, outfp); | ||
| 1051 | fclose(tmpfp); | ||
| 1052 | } | ||
| 1053 | } else if (mimeinfo->encoding_type == ENC_X_UUENCODE) { | ||
| 1054 | gchar outbuf[BUFFSIZE]; | ||
| 1055 | gint len; | ||
| 1056 | gboolean flag = FALSE; | ||
| 1057 | |||
| 1058 | while (fgets(buf, sizeof(buf), infp) != NULL && | ||
| 1059 | (!boundary || | ||
| 1060 | !IS_BOUNDARY(buf, boundary, boundary_len))) { | ||
| 1061 | if(!flag && strncmp(buf,"begin ", 6)) continue; | ||
| 1062 | |||
| 1063 | if (flag) { | ||
| 1064 | len = fromuutobits(outbuf, buf); | ||
| 1065 | if (len <= 0) { | ||
| 1066 | if (len < 0) | ||
| 1067 | g_warning("Bad UUENCODE content(%d)\n", len); | ||
| 1068 | break; | ||
| 1069 | } | ||
| 1070 | fwrite(outbuf, sizeof(gchar), len, outfp); | ||
| 1071 | } else | ||
| 1072 | flag = TRUE; | ||
| 1073 | } | ||
| 1074 | } else { | ||
| 1075 | gchar prev_empty_line[3] = ""; | ||
| 1076 | gint len; | ||
| 1077 | gboolean cont_line = FALSE; | ||
| 1078 | |||
| 1079 | while (fgets(buf, sizeof(buf), infp) != NULL && | ||
| 1080 | (!boundary || | ||
| 1081 | !IS_BOUNDARY(buf, boundary, boundary_len))) { | ||
| 1082 | if (prev_empty_line[0]) { | ||
| 1083 | fputs(prev_empty_line, outfp); | ||
| 1084 | prev_empty_line[0] = '\0'; | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | len = strlen(buf); | ||
| 1088 | if (len == sizeof(buf) - 1 && | ||
| 1089 | buf[len - 1] != '\n') { | ||
| 1090 | if (buf[len - 1] == '\r') { | ||
| 1091 | ungetc('\r', infp); | ||
| 1092 | buf[len - 1] = '\0'; | ||
| 1093 | } | ||
| 1094 | fputs(buf, outfp); | ||
| 1095 | cont_line = TRUE; | ||
| 1096 | continue; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (normalize_lbreak) { | ||
| 1100 | #ifdef G_OS_WIN32 | ||
| 1101 | strretchomp(buf); | ||
| 1102 | if (!cont_line && buf[0] == '\0') | ||
| 1103 | strcpy(prev_empty_line, "\r\n"); | ||
| 1104 | else { | ||
| 1105 | fputs(buf, outfp); | ||
| 1106 | fputs("\r\n", outfp); | ||
| 1107 | } | ||
| 1108 | #else | ||
| 1109 | strcrchomp(buf); | ||
| 1110 | if (!cont_line && buf[0] == '\n') | ||
| 1111 | strcpy(prev_empty_line, "\n"); | ||
| 1112 | else | ||
| 1113 | fputs(buf, outfp); | ||
| 1114 | #endif | ||
| 1115 | } else { | ||
| 1116 | if (!cont_line && | ||
| 1117 | (buf[0] == '\n' || | ||
| 1118 | (buf[0] == '\r' && buf[1] == '\n'))) | ||
| 1119 | strcpy(prev_empty_line, buf); | ||
| 1120 | else | ||
| 1121 | fputs(buf, outfp); | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | cont_line = FALSE; | ||
| 1125 | } | ||
| 1126 | if (!boundary && prev_empty_line[0]) | ||
| 1127 | fputs(prev_empty_line, outfp); | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | if (fflush(outfp) == EOF) | ||
| 1131 | perror("fflush"); | ||
| 1132 | if (ferror(outfp) != 0) { | ||
| 1133 | g_warning("procmime_decode_content(): Can't write to temporary file\n"); | ||
| 1134 | if (tmp_file) fclose(outfp); | ||
| 1135 | return NULL; | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | if (tmp_file) rewind(outfp); | ||
| 1139 | return outfp; | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | gint procmime_get_part(const gchar *outfile, const gchar *infile, | ||
| 1143 | MimeInfo *mimeinfo) | ||
| 1144 | { | ||
| 1145 | FILE *infp; | ||
| 1146 | gint ret; | ||
| 1147 | |||
| 1148 | g_return_val_if_fail(outfile != NULL, -1); | ||
| 1149 | g_return_val_if_fail(infile != NULL, -1); | ||
| 1150 | g_return_val_if_fail(mimeinfo != NULL, -1); | ||
| 1151 | |||
| 1152 | if ((infp = g_fopen(infile, "rb")) == NULL) { | ||
| 1153 | FILE_OP_ERROR(infile, "fopen"); | ||
| 1154 | return -1; | ||
| 1155 | } | ||
| 1156 | ret = procmime_get_part_fp(outfile, infp, mimeinfo); | ||
| 1157 | fclose(infp); | ||
| 1158 | |||
| 1159 | return ret; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | gint procmime_get_part_fp(const gchar *outfile, FILE *infp, MimeInfo *mimeinfo) | ||
| 1163 | { | ||
| 1164 | FILE *outfp; | ||
| 1165 | gchar buf[BUFFSIZE]; | ||
| 1166 | |||
| 1167 | g_return_val_if_fail(outfile != NULL, -1); | ||
| 1168 | g_return_val_if_fail(infp != NULL, -1); | ||
| 1169 | g_return_val_if_fail(mimeinfo != NULL, -1); | ||
| 1170 | |||
| 1171 | if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) { | ||
| 1172 | FILE_OP_ERROR("procmime_get_part_fp()", "fseek"); | ||
| 1173 | return -1; | ||
| 1174 | } | ||
| 1175 | if ((outfp = g_fopen(outfile, "wb")) == NULL) { | ||
| 1176 | FILE_OP_ERROR(outfile, "fopen"); | ||
| 1177 | return -1; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | while (fgets(buf, sizeof(buf), infp) != NULL) | ||
| 1181 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 1182 | |||
| 1183 | if (procmime_decode_content(outfp, infp, mimeinfo) == NULL) { | ||
| 1184 | fclose(outfp); | ||
| 1185 | g_unlink(outfile); | ||
| 1186 | return -1; | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | if (fclose(outfp) == EOF) { | ||
| 1190 | FILE_OP_ERROR(outfile, "fclose"); | ||
| 1191 | g_unlink(outfile); | ||
| 1192 | return -1; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | return 0; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | FILE *procmime_get_part_fp_fp(FILE *outfp, FILE *infp, MimeInfo *mimeinfo) | ||
| 1199 | { | ||
| 1200 | gchar buf[BUFFSIZE]; | ||
| 1201 | |||
| 1202 | g_return_val_if_fail(infp != NULL, NULL); | ||
| 1203 | g_return_val_if_fail(mimeinfo != NULL, NULL); | ||
| 1204 | |||
| 1205 | if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) { | ||
| 1206 | FILE_OP_ERROR("procmime_get_part_fp()", "fseek"); | ||
| 1207 | return NULL; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | while (fgets(buf, sizeof(buf), infp) != NULL) | ||
| 1211 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 1212 | |||
| 1213 | if ((outfp = procmime_decode_content(outfp, infp, mimeinfo)) == NULL) { | ||
| 1214 | return NULL; | ||
| 1215 | } | ||
| 1216 | |||
| 1217 | return outfp; | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | gint procmime_get_all_parts(const gchar *dir, const gchar *infile, | ||
| 1221 | MimeInfo *mimeinfo) | ||
| 1222 | { | ||
| 1223 | FILE *fp; | ||
| 1224 | MimeInfo *partinfo; | ||
| 1225 | gchar *base, *filename; | ||
| 1226 | |||
| 1227 | g_return_val_if_fail(dir != NULL, -1); | ||
| 1228 | g_return_val_if_fail(infile != NULL, -1); | ||
| 1229 | g_return_val_if_fail(mimeinfo != NULL, -1); | ||
| 1230 | |||
| 1231 | if (!is_dir_exist(dir)) { | ||
| 1232 | g_warning("%s: directory not exist.\n", dir); | ||
| 1233 | return -1; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | if ((fp = g_fopen(infile, "rb")) == NULL) { | ||
| 1237 | FILE_OP_ERROR(infile, "fopen"); | ||
| 1238 | return -1; | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | for (partinfo = mimeinfo; partinfo != NULL; | ||
| 1242 | partinfo = procmime_mimeinfo_next(partinfo)) { | ||
| 1243 | if (partinfo->filename || partinfo->name) { | ||
| 1244 | gint count = 1; | ||
| 1245 | |||
| 1246 | base = procmime_get_part_file_name(partinfo); | ||
| 1247 | filename = g_strconcat(dir, G_DIR_SEPARATOR_S, base, | ||
| 1248 | NULL); | ||
| 1249 | |||
| 1250 | while (is_file_entry_exist(filename)) { | ||
| 1251 | gchar *base_alt; | ||
| 1252 | |||
| 1253 | base_alt = get_alt_filename(base, count++); | ||
| 1254 | g_free(filename); | ||
| 1255 | filename = g_strconcat | ||
| 1256 | (dir, G_DIR_SEPARATOR_S, base_alt, | ||
| 1257 | NULL); | ||
| 1258 | g_free(base_alt); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | procmime_get_part_fp(filename, fp, partinfo); | ||
| 1262 | |||
| 1263 | g_free(filename); | ||
| 1264 | g_free(base); | ||
| 1265 | } | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | fclose(fp); | ||
| 1269 | |||
| 1270 | return 0; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | FILE *procmime_get_text_content(MimeInfo *mimeinfo, FILE *infp, | ||
| 1274 | const gchar *encoding) | ||
| 1275 | { | ||
| 1276 | FILE *tmpfp, *outfp; | ||
| 1277 | const gchar *src_encoding; | ||
| 1278 | gboolean conv_fail = FALSE; | ||
| 1279 | gchar buf[BUFFSIZE]; | ||
| 1280 | |||
| 1281 | g_return_val_if_fail(mimeinfo != NULL, NULL); | ||
| 1282 | g_return_val_if_fail(infp != NULL, NULL); | ||
| 1283 | g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT || | ||
| 1284 | mimeinfo->mime_type == MIME_TEXT_HTML, NULL); | ||
| 1285 | |||
| 1286 | if (fseek(infp, mimeinfo->fpos, SEEK_SET) < 0) { | ||
| 1287 | perror("fseek"); | ||
| 1288 | return NULL; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | while (fgets(buf, sizeof(buf), infp) != NULL) | ||
| 1292 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 1293 | |||
| 1294 | tmpfp = procmime_decode_content(NULL, infp, mimeinfo); | ||
| 1295 | if (!tmpfp) | ||
| 1296 | return NULL; | ||
| 1297 | |||
| 1298 | if ((outfp = my_tmpfile()) == NULL) { | ||
| 1299 | perror("tmpfile"); | ||
| 1300 | fclose(tmpfp); | ||
| 1301 | return NULL; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | src_encoding = prefs_common.force_charset ? prefs_common.force_charset | ||
| 1305 | : mimeinfo->charset ? mimeinfo->charset | ||
| 1306 | : prefs_common.default_encoding; | ||
| 1307 | |||
| 1308 | if (mimeinfo->mime_type == MIME_TEXT) { | ||
| 1309 | while (fgets(buf, sizeof(buf), tmpfp) != NULL) { | ||
| 1310 | gchar *str; | ||
| 1311 | |||
| 1312 | str = conv_codeset_strdup(buf, src_encoding, encoding); | ||
| 1313 | if (str) { | ||
| 1314 | fputs(str, outfp); | ||
| 1315 | g_free(str); | ||
| 1316 | } else { | ||
| 1317 | conv_fail = TRUE; | ||
| 1318 | fputs(buf, outfp); | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | } else if (mimeinfo->mime_type == MIME_TEXT_HTML) { | ||
| 1322 | HTMLParser *parser; | ||
| 1323 | CodeConverter *conv; | ||
| 1324 | const gchar *str; | ||
| 1325 | |||
| 1326 | conv = conv_code_converter_new(src_encoding, encoding); | ||
| 1327 | parser = html_parser_new(tmpfp, conv); | ||
| 1328 | while ((str = html_parse(parser)) != NULL) { | ||
| 1329 | fputs(str, outfp); | ||
| 1330 | } | ||
| 1331 | html_parser_destroy(parser); | ||
| 1332 | conv_code_converter_destroy(conv); | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | if (conv_fail) | ||
| 1336 | g_warning(_("procmime_get_text_content(): Code conversion failed.\n")); | ||
| 1337 | |||
| 1338 | fclose(tmpfp); | ||
| 1339 | if (fflush(outfp) == EOF) { | ||
| 1340 | perror("fflush"); | ||
| 1341 | fclose(outfp); | ||
| 1342 | return NULL; | ||
| 1343 | } | ||
| 1344 | rewind(outfp); | ||
| 1345 | |||
| 1346 | return outfp; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | /* search the first text part of (multipart) MIME message, | ||
| 1350 | decode, convert it and output to outfp. */ | ||
| 1351 | FILE *procmime_get_first_text_content(MsgInfo *msginfo, const gchar *encoding) | ||
| 1352 | { | ||
| 1353 | FILE *infp, *outfp = NULL; | ||
| 1354 | MimeInfo *mimeinfo, *partinfo; | ||
| 1355 | |||
| 1356 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 1357 | |||
| 1358 | mimeinfo = procmime_scan_message(msginfo); | ||
| 1359 | if (!mimeinfo) return NULL; | ||
| 1360 | |||
| 1361 | if ((infp = procmsg_open_message(msginfo)) == NULL) { | ||
| 1362 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1363 | return NULL; | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | partinfo = mimeinfo; | ||
| 1367 | while (partinfo && partinfo->mime_type != MIME_TEXT) | ||
| 1368 | partinfo = procmime_mimeinfo_next(partinfo); | ||
| 1369 | if (!partinfo) { | ||
| 1370 | partinfo = mimeinfo; | ||
| 1371 | while (partinfo && partinfo->mime_type != MIME_TEXT_HTML) | ||
| 1372 | partinfo = procmime_mimeinfo_next(partinfo); | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | if (partinfo) | ||
| 1376 | outfp = procmime_get_text_content(partinfo, infp, encoding); | ||
| 1377 | |||
| 1378 | fclose(infp); | ||
| 1379 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1380 | |||
| 1381 | return outfp; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | gboolean procmime_find_string_part(MimeInfo *mimeinfo, const gchar *filename, | ||
| 1385 | const gchar *str, StrFindFunc find_func) | ||
| 1386 | { | ||
| 1387 | |||
| 1388 | FILE *infp, *outfp; | ||
| 1389 | gchar buf[BUFFSIZE]; | ||
| 1390 | |||
| 1391 | g_return_val_if_fail(mimeinfo != NULL, FALSE); | ||
| 1392 | g_return_val_if_fail(mimeinfo->mime_type == MIME_TEXT || | ||
| 1393 | mimeinfo->mime_type == MIME_TEXT_HTML, FALSE); | ||
| 1394 | g_return_val_if_fail(str != NULL, FALSE); | ||
| 1395 | g_return_val_if_fail(find_func != NULL, FALSE); | ||
| 1396 | |||
| 1397 | if ((infp = g_fopen(filename, "rb")) == NULL) { | ||
| 1398 | FILE_OP_ERROR(filename, "fopen"); | ||
| 1399 | return FALSE; | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | outfp = procmime_get_text_content(mimeinfo, infp, NULL); | ||
| 1403 | fclose(infp); | ||
| 1404 | |||
| 1405 | if (!outfp) | ||
| 1406 | return FALSE; | ||
| 1407 | |||
| 1408 | while (fgets(buf, sizeof(buf), outfp) != NULL) { | ||
| 1409 | strretchomp(buf); | ||
| 1410 | if (find_func(buf, str)) { | ||
| 1411 | fclose(outfp); | ||
| 1412 | return TRUE; | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | fclose(outfp); | ||
| 1417 | |||
| 1418 | return FALSE; | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | gboolean procmime_find_string(MsgInfo *msginfo, const gchar *str, | ||
| 1422 | StrFindFunc find_func) | ||
| 1423 | { | ||
| 1424 | MimeInfo *mimeinfo; | ||
| 1425 | MimeInfo *partinfo; | ||
| 1426 | gchar *filename; | ||
| 1427 | gboolean found = FALSE; | ||
| 1428 | |||
| 1429 | g_return_val_if_fail(msginfo != NULL, FALSE); | ||
| 1430 | g_return_val_if_fail(str != NULL, FALSE); | ||
| 1431 | g_return_val_if_fail(find_func != NULL, FALSE); | ||
| 1432 | |||
| 1433 | filename = procmsg_get_message_file(msginfo); | ||
| 1434 | if (!filename) return FALSE; | ||
| 1435 | mimeinfo = procmime_scan_message(msginfo); | ||
| 1436 | |||
| 1437 | for (partinfo = mimeinfo; partinfo != NULL; | ||
| 1438 | partinfo = procmime_mimeinfo_next(partinfo)) { | ||
| 1439 | if (partinfo->mime_type == MIME_TEXT || | ||
| 1440 | partinfo->mime_type == MIME_TEXT_HTML) { | ||
| 1441 | if (procmime_find_string_part | ||
| 1442 | (partinfo, filename, str, find_func) == TRUE) { | ||
| 1443 | found = TRUE; | ||
| 1444 | break; | ||
| 1445 | } | ||
| 1446 | } | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1450 | g_free(filename); | ||
| 1451 | |||
| 1452 | return found; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | gchar *procmime_get_part_file_name(MimeInfo *mimeinfo) | ||
| 1456 | { | ||
| 1457 | gchar *base; | ||
| 1458 | const gchar *base_; | ||
| 1459 | |||
| 1460 | base_ = mimeinfo->filename ? mimeinfo->filename | ||
| 1461 | : mimeinfo->name ? mimeinfo->name : "mimetmp"; | ||
| 1462 | base_ = g_basename(base_); | ||
| 1463 | if (*base_ == '\0') base_ = "mimetmp"; | ||
| 1464 | base = conv_filename_from_utf8(base_); | ||
| 1465 | subst_for_filename(base); | ||
| 1466 | |||
| 1467 | return base; | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | gchar *procmime_get_tmp_file_name(MimeInfo *mimeinfo) | ||
| 1471 | { | ||
| 1472 | static guint32 id = 0; | ||
| 1473 | gchar *base; | ||
| 1474 | gchar *filename = NULL; | ||
| 1475 | gchar f_prefix[10]; | ||
| 1476 | |||
| 1477 | g_return_val_if_fail(mimeinfo != NULL, NULL); | ||
| 1478 | |||
| 1479 | if (MIME_TEXT_HTML == mimeinfo->mime_type) | ||
| 1480 | base = g_strdup("mimetmp.html"); | ||
| 1481 | else | ||
| 1482 | base = procmime_get_part_file_name(mimeinfo); | ||
| 1483 | |||
| 1484 | do { | ||
| 1485 | g_snprintf(f_prefix, sizeof(f_prefix), "%08x.", id++); | ||
| 1486 | if (filename) | ||
| 1487 | g_free(filename); | ||
| 1488 | filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S, | ||
| 1489 | f_prefix, base, NULL); | ||
| 1490 | } while (is_file_entry_exist(filename)); | ||
| 1491 | |||
| 1492 | g_free(base); | ||
| 1493 | |||
| 1494 | debug_print("procmime_get_tmp_file_name: %s\n", filename); | ||
| 1495 | |||
| 1496 | return filename; | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | gchar *procmime_get_tmp_file_name_for_user(MimeInfo *mimeinfo) | ||
| 1500 | { | ||
| 1501 | gchar *base; | ||
| 1502 | gchar *filename; | ||
| 1503 | gint count = 1; | ||
| 1504 | |||
| 1505 | g_return_val_if_fail(mimeinfo != NULL, NULL); | ||
| 1506 | |||
| 1507 | if (MIME_TEXT_HTML == mimeinfo->mime_type) | ||
| 1508 | base = g_strdup("mimetmp.html"); | ||
| 1509 | else | ||
| 1510 | base = procmime_get_part_file_name(mimeinfo); | ||
| 1511 | |||
| 1512 | filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S, | ||
| 1513 | base, NULL); | ||
| 1514 | while (is_file_entry_exist(filename)) { | ||
| 1515 | gchar *base_alt; | ||
| 1516 | |||
| 1517 | base_alt = get_alt_filename(base, count++); | ||
| 1518 | g_free(filename); | ||
| 1519 | filename = g_strconcat(get_mime_tmp_dir(), G_DIR_SEPARATOR_S, | ||
| 1520 | base_alt, NULL); | ||
| 1521 | g_free(base_alt); | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | g_free(base); | ||
| 1525 | |||
| 1526 | debug_print("procmime_get_tmp_file_name_for_user: %s\n", filename); | ||
| 1527 | |||
| 1528 | return filename; | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | ContentType procmime_scan_mime_type(const gchar *mime_type) | ||
| 1532 | { | ||
| 1533 | ContentType type; | ||
| 1534 | |||
| 1535 | if (!g_ascii_strncasecmp(mime_type, "text/html", 9)) | ||
| 1536 | type = MIME_TEXT_HTML; | ||
| 1537 | else if (!g_ascii_strncasecmp(mime_type, "text/", 5)) | ||
| 1538 | type = MIME_TEXT; | ||
| 1539 | else if (!g_ascii_strncasecmp(mime_type, "message/rfc822", 14)) | ||
| 1540 | type = MIME_MESSAGE_RFC822; | ||
| 1541 | else if (!g_ascii_strncasecmp(mime_type, "message/", 8)) | ||
| 1542 | type = MIME_TEXT; | ||
| 1543 | else if (!g_ascii_strncasecmp(mime_type, "application/octet-stream", | ||
| 1544 | 24)) | ||
| 1545 | type = MIME_APPLICATION_OCTET_STREAM; | ||
| 1546 | else if (!g_ascii_strncasecmp(mime_type, "application/", 12)) | ||
| 1547 | type = MIME_APPLICATION; | ||
| 1548 | else if (!g_ascii_strncasecmp(mime_type, "multipart/", 10)) | ||
| 1549 | type = MIME_MULTIPART; | ||
| 1550 | else if (!g_ascii_strncasecmp(mime_type, "image/", 6)) | ||
| 1551 | type = MIME_IMAGE; | ||
| 1552 | else if (!g_ascii_strncasecmp(mime_type, "audio/", 6)) | ||
| 1553 | type = MIME_AUDIO; | ||
| 1554 | else if (!g_ascii_strncasecmp(mime_type, "video/", 6)) | ||
| 1555 | type = MIME_VIDEO; | ||
| 1556 | else if (!g_ascii_strcasecmp(mime_type, "text")) | ||
| 1557 | type = MIME_TEXT; | ||
| 1558 | else | ||
| 1559 | type = MIME_UNKNOWN; | ||
| 1560 | |||
| 1561 | return type; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | static GList *mime_type_list = NULL; | ||
| 1565 | |||
| 1566 | gchar *procmime_get_mime_type(const gchar *filename) | ||
| 1567 | { | ||
| 1568 | static GHashTable *mime_type_table = NULL; | ||
| 1569 | MimeType *mime_type; | ||
| 1570 | const gchar *p; | ||
| 1571 | gchar ext[64]; | ||
| 1572 | static gboolean no_mime_type_table = FALSE; | ||
| 1573 | |||
| 1574 | if (no_mime_type_table) | ||
| 1575 | return NULL; | ||
| 1576 | |||
| 1577 | if (!mime_type_table) { | ||
| 1578 | mime_type_table = procmime_get_mime_type_table(); | ||
| 1579 | if (!mime_type_table) { | ||
| 1580 | no_mime_type_table = TRUE; | ||
| 1581 | return NULL; | ||
| 1582 | } | ||
| 1583 | } | ||
| 1584 | |||
| 1585 | filename = g_basename(filename); | ||
| 1586 | p = strrchr(filename, '.'); | ||
| 1587 | if (!p) return NULL; | ||
| 1588 | |||
| 1589 | strncpy2(ext, p + 1, sizeof(ext)); | ||
| 1590 | g_strdown(ext); | ||
| 1591 | mime_type = g_hash_table_lookup(mime_type_table, ext); | ||
| 1592 | if (mime_type) { | ||
| 1593 | gchar *str; | ||
| 1594 | |||
| 1595 | str = g_strconcat(mime_type->type, "/", mime_type->sub_type, | ||
| 1596 | NULL); | ||
| 1597 | return str; | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | return NULL; | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | static GHashTable *procmime_get_mime_type_table(void) | ||
| 1604 | { | ||
| 1605 | GHashTable *table = NULL; | ||
| 1606 | GList *cur; | ||
| 1607 | MimeType *mime_type; | ||
| 1608 | gchar **exts; | ||
| 1609 | |||
| 1610 | if (!mime_type_list) { | ||
| 1611 | GList *list; | ||
| 1612 | gchar *dir; | ||
| 1613 | |||
| 1614 | #ifdef G_OS_WIN32 | ||
| 1615 | dir = g_strconcat(get_startup_dir(), | ||
| 1616 | G_DIR_SEPARATOR_S "etc" G_DIR_SEPARATOR_S | ||
| 1617 | "mime.types", NULL); | ||
| 1618 | mime_type_list = procmime_get_mime_type_list(dir); | ||
| 1619 | g_free(dir); | ||
| 1620 | #else | ||
| 1621 | mime_type_list = | ||
| 1622 | procmime_get_mime_type_list(SYSCONFDIR "/mime.types"); | ||
| 1623 | if (!mime_type_list) | ||
| 1624 | mime_type_list = | ||
| 1625 | procmime_get_mime_type_list("/etc/mime.types"); | ||
| 1626 | #endif | ||
| 1627 | dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 1628 | "mime.types", NULL); | ||
| 1629 | list = procmime_get_mime_type_list(dir); | ||
| 1630 | g_free(dir); | ||
| 1631 | mime_type_list = g_list_concat(mime_type_list, list); | ||
| 1632 | |||
| 1633 | if (!mime_type_list) { | ||
| 1634 | debug_print("mime.types not found\n"); | ||
| 1635 | return NULL; | ||
| 1636 | } | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 1640 | |||
| 1641 | for (cur = mime_type_list; cur != NULL; cur = cur->next) { | ||
| 1642 | gint i; | ||
| 1643 | gchar *key; | ||
| 1644 | |||
| 1645 | mime_type = (MimeType *)cur->data; | ||
| 1646 | |||
| 1647 | if (!mime_type->extension) continue; | ||
| 1648 | |||
| 1649 | exts = g_strsplit(mime_type->extension, " ", 16); | ||
| 1650 | for (i = 0; exts[i] != NULL; i++) { | ||
| 1651 | /* make the key case insensitive */ | ||
| 1652 | g_strdown(exts[i]); | ||
| 1653 | /* use previously dup'd key on overwriting */ | ||
| 1654 | if (g_hash_table_lookup(table, exts[i])) | ||
| 1655 | key = exts[i]; | ||
| 1656 | else | ||
| 1657 | key = g_strdup(exts[i]); | ||
| 1658 | g_hash_table_insert(table, key, mime_type); | ||
| 1659 | } | ||
| 1660 | g_strfreev(exts); | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | return table; | ||
| 1664 | } | ||
| 1665 | |||
| 1666 | static GList *procmime_get_mime_type_list(const gchar *file) | ||
| 1667 | { | ||
| 1668 | GList *list = NULL; | ||
| 1669 | FILE *fp; | ||
| 1670 | gchar buf[BUFFSIZE]; | ||
| 1671 | gchar *p; | ||
| 1672 | gchar *delim; | ||
| 1673 | MimeType *mime_type; | ||
| 1674 | |||
| 1675 | if ((fp = g_fopen(file, "rb")) == NULL) return NULL; | ||
| 1676 | |||
| 1677 | debug_print("Reading %s ...\n", file); | ||
| 1678 | |||
| 1679 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 1680 | p = strchr(buf, '#'); | ||
| 1681 | if (p) *p = '\0'; | ||
| 1682 | g_strstrip(buf); | ||
| 1683 | |||
| 1684 | p = buf; | ||
| 1685 | while (*p && !g_ascii_isspace(*p)) p++; | ||
| 1686 | if (*p) { | ||
| 1687 | *p = '\0'; | ||
| 1688 | p++; | ||
| 1689 | } | ||
| 1690 | delim = strchr(buf, '/'); | ||
| 1691 | if (delim == NULL) continue; | ||
| 1692 | *delim = '\0'; | ||
| 1693 | |||
| 1694 | mime_type = g_new(MimeType, 1); | ||
| 1695 | mime_type->type = g_strdup(buf); | ||
| 1696 | mime_type->sub_type = g_strdup(delim + 1); | ||
| 1697 | |||
| 1698 | while (*p && g_ascii_isspace(*p)) p++; | ||
| 1699 | if (*p) | ||
| 1700 | mime_type->extension = g_strdup(p); | ||
| 1701 | else | ||
| 1702 | mime_type->extension = NULL; | ||
| 1703 | |||
| 1704 | list = g_list_append(list, mime_type); | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | fclose(fp); | ||
| 1708 | |||
| 1709 | if (!list) | ||
| 1710 | g_warning("Can't read mime.types\n"); | ||
| 1711 | |||
| 1712 | return list; | ||
| 1713 | } | ||
| 1714 | |||
| 1715 | static GList *mailcap_list = NULL; | ||
| 1716 | |||
| 1717 | static GList *procmime_parse_mailcap(const gchar *file) | ||
| 1718 | { | ||
| 1719 | GList *list = NULL; | ||
| 1720 | FILE *fp; | ||
| 1721 | gchar buf[BUFFSIZE]; | ||
| 1722 | MailCap *mailcap; | ||
| 1723 | |||
| 1724 | if ((fp = g_fopen(file, "rb")) == NULL) return NULL; | ||
| 1725 | |||
| 1726 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 1727 | gint i; | ||
| 1728 | gchar *p; | ||
| 1729 | gchar **strv; | ||
| 1730 | |||
| 1731 | p = strchr(buf, '#'); | ||
| 1732 | if (p) *p = '\0'; | ||
| 1733 | g_strstrip(buf); | ||
| 1734 | |||
| 1735 | strv = strsplit_with_quote(buf, ";", 0); | ||
| 1736 | if (!strv) | ||
| 1737 | continue; | ||
| 1738 | |||
| 1739 | for (i = 0; strv[i] != NULL; ++i) | ||
| 1740 | g_strstrip(strv[i]); | ||
| 1741 | |||
| 1742 | if (!strv[0] || *strv[0] == '\0' || | ||
| 1743 | !strv[1] || *strv[1] == '\0') { | ||
| 1744 | g_strfreev(strv); | ||
| 1745 | continue; | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | mailcap = g_new(MailCap, 1); | ||
| 1749 | mailcap->mime_type = g_strdup(strv[0]); | ||
| 1750 | mailcap->cmdline_fmt = g_strdup(strv[1]); | ||
| 1751 | mailcap->needs_terminal = FALSE; | ||
| 1752 | |||
| 1753 | for (i = 0; strv[i] != NULL; ++i) { | ||
| 1754 | if (strcmp(strv[i], "needsterminal") == 0) | ||
| 1755 | mailcap->needs_terminal = TRUE; | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | g_strfreev(strv); | ||
| 1759 | |||
| 1760 | list = g_list_append(list, mailcap); | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | fclose(fp); | ||
| 1764 | |||
| 1765 | return list; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | gint procmime_execute_open_file(const gchar *file, const gchar *mime_type) | ||
| 1769 | { | ||
| 1770 | gchar *mime_type_ = NULL; | ||
| 1771 | GList *cur; | ||
| 1772 | MailCap *mailcap; | ||
| 1773 | gchar *cmdline; | ||
| 1774 | gint ret = -1; | ||
| 1775 | static gboolean mailcap_list_init = FALSE; | ||
| 1776 | |||
| 1777 | g_return_val_if_fail(file != NULL, -1); | ||
| 1778 | |||
| 1779 | if (!mime_type || | ||
| 1780 | g_ascii_strcasecmp(mime_type, "application/octet-stream") == 0) { | ||
| 1781 | gchar *tmp; | ||
| 1782 | tmp = procmime_get_mime_type(file); | ||
| 1783 | if (!tmp) | ||
| 1784 | return -1; | ||
| 1785 | mime_type_ = g_ascii_strdown(tmp, -1); | ||
| 1786 | g_free(tmp); | ||
| 1787 | } else | ||
| 1788 | mime_type_ = g_ascii_strdown(mime_type, -1); | ||
| 1789 | |||
| 1790 | if (!mailcap_list_init && !mailcap_list) { | ||
| 1791 | GList *list; | ||
| 1792 | gchar *path; | ||
| 1793 | |||
| 1794 | path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "mailcap", | ||
| 1795 | NULL); | ||
| 1796 | mailcap_list = procmime_parse_mailcap(path); | ||
| 1797 | g_free(path); | ||
| 1798 | #ifdef G_OS_WIN32 | ||
| 1799 | path = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S "etc" | ||
| 1800 | G_DIR_SEPARATOR_S "mailcap", NULL); | ||
| 1801 | list = procmime_parse_mailcap(path); | ||
| 1802 | g_free(path); | ||
| 1803 | #else | ||
| 1804 | if (!mailcap_list) { | ||
| 1805 | path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 1806 | ".mailcap", NULL); | ||
| 1807 | mailcap_list = procmime_parse_mailcap(path); | ||
| 1808 | g_free(path); | ||
| 1809 | } | ||
| 1810 | list = procmime_parse_mailcap(SYSCONFDIR "/mailcap"); | ||
| 1811 | if (!list) | ||
| 1812 | list = procmime_parse_mailcap("/etc/mailcap"); | ||
| 1813 | #endif | ||
| 1814 | mailcap_list = g_list_concat(mailcap_list, list); | ||
| 1815 | |||
| 1816 | mailcap_list_init = TRUE; | ||
| 1817 | } | ||
| 1818 | |||
| 1819 | for (cur = mailcap_list; cur != NULL; cur = cur->next) { | ||
| 1820 | mailcap = (MailCap *)cur->data; | ||
| 1821 | |||
| 1822 | if (!g_pattern_match_simple(mailcap->mime_type, mime_type_)) | ||
| 1823 | continue; | ||
| 1824 | if (mailcap->needs_terminal) | ||
| 1825 | continue; | ||
| 1826 | |||
| 1827 | if (str_find_format_times(mailcap->cmdline_fmt, 's') == 1) | ||
| 1828 | cmdline = g_strdup_printf(mailcap->cmdline_fmt, file); | ||
| 1829 | else | ||
| 1830 | cmdline = g_strconcat(mailcap->cmdline_fmt, " \"", file, | ||
| 1831 | "\"", NULL); | ||
| 1832 | ret = execute_command_line(cmdline, TRUE); | ||
| 1833 | g_free(cmdline); | ||
| 1834 | break; | ||
| 1835 | } | ||
| 1836 | |||
| 1837 | g_free(mime_type_); | ||
| 1838 | |||
| 1839 | return ret; | ||
| 1840 | } | ||
| 1841 | |||
| 1842 | EncodingType procmime_get_encoding_for_charset(const gchar *charset) | ||
| 1843 | { | ||
| 1844 | if (!charset) | ||
| 1845 | return ENC_8BIT; | ||
| 1846 | else if (!g_ascii_strncasecmp(charset, "ISO-2022-", 9) || | ||
| 1847 | !g_ascii_strcasecmp(charset, "US-ASCII")) | ||
| 1848 | return ENC_7BIT; | ||
| 1849 | else if (!g_ascii_strcasecmp(charset, "ISO-8859-5") || | ||
| 1850 | !g_ascii_strncasecmp(charset, "KOI8-", 5) || | ||
| 1851 | !g_ascii_strcasecmp(charset, "Windows-1251")) | ||
| 1852 | return ENC_8BIT; | ||
| 1853 | else if (!g_ascii_strncasecmp(charset, "ISO-8859-", 9)) | ||
| 1854 | return ENC_QUOTED_PRINTABLE; | ||
| 1855 | else | ||
| 1856 | return ENC_8BIT; | ||
| 1857 | } | ||
| 1858 | |||
| 1859 | EncodingType procmime_get_encoding_for_text_file(const gchar *file) | ||
| 1860 | { | ||
| 1861 | FILE *fp; | ||
| 1862 | guchar buf[BUFFSIZE]; | ||
| 1863 | size_t len; | ||
| 1864 | size_t octet_chars = 0; | ||
| 1865 | size_t total_len = 0; | ||
| 1866 | gfloat octet_percentage; | ||
| 1867 | |||
| 1868 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 1869 | FILE_OP_ERROR(file, "fopen"); | ||
| 1870 | return ENC_UNKNOWN; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | while ((len = fread(buf, sizeof(guchar), sizeof(buf), fp)) > 0) { | ||
| 1874 | guchar *p; | ||
| 1875 | gint i; | ||
| 1876 | |||
| 1877 | for (p = buf, i = 0; i < len; ++p, ++i) { | ||
| 1878 | if ((*p & 0x80) || *p == 0x00 || *p == 0x7f) { | ||
| 1879 | ++octet_chars; | ||
| 1880 | } | ||
| 1881 | } | ||
| 1882 | total_len += len; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | fclose(fp); | ||
| 1886 | |||
| 1887 | if (total_len > 0) | ||
| 1888 | octet_percentage = (gfloat)octet_chars / (gfloat)total_len; | ||
| 1889 | else | ||
| 1890 | octet_percentage = 0.0; | ||
| 1891 | |||
| 1892 | debug_print("procmime_get_encoding_for_text_file(): " | ||
| 1893 | "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len, | ||
| 1894 | 100.0 * octet_percentage); | ||
| 1895 | |||
| 1896 | if (octet_percentage > 0.20) { | ||
| 1897 | debug_print("using BASE64\n"); | ||
| 1898 | return ENC_BASE64; | ||
| 1899 | } else if (octet_chars > 0) { | ||
| 1900 | debug_print("using quoted-printable\n"); | ||
| 1901 | return ENC_QUOTED_PRINTABLE; | ||
| 1902 | } else { | ||
| 1903 | debug_print("using 7bit\n"); | ||
| 1904 | return ENC_7BIT; | ||
| 1905 | } | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | EncodingType procmime_get_encoding_for_str(const gchar *str) | ||
| 1909 | { | ||
| 1910 | const guchar *p; | ||
| 1911 | size_t octet_chars = 0; | ||
| 1912 | size_t total_len = 0; | ||
| 1913 | gfloat octet_percentage; | ||
| 1914 | |||
| 1915 | total_len = strlen(str); | ||
| 1916 | |||
| 1917 | for (p = (const guchar *)str; *p != '\0'; ++p) { | ||
| 1918 | if ((*p & 0x80) || *p == 0x7f) { | ||
| 1919 | ++octet_chars; | ||
| 1920 | } | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | if (total_len > 0) | ||
| 1924 | octet_percentage = (gfloat)octet_chars / (gfloat)total_len; | ||
| 1925 | else | ||
| 1926 | octet_percentage = 0.0; | ||
| 1927 | |||
| 1928 | debug_print("procmime_get_encoding_for_str(): " | ||
| 1929 | "8bit chars: %d / %d (%f%%)\n", octet_chars, total_len, | ||
| 1930 | 100.0 * octet_percentage); | ||
| 1931 | |||
| 1932 | if (octet_percentage > 0.20) { | ||
| 1933 | debug_print("using BASE64\n"); | ||
| 1934 | return ENC_BASE64; | ||
| 1935 | } else if (octet_chars > 0) { | ||
| 1936 | debug_print("using quoted-printable\n"); | ||
| 1937 | return ENC_QUOTED_PRINTABLE; | ||
| 1938 | } else { | ||
| 1939 | debug_print("using 7bit\n"); | ||
| 1940 | return ENC_7BIT; | ||
| 1941 | } | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | const gchar *procmime_get_encoding_str(EncodingType encoding) | ||
| 1945 | { | ||
| 1946 | static const gchar *encoding_str[] = { | ||
| 1947 | "7bit", "8bit", "quoted-printable", "base64", "x-uuencode", | ||
| 1948 | NULL | ||
| 1949 | }; | ||
| 1950 | |||
| 1951 | if (encoding >= ENC_7BIT && encoding <= ENC_UNKNOWN) | ||
| 1952 | return encoding_str[encoding]; | ||
| 1953 | else | ||
| 1954 | return NULL; | ||
| 1955 | } | ||
diff --git a/libsylph/procmime.h b/libsylph/procmime.h new file mode 100644 index 0000000..0242d34 --- /dev/null +++ b/libsylph/procmime.h | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2010 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PROCMIME_H__ | ||
| 21 | #define __PROCMIME_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | |||
| 30 | typedef struct _MimeType MimeType; | ||
| 31 | typedef struct _MailCap MailCap; | ||
| 32 | typedef struct _MimeInfo MimeInfo; | ||
| 33 | |||
| 34 | #include "procmsg.h" | ||
| 35 | #include "utils.h" | ||
| 36 | |||
| 37 | typedef enum | ||
| 38 | { | ||
| 39 | ENC_7BIT, | ||
| 40 | ENC_8BIT, | ||
| 41 | ENC_QUOTED_PRINTABLE, | ||
| 42 | ENC_BASE64, | ||
| 43 | ENC_X_UUENCODE, | ||
| 44 | ENC_UNKNOWN | ||
| 45 | } EncodingType; | ||
| 46 | |||
| 47 | typedef enum | ||
| 48 | { | ||
| 49 | MIME_TEXT, | ||
| 50 | MIME_TEXT_HTML, | ||
| 51 | MIME_MESSAGE_RFC822, | ||
| 52 | MIME_APPLICATION, | ||
| 53 | MIME_APPLICATION_OCTET_STREAM, | ||
| 54 | MIME_MULTIPART, | ||
| 55 | MIME_IMAGE, | ||
| 56 | MIME_AUDIO, | ||
| 57 | MIME_VIDEO, | ||
| 58 | MIME_UNKNOWN | ||
| 59 | } ContentType; | ||
| 60 | |||
| 61 | struct _MimeType | ||
| 62 | { | ||
| 63 | gchar *type; | ||
| 64 | gchar *sub_type; | ||
| 65 | |||
| 66 | gchar *extension; | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct _MailCap | ||
| 70 | { | ||
| 71 | gchar *mime_type; | ||
| 72 | gchar *cmdline_fmt; | ||
| 73 | gboolean needs_terminal; | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* | ||
| 77 | * An example of MimeInfo structure: | ||
| 78 | * | ||
| 79 | * multipart/mixed root <-+ parent | ||
| 80 | * | | ||
| 81 | * multipart/alternative children <-+ parent | ||
| 82 | * | | ||
| 83 | * text/plain children --+ | ||
| 84 | * | | ||
| 85 | * text/html next <-+ | ||
| 86 | * | ||
| 87 | * message/rfc822 next <-+ main | ||
| 88 | * | | ||
| 89 | * sub (capsulated message) | ||
| 90 | * | ||
| 91 | * image/jpeg next | ||
| 92 | */ | ||
| 93 | |||
| 94 | struct _MimeInfo | ||
| 95 | { | ||
| 96 | gchar *encoding; | ||
| 97 | |||
| 98 | EncodingType encoding_type; | ||
| 99 | ContentType mime_type; | ||
| 100 | |||
| 101 | gchar *content_type; | ||
| 102 | gchar *charset; | ||
| 103 | gchar *name; | ||
| 104 | gchar *boundary; | ||
| 105 | |||
| 106 | gchar *content_disposition; | ||
| 107 | gchar *filename; | ||
| 108 | |||
| 109 | glong fpos; | ||
| 110 | guint size; | ||
| 111 | guint content_size; | ||
| 112 | |||
| 113 | MimeInfo *main; | ||
| 114 | MimeInfo *sub; | ||
| 115 | |||
| 116 | MimeInfo *next; | ||
| 117 | MimeInfo *parent; | ||
| 118 | MimeInfo *children; | ||
| 119 | |||
| 120 | MimeInfo *plaintext; | ||
| 121 | gchar *sigstatus; | ||
| 122 | gchar *sigstatus_full; | ||
| 123 | |||
| 124 | gint level; | ||
| 125 | }; | ||
| 126 | |||
| 127 | #define IS_BOUNDARY(s, bnd, len) \ | ||
| 128 | (bnd && s[0] == '-' && s[1] == '-' && !strncmp(s + 2, bnd, len)) | ||
| 129 | |||
| 130 | /* MimeInfo handling */ | ||
| 131 | |||
| 132 | MimeInfo *procmime_mimeinfo_new (void); | ||
| 133 | void procmime_mimeinfo_free_all (MimeInfo *mimeinfo); | ||
| 134 | |||
| 135 | MimeInfo *procmime_mimeinfo_insert (MimeInfo *parent, | ||
| 136 | MimeInfo *mimeinfo); | ||
| 137 | #if 0 | ||
| 138 | void procmime_mimeinfo_replace (MimeInfo *old, | ||
| 139 | MimeInfo *new); | ||
| 140 | #endif | ||
| 141 | |||
| 142 | MimeInfo *procmime_mimeinfo_next (MimeInfo *mimeinfo); | ||
| 143 | |||
| 144 | MimeInfo *procmime_scan_message (MsgInfo *msginfo); | ||
| 145 | MimeInfo *procmime_scan_message_stream (FILE *fp); | ||
| 146 | void procmime_scan_multipart_message (MimeInfo *mimeinfo, | ||
| 147 | FILE *fp); | ||
| 148 | |||
| 149 | /* scan headers */ | ||
| 150 | |||
| 151 | void procmime_scan_encoding (MimeInfo *mimeinfo, | ||
| 152 | const gchar *encoding); | ||
| 153 | void procmime_scan_content_type (MimeInfo *mimeinfo, | ||
| 154 | const gchar *content_type); | ||
| 155 | void procmime_scan_content_type_str (const gchar *content_type, | ||
| 156 | gchar **mime_type, | ||
| 157 | gchar **charset, | ||
| 158 | gchar **name, | ||
| 159 | gchar **boundary); | ||
| 160 | void procmime_scan_content_type_partial (const gchar *content_type, | ||
| 161 | gint *total, | ||
| 162 | gchar **part_id, | ||
| 163 | gint *number); | ||
| 164 | void procmime_scan_content_disposition (MimeInfo *mimeinfo, | ||
| 165 | const gchar *content_disposition); | ||
| 166 | MimeInfo *procmime_scan_mime_header (FILE *fp); | ||
| 167 | |||
| 168 | FILE *procmime_decode_content (FILE *outfp, | ||
| 169 | FILE *infp, | ||
| 170 | MimeInfo *mimeinfo); | ||
| 171 | gint procmime_get_part (const gchar *outfile, | ||
| 172 | const gchar *infile, | ||
| 173 | MimeInfo *mimeinfo); | ||
| 174 | gint procmime_get_part_fp (const gchar *outfile, | ||
| 175 | FILE *infp, | ||
| 176 | MimeInfo *mimeinfo); | ||
| 177 | FILE *procmime_get_part_fp_fp (FILE *outfp, | ||
| 178 | FILE *infp, | ||
| 179 | MimeInfo *mimeinfo); | ||
| 180 | gint procmime_get_all_parts (const gchar *dir, | ||
| 181 | const gchar *infile, | ||
| 182 | MimeInfo *mimeinfo); | ||
| 183 | FILE *procmime_get_text_content (MimeInfo *mimeinfo, | ||
| 184 | FILE *infp, | ||
| 185 | const gchar *encoding); | ||
| 186 | FILE *procmime_get_first_text_content (MsgInfo *msginfo, | ||
| 187 | const gchar *encoding); | ||
| 188 | |||
| 189 | gboolean procmime_find_string_part (MimeInfo *mimeinfo, | ||
| 190 | const gchar *filename, | ||
| 191 | const gchar *str, | ||
| 192 | StrFindFunc find_func); | ||
| 193 | gboolean procmime_find_string (MsgInfo *msginfo, | ||
| 194 | const gchar *str, | ||
| 195 | StrFindFunc find_func); | ||
| 196 | |||
| 197 | gchar *procmime_get_part_file_name (MimeInfo *mimeinfo); | ||
| 198 | gchar *procmime_get_tmp_file_name (MimeInfo *mimeinfo); | ||
| 199 | gchar *procmime_get_tmp_file_name_for_user | ||
| 200 | (MimeInfo *mimeinfo); | ||
| 201 | |||
| 202 | ContentType procmime_scan_mime_type (const gchar *mime_type); | ||
| 203 | gchar *procmime_get_mime_type (const gchar *filename); | ||
| 204 | |||
| 205 | gint procmime_execute_open_file (const gchar *file, | ||
| 206 | const gchar *mime_type); | ||
| 207 | |||
| 208 | EncodingType procmime_get_encoding_for_charset (const gchar *charset); | ||
| 209 | EncodingType procmime_get_encoding_for_text_file(const gchar *file); | ||
| 210 | EncodingType procmime_get_encoding_for_str (const gchar *str); | ||
| 211 | const gchar *procmime_get_encoding_str (EncodingType encoding); | ||
| 212 | |||
| 213 | #endif /* __PROCMIME_H__ */ | ||
diff --git a/libsylph/procmsg.c b/libsylph/procmsg.c new file mode 100644 index 0000000..28d16f3 --- /dev/null +++ b/libsylph/procmsg.c | |||
| @@ -0,0 +1,2446 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2013 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "defs.h" | ||
| 21 | |||
| 22 | #include <glib.h> | ||
| 23 | #include <glib/gi18n.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | #include <stdlib.h> | ||
| 26 | #include <errno.h> | ||
| 27 | |||
| 28 | #include "utils.h" | ||
| 29 | #include "procmsg.h" | ||
| 30 | #include "procheader.h" | ||
| 31 | #include "account.h" | ||
| 32 | #include "procmime.h" | ||
| 33 | #include "prefs_common.h" | ||
| 34 | #include "folder.h" | ||
| 35 | #include "codeconv.h" | ||
| 36 | |||
| 37 | typedef struct _MsgFlagInfo { | ||
| 38 | guint msgnum; | ||
| 39 | MsgFlags flags; | ||
| 40 | } MsgFlagInfo; | ||
| 41 | |||
| 42 | static GSList *procmsg_read_cache_queue (FolderItem *item, | ||
| 43 | gboolean scan_file); | ||
| 44 | |||
| 45 | static void mark_sum_func (gpointer key, | ||
| 46 | gpointer value, | ||
| 47 | gpointer data); | ||
| 48 | |||
| 49 | static GHashTable *procmsg_read_mark_file (FolderItem *item); | ||
| 50 | static void procmsg_write_mark_file (FolderItem *item, | ||
| 51 | GHashTable *mark_table); | ||
| 52 | |||
| 53 | static GMappedFile *procmsg_open_cache_file_mmap(FolderItem *item, | ||
| 54 | DataOpenMode mode); | ||
| 55 | |||
| 56 | static gint procmsg_cmp_by_mark (gconstpointer a, | ||
| 57 | gconstpointer b); | ||
| 58 | static gint procmsg_cmp_by_unread (gconstpointer a, | ||
| 59 | gconstpointer b); | ||
| 60 | static gint procmsg_cmp_by_mime (gconstpointer a, | ||
| 61 | gconstpointer b); | ||
| 62 | static gint procmsg_cmp_by_label (gconstpointer a, | ||
| 63 | gconstpointer b); | ||
| 64 | static gint procmsg_cmp_by_number (gconstpointer a, | ||
| 65 | gconstpointer b); | ||
| 66 | static gint procmsg_cmp_by_size (gconstpointer a, | ||
| 67 | gconstpointer b); | ||
| 68 | static gint procmsg_cmp_by_date (gconstpointer a, | ||
| 69 | gconstpointer b); | ||
| 70 | static gint procmsg_cmp_by_from (gconstpointer a, | ||
| 71 | gconstpointer b); | ||
| 72 | static gint procmsg_cmp_by_to (gconstpointer a, | ||
| 73 | gconstpointer b); | ||
| 74 | static gint procmsg_cmp_by_subject (gconstpointer a, | ||
| 75 | gconstpointer b); | ||
| 76 | |||
| 77 | |||
| 78 | GHashTable *procmsg_msg_hash_table_create(GSList *mlist) | ||
| 79 | { | ||
| 80 | GHashTable *msg_table; | ||
| 81 | |||
| 82 | if (mlist == NULL) return NULL; | ||
| 83 | |||
| 84 | msg_table = g_hash_table_new(NULL, g_direct_equal); | ||
| 85 | procmsg_msg_hash_table_append(msg_table, mlist); | ||
| 86 | |||
| 87 | return msg_table; | ||
| 88 | } | ||
| 89 | |||
| 90 | void procmsg_msg_hash_table_append(GHashTable *msg_table, GSList *mlist) | ||
| 91 | { | ||
| 92 | GSList *cur; | ||
| 93 | MsgInfo *msginfo; | ||
| 94 | |||
| 95 | if (msg_table == NULL || mlist == NULL) return; | ||
| 96 | |||
| 97 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 98 | msginfo = (MsgInfo *)cur->data; | ||
| 99 | |||
| 100 | g_hash_table_insert(msg_table, | ||
| 101 | GUINT_TO_POINTER(msginfo->msgnum), | ||
| 102 | msginfo); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | GHashTable *procmsg_to_folder_hash_table_create(GSList *mlist) | ||
| 107 | { | ||
| 108 | GHashTable *msg_table; | ||
| 109 | GSList *cur; | ||
| 110 | MsgInfo *msginfo; | ||
| 111 | |||
| 112 | if (mlist == NULL) return NULL; | ||
| 113 | |||
| 114 | msg_table = g_hash_table_new(NULL, g_direct_equal); | ||
| 115 | |||
| 116 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 117 | msginfo = (MsgInfo *)cur->data; | ||
| 118 | g_hash_table_insert(msg_table, msginfo->to_folder, msginfo); | ||
| 119 | } | ||
| 120 | |||
| 121 | return msg_table; | ||
| 122 | } | ||
| 123 | |||
| 124 | gint procmsg_read_cache_data_str(FILE *fp, gchar **str) | ||
| 125 | { | ||
| 126 | gchar buf[BUFFSIZE]; | ||
| 127 | guint32 len; | ||
| 128 | gchar *tmp = NULL; | ||
| 129 | |||
| 130 | if (fread(&len, sizeof(len), 1, fp) != 1) | ||
| 131 | return -1; | ||
| 132 | |||
| 133 | if (len > G_MAXINT) | ||
| 134 | return -1; | ||
| 135 | |||
| 136 | while (len > 0) { | ||
| 137 | size_t size = MIN(len, BUFFSIZE - 1); | ||
| 138 | |||
| 139 | if (fread(buf, size, 1, fp) != 1) { | ||
| 140 | if (tmp) | ||
| 141 | g_free(tmp); | ||
| 142 | *str = NULL; | ||
| 143 | return -1; | ||
| 144 | } | ||
| 145 | |||
| 146 | buf[size] = '\0'; | ||
| 147 | if (tmp) { | ||
| 148 | *str = g_strconcat(tmp, buf, NULL); | ||
| 149 | g_free(tmp); | ||
| 150 | tmp = *str; | ||
| 151 | } else | ||
| 152 | tmp = *str = g_strdup(buf); | ||
| 153 | |||
| 154 | len -= size; | ||
| 155 | } | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | static gint procmsg_read_cache_data_str_mem(const gchar **p, const gchar *endp, gchar **str) | ||
| 161 | { | ||
| 162 | guint32 len; | ||
| 163 | |||
| 164 | if (endp - *p < sizeof(len)) | ||
| 165 | return -1; | ||
| 166 | |||
| 167 | memcpy(&len, *p, sizeof(len)); | ||
| 168 | *p += sizeof(len); | ||
| 169 | if (len > G_MAXINT || len > endp - *p) | ||
| 170 | return -1; | ||
| 171 | |||
| 172 | if (len > 0) { | ||
| 173 | *str = g_strndup(*p, len); | ||
| 174 | *p += len; | ||
| 175 | } | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | #define READ_CACHE_DATA(data) \ | ||
| 181 | { \ | ||
| 182 | if (procmsg_read_cache_data_str_mem(&p, endp, &data) < 0) { \ | ||
| 183 | g_warning("Cache data is corrupted\n"); \ | ||
| 184 | procmsg_msginfo_free(msginfo); \ | ||
| 185 | procmsg_msg_list_free(mlist); \ | ||
| 186 | g_mapped_file_free(mapfile); \ | ||
| 187 | return NULL; \ | ||
| 188 | } \ | ||
| 189 | } | ||
| 190 | |||
| 191 | #define READ_CACHE_DATA_INT(n) \ | ||
| 192 | { \ | ||
| 193 | if (endp - p < sizeof(guint32)) { \ | ||
| 194 | g_warning("Cache data is corrupted\n"); \ | ||
| 195 | procmsg_msginfo_free(msginfo); \ | ||
| 196 | procmsg_msg_list_free(mlist); \ | ||
| 197 | g_mapped_file_free(mapfile); \ | ||
| 198 | return NULL; \ | ||
| 199 | } else { \ | ||
| 200 | guint32 idata; \ | ||
| 201 | memcpy(&idata, p, sizeof(idata)); \ | ||
| 202 | n = idata; \ | ||
| 203 | p += sizeof(guint32); \ | ||
| 204 | } \ | ||
| 205 | } | ||
| 206 | |||
| 207 | GSList *procmsg_read_cache(FolderItem *item, gboolean scan_file) | ||
| 208 | { | ||
| 209 | GSList *mlist = NULL; | ||
| 210 | GSList *last = NULL; | ||
| 211 | GMappedFile *mapfile; | ||
| 212 | const gchar *filep; | ||
| 213 | gsize file_len; | ||
| 214 | const gchar *p, *endp; | ||
| 215 | MsgInfo *msginfo; | ||
| 216 | MsgFlags default_flags; | ||
| 217 | guint32 num; | ||
| 218 | guint refnum; | ||
| 219 | FolderType type; | ||
| 220 | |||
| 221 | g_return_val_if_fail(item != NULL, NULL); | ||
| 222 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 223 | type = FOLDER_TYPE(item->folder); | ||
| 224 | |||
| 225 | default_flags.perm_flags = MSG_NEW|MSG_UNREAD; | ||
| 226 | default_flags.tmp_flags = 0; | ||
| 227 | if (type == F_MH || type == F_IMAP) { | ||
| 228 | if (item->stype == F_QUEUE) { | ||
| 229 | MSG_SET_TMP_FLAGS(default_flags, MSG_QUEUED); | ||
| 230 | } else if (item->stype == F_DRAFT) { | ||
| 231 | MSG_SET_TMP_FLAGS(default_flags, MSG_DRAFT); | ||
| 232 | } | ||
| 233 | } | ||
| 234 | if (type == F_IMAP) { | ||
| 235 | MSG_SET_TMP_FLAGS(default_flags, MSG_IMAP); | ||
| 236 | } else if (type == F_NEWS) { | ||
| 237 | MSG_SET_TMP_FLAGS(default_flags, MSG_NEWS); | ||
| 238 | } | ||
| 239 | |||
| 240 | if (type == F_MH) { | ||
| 241 | gchar *path; | ||
| 242 | |||
| 243 | path = folder_item_get_path(item); | ||
| 244 | if (change_dir(path) < 0) { | ||
| 245 | g_free(path); | ||
| 246 | return NULL; | ||
| 247 | } | ||
| 248 | g_free(path); | ||
| 249 | } | ||
| 250 | |||
| 251 | mapfile = procmsg_open_cache_file_mmap(item, DATA_READ); | ||
| 252 | if (!mapfile) { | ||
| 253 | item->cache_dirty = TRUE; | ||
| 254 | return NULL; | ||
| 255 | } | ||
| 256 | |||
| 257 | debug_print("Reading summary cache...\n"); | ||
| 258 | |||
| 259 | filep = g_mapped_file_get_contents(mapfile); | ||
| 260 | file_len = g_mapped_file_get_length(mapfile); | ||
| 261 | endp = filep + file_len; | ||
| 262 | p = filep + sizeof(guint32); /* version */ | ||
| 263 | |||
| 264 | while (endp - p >= sizeof(num)) { | ||
| 265 | msginfo = g_new0(MsgInfo, 1); | ||
| 266 | |||
| 267 | READ_CACHE_DATA_INT(msginfo->msgnum); | ||
| 268 | |||
| 269 | READ_CACHE_DATA_INT(msginfo->size); | ||
| 270 | READ_CACHE_DATA_INT(msginfo->mtime); | ||
| 271 | READ_CACHE_DATA_INT(msginfo->date_t); | ||
| 272 | READ_CACHE_DATA_INT(msginfo->flags.tmp_flags); | ||
| 273 | |||
| 274 | READ_CACHE_DATA(msginfo->fromname); | ||
| 275 | |||
| 276 | READ_CACHE_DATA(msginfo->date); | ||
| 277 | READ_CACHE_DATA(msginfo->from); | ||
| 278 | READ_CACHE_DATA(msginfo->to); | ||
| 279 | READ_CACHE_DATA(msginfo->newsgroups); | ||
| 280 | READ_CACHE_DATA(msginfo->subject); | ||
| 281 | READ_CACHE_DATA(msginfo->msgid); | ||
| 282 | READ_CACHE_DATA(msginfo->inreplyto); | ||
| 283 | |||
| 284 | READ_CACHE_DATA_INT(refnum); | ||
| 285 | for (; refnum != 0; refnum--) { | ||
| 286 | gchar *ref; | ||
| 287 | |||
| 288 | READ_CACHE_DATA(ref); | ||
| 289 | msginfo->references = | ||
| 290 | g_slist_prepend(msginfo->references, ref); | ||
| 291 | } | ||
| 292 | if (msginfo->references) | ||
| 293 | msginfo->references = | ||
| 294 | g_slist_reverse(msginfo->references); | ||
| 295 | |||
| 296 | MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags); | ||
| 297 | MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags); | ||
| 298 | |||
| 299 | /* if the message file doesn't exist or is changed, | ||
| 300 | don't add the data */ | ||
| 301 | if ((type == F_MH && scan_file && | ||
| 302 | folder_item_is_msg_changed(item, msginfo)) || | ||
| 303 | msginfo->msgnum == 0) { | ||
| 304 | procmsg_msginfo_free(msginfo); | ||
| 305 | item->cache_dirty = TRUE; | ||
| 306 | } else { | ||
| 307 | msginfo->folder = item; | ||
| 308 | |||
| 309 | if (!mlist) | ||
| 310 | last = mlist = g_slist_append(NULL, msginfo); | ||
| 311 | else { | ||
| 312 | last = g_slist_append(last, msginfo); | ||
| 313 | last = last->next; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | g_mapped_file_free(mapfile); | ||
| 319 | |||
| 320 | if (item->cache_queue) { | ||
| 321 | GSList *qlist; | ||
| 322 | qlist = procmsg_read_cache_queue(item, scan_file); | ||
| 323 | mlist = g_slist_concat(mlist, qlist); | ||
| 324 | } | ||
| 325 | |||
| 326 | debug_print("done.\n"); | ||
| 327 | |||
| 328 | return mlist; | ||
| 329 | } | ||
| 330 | |||
| 331 | #undef READ_CACHE_DATA | ||
| 332 | #undef READ_CACHE_DATA_INT | ||
| 333 | |||
| 334 | static GSList *procmsg_read_cache_queue(FolderItem *item, gboolean scan_file) | ||
| 335 | { | ||
| 336 | FolderType type; | ||
| 337 | MsgInfo *msginfo; | ||
| 338 | MsgFlags default_flags; | ||
| 339 | GSList *cur; | ||
| 340 | GSList *qlist = NULL; | ||
| 341 | |||
| 342 | g_return_val_if_fail(item != NULL, NULL); | ||
| 343 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 344 | |||
| 345 | if (!item->cache_queue) | ||
| 346 | return NULL; | ||
| 347 | |||
| 348 | debug_print("Reading cache queue...\n"); | ||
| 349 | |||
| 350 | type = FOLDER_TYPE(item->folder); | ||
| 351 | default_flags.perm_flags = MSG_NEW|MSG_UNREAD; | ||
| 352 | default_flags.tmp_flags = 0; | ||
| 353 | |||
| 354 | for (cur = item->cache_queue; cur != NULL; cur = cur->next) { | ||
| 355 | msginfo = (MsgInfo *)cur->data; | ||
| 356 | |||
| 357 | debug_print("read cache queue: %s/%d\n", | ||
| 358 | item->path, msginfo->msgnum); | ||
| 359 | |||
| 360 | MSG_SET_PERM_FLAGS(msginfo->flags, default_flags.perm_flags); | ||
| 361 | MSG_SET_TMP_FLAGS(msginfo->flags, default_flags.tmp_flags); | ||
| 362 | |||
| 363 | if ((type == F_MH && scan_file && | ||
| 364 | folder_item_is_msg_changed(item, msginfo))) { | ||
| 365 | procmsg_msginfo_free(msginfo); | ||
| 366 | item->cache_dirty = TRUE; | ||
| 367 | } else { | ||
| 368 | msginfo->folder = item; | ||
| 369 | qlist = g_slist_prepend(qlist, msginfo); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | g_slist_free(item->cache_queue); | ||
| 374 | item->cache_queue = NULL; | ||
| 375 | item->cache_dirty = TRUE; | ||
| 376 | |||
| 377 | return qlist; | ||
| 378 | } | ||
| 379 | |||
| 380 | static void mark_unset_new_func(gpointer key, gpointer value, gpointer data) | ||
| 381 | { | ||
| 382 | MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW); | ||
| 383 | } | ||
| 384 | |||
| 385 | void procmsg_set_flags(GSList *mlist, FolderItem *item) | ||
| 386 | { | ||
| 387 | GSList *cur; | ||
| 388 | gint new = 0, unread = 0, total = 0; | ||
| 389 | gint lastnum = 0; | ||
| 390 | gint unflagged = 0; | ||
| 391 | gboolean mark_queue_exist; | ||
| 392 | MsgInfo *msginfo; | ||
| 393 | GHashTable *mark_table; | ||
| 394 | MsgFlags *flags; | ||
| 395 | |||
| 396 | g_return_if_fail(item != NULL); | ||
| 397 | g_return_if_fail(item->folder != NULL); | ||
| 398 | |||
| 399 | debug_print("Marking the messages...\n"); | ||
| 400 | |||
| 401 | mark_queue_exist = (item->mark_queue != NULL); | ||
| 402 | mark_table = procmsg_read_mark_file(item); | ||
| 403 | if (!mark_table) { | ||
| 404 | item->new = item->unread = item->total = g_slist_length(mlist); | ||
| 405 | item->updated = TRUE; | ||
| 406 | item->mark_dirty = TRUE; | ||
| 407 | return; | ||
| 408 | } | ||
| 409 | |||
| 410 | /* unset new flags if new (unflagged) messages exist */ | ||
| 411 | if (!mark_queue_exist) { | ||
| 412 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 413 | msginfo = (MsgInfo *)cur->data; | ||
| 414 | flags = g_hash_table_lookup | ||
| 415 | (mark_table, GUINT_TO_POINTER(msginfo->msgnum)); | ||
| 416 | if (!flags) { | ||
| 417 | g_hash_table_foreach(mark_table, | ||
| 418 | mark_unset_new_func, NULL); | ||
| 419 | item->mark_dirty = TRUE; | ||
| 420 | break; | ||
| 421 | } | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 426 | msginfo = (MsgInfo *)cur->data; | ||
| 427 | |||
| 428 | if (lastnum < msginfo->msgnum) | ||
| 429 | lastnum = msginfo->msgnum; | ||
| 430 | |||
| 431 | flags = g_hash_table_lookup | ||
| 432 | (mark_table, GUINT_TO_POINTER(msginfo->msgnum)); | ||
| 433 | |||
| 434 | if (flags != NULL) { | ||
| 435 | /* add the permanent flags only */ | ||
| 436 | msginfo->flags.perm_flags = flags->perm_flags; | ||
| 437 | if (MSG_IS_NEW(*flags)) | ||
| 438 | ++new; | ||
| 439 | if (MSG_IS_UNREAD(*flags)) | ||
| 440 | ++unread; | ||
| 441 | if (FOLDER_TYPE(item->folder) == F_IMAP) { | ||
| 442 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP); | ||
| 443 | } else if (FOLDER_TYPE(item->folder) == F_NEWS) { | ||
| 444 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS); | ||
| 445 | } | ||
| 446 | } else { | ||
| 447 | ++unflagged; | ||
| 448 | ++new; | ||
| 449 | ++unread; | ||
| 450 | } | ||
| 451 | |||
| 452 | ++total; | ||
| 453 | } | ||
| 454 | |||
| 455 | item->new = new; | ||
| 456 | item->unread = unread; | ||
| 457 | item->total = total; | ||
| 458 | item->unmarked_num = unflagged; | ||
| 459 | item->last_num = lastnum; | ||
| 460 | item->updated = TRUE; | ||
| 461 | |||
| 462 | if (unflagged > 0) | ||
| 463 | item->mark_dirty = TRUE; | ||
| 464 | |||
| 465 | debug_print("new: %d unread: %d unflagged: %d total: %d\n", | ||
| 466 | new, unread, unflagged, total); | ||
| 467 | |||
| 468 | hash_free_value_mem(mark_table); | ||
| 469 | g_hash_table_destroy(mark_table); | ||
| 470 | } | ||
| 471 | |||
| 472 | static void mark_all_read_func(gpointer key, gpointer value, gpointer data) | ||
| 473 | { | ||
| 474 | MSG_UNSET_PERM_FLAGS(*((MsgFlags *)value), MSG_NEW|MSG_UNREAD); | ||
| 475 | } | ||
| 476 | |||
| 477 | void procmsg_mark_all_read(FolderItem *item) | ||
| 478 | { | ||
| 479 | GHashTable *mark_table; | ||
| 480 | |||
| 481 | debug_print("Marking all messages as read\n"); | ||
| 482 | |||
| 483 | mark_table = procmsg_read_mark_file(item); | ||
| 484 | if (mark_table) { | ||
| 485 | g_hash_table_foreach(mark_table, mark_all_read_func, NULL); | ||
| 486 | procmsg_write_mark_file(item, mark_table); | ||
| 487 | hash_free_value_mem(mark_table); | ||
| 488 | g_hash_table_destroy(mark_table); | ||
| 489 | } | ||
| 490 | |||
| 491 | if (item->mark_queue) { | ||
| 492 | GSList *cur; | ||
| 493 | MsgFlagInfo *flaginfo; | ||
| 494 | |||
| 495 | for (cur = item->mark_queue; cur != NULL; cur = cur->next) { | ||
| 496 | flaginfo = (MsgFlagInfo *)cur->data; | ||
| 497 | MSG_UNSET_PERM_FLAGS | ||
| 498 | (flaginfo->flags, MSG_NEW|MSG_UNREAD); | ||
| 499 | } | ||
| 500 | item->mark_dirty = TRUE; | ||
| 501 | } | ||
| 502 | |||
| 503 | item->new = item->unread = 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | static FolderSortType cmp_func_sort_type; | ||
| 507 | |||
| 508 | GSList *procmsg_sort_msg_list(GSList *mlist, FolderSortKey sort_key, | ||
| 509 | FolderSortType sort_type) | ||
| 510 | { | ||
| 511 | GCompareFunc cmp_func; | ||
| 512 | |||
| 513 | switch (sort_key) { | ||
| 514 | case SORT_BY_MARK: | ||
| 515 | cmp_func = procmsg_cmp_by_mark; break; | ||
| 516 | case SORT_BY_UNREAD: | ||
| 517 | cmp_func = procmsg_cmp_by_unread; break; | ||
| 518 | case SORT_BY_MIME: | ||
| 519 | cmp_func = procmsg_cmp_by_mime; break; | ||
| 520 | case SORT_BY_LABEL: | ||
| 521 | cmp_func = procmsg_cmp_by_label; break; | ||
| 522 | case SORT_BY_NUMBER: | ||
| 523 | cmp_func = procmsg_cmp_by_number; break; | ||
| 524 | case SORT_BY_SIZE: | ||
| 525 | cmp_func = procmsg_cmp_by_size; break; | ||
| 526 | case SORT_BY_DATE: | ||
| 527 | cmp_func = procmsg_cmp_by_date; break; | ||
| 528 | case SORT_BY_FROM: | ||
| 529 | cmp_func = procmsg_cmp_by_from; break; | ||
| 530 | case SORT_BY_SUBJECT: | ||
| 531 | cmp_func = procmsg_cmp_by_subject; break; | ||
| 532 | case SORT_BY_TO: | ||
| 533 | cmp_func = procmsg_cmp_by_to; break; | ||
| 534 | default: | ||
| 535 | return mlist; | ||
| 536 | } | ||
| 537 | |||
| 538 | cmp_func_sort_type = sort_type; | ||
| 539 | |||
| 540 | mlist = g_slist_sort(mlist, cmp_func); | ||
| 541 | |||
| 542 | return mlist; | ||
| 543 | } | ||
| 544 | |||
| 545 | gint procmsg_get_last_num_in_msg_list(GSList *mlist) | ||
| 546 | { | ||
| 547 | GSList *cur; | ||
| 548 | MsgInfo *msginfo; | ||
| 549 | gint last = 0; | ||
| 550 | |||
| 551 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 552 | msginfo = (MsgInfo *)cur->data; | ||
| 553 | if (msginfo && msginfo->msgnum > last) | ||
| 554 | last = msginfo->msgnum; | ||
| 555 | } | ||
| 556 | |||
| 557 | return last; | ||
| 558 | } | ||
| 559 | |||
| 560 | void procmsg_msg_list_free(GSList *mlist) | ||
| 561 | { | ||
| 562 | GSList *cur; | ||
| 563 | MsgInfo *msginfo; | ||
| 564 | |||
| 565 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 566 | msginfo = (MsgInfo *)cur->data; | ||
| 567 | procmsg_msginfo_free(msginfo); | ||
| 568 | } | ||
| 569 | g_slist_free(mlist); | ||
| 570 | } | ||
| 571 | |||
| 572 | void procmsg_write_cache(MsgInfo *msginfo, FILE *fp) | ||
| 573 | { | ||
| 574 | MsgTmpFlags flags = msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK; | ||
| 575 | GSList *cur; | ||
| 576 | |||
| 577 | WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); | ||
| 578 | WRITE_CACHE_DATA_INT(msginfo->size, fp); | ||
| 579 | WRITE_CACHE_DATA_INT(msginfo->mtime, fp); | ||
| 580 | WRITE_CACHE_DATA_INT(msginfo->date_t, fp); | ||
| 581 | WRITE_CACHE_DATA_INT(flags, fp); | ||
| 582 | |||
| 583 | WRITE_CACHE_DATA(msginfo->fromname, fp); | ||
| 584 | |||
| 585 | WRITE_CACHE_DATA(msginfo->date, fp); | ||
| 586 | WRITE_CACHE_DATA(msginfo->from, fp); | ||
| 587 | WRITE_CACHE_DATA(msginfo->to, fp); | ||
| 588 | WRITE_CACHE_DATA(msginfo->newsgroups, fp); | ||
| 589 | WRITE_CACHE_DATA(msginfo->subject, fp); | ||
| 590 | WRITE_CACHE_DATA(msginfo->msgid, fp); | ||
| 591 | WRITE_CACHE_DATA(msginfo->inreplyto, fp); | ||
| 592 | |||
| 593 | WRITE_CACHE_DATA_INT(g_slist_length(msginfo->references), fp); | ||
| 594 | for (cur = msginfo->references; cur != NULL; cur = cur->next) { | ||
| 595 | WRITE_CACHE_DATA((gchar *)cur->data, fp); | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | void procmsg_write_flags(MsgInfo *msginfo, FILE *fp) | ||
| 600 | { | ||
| 601 | MsgPermFlags flags = msginfo->flags.perm_flags; | ||
| 602 | |||
| 603 | WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); | ||
| 604 | WRITE_CACHE_DATA_INT(flags, fp); | ||
| 605 | } | ||
| 606 | |||
| 607 | void procmsg_write_cache_list(FolderItem *item, GSList *mlist) | ||
| 608 | { | ||
| 609 | FILE *fp; | ||
| 610 | GSList *cur; | ||
| 611 | |||
| 612 | g_return_if_fail(item != NULL); | ||
| 613 | |||
| 614 | debug_print("Writing summary cache (%s)\n", item->path); | ||
| 615 | |||
| 616 | fp = procmsg_open_cache_file(item, DATA_WRITE); | ||
| 617 | if (fp == NULL) | ||
| 618 | return; | ||
| 619 | |||
| 620 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 621 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 622 | procmsg_write_cache(msginfo, fp); | ||
| 623 | } | ||
| 624 | |||
| 625 | if (item->cache_queue) | ||
| 626 | procmsg_flush_cache_queue(item, fp); | ||
| 627 | |||
| 628 | fclose(fp); | ||
| 629 | item->cache_dirty = FALSE; | ||
| 630 | } | ||
| 631 | |||
| 632 | void procmsg_write_flags_list(FolderItem *item, GSList *mlist) | ||
| 633 | { | ||
| 634 | FILE *fp; | ||
| 635 | GSList *cur; | ||
| 636 | |||
| 637 | g_return_if_fail(item != NULL); | ||
| 638 | |||
| 639 | debug_print("Writing summary flags (%s)\n", item->path); | ||
| 640 | |||
| 641 | fp = procmsg_open_mark_file(item, DATA_WRITE); | ||
| 642 | if (fp == NULL) | ||
| 643 | return; | ||
| 644 | |||
| 645 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 646 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 647 | procmsg_write_flags(msginfo, fp); | ||
| 648 | } | ||
| 649 | |||
| 650 | if (item->mark_queue) | ||
| 651 | procmsg_flush_mark_queue(item, fp); | ||
| 652 | |||
| 653 | fclose(fp); | ||
| 654 | item->mark_dirty = FALSE; | ||
| 655 | } | ||
| 656 | |||
| 657 | static gint cmp_by_item(gconstpointer a, gconstpointer b) | ||
| 658 | { | ||
| 659 | const MsgInfo *msginfo1 = a; | ||
| 660 | const MsgInfo *msginfo2 = b; | ||
| 661 | |||
| 662 | if (msginfo1->folder == msginfo2->folder) | ||
| 663 | return msginfo1->msgnum - msginfo2->msgnum; | ||
| 664 | |||
| 665 | return msginfo1->folder - msginfo2->folder; | ||
| 666 | } | ||
| 667 | |||
| 668 | void procmsg_write_flags_for_multiple_folders(GSList *mlist) | ||
| 669 | { | ||
| 670 | GSList *tmp_list, *cur; | ||
| 671 | FolderItem *prev_item = NULL; | ||
| 672 | FILE *fp = NULL; | ||
| 673 | |||
| 674 | if (!mlist) | ||
| 675 | return; | ||
| 676 | |||
| 677 | tmp_list = g_slist_copy(mlist); | ||
| 678 | tmp_list = g_slist_sort(tmp_list, cmp_by_item); | ||
| 679 | |||
| 680 | for (cur = tmp_list; cur != NULL; cur = cur->next) { | ||
| 681 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 682 | FolderItem *item = msginfo->folder; | ||
| 683 | |||
| 684 | if (prev_item != item) { | ||
| 685 | if (fp) | ||
| 686 | fclose(fp); | ||
| 687 | fp = procmsg_open_mark_file(item, DATA_APPEND); | ||
| 688 | if (!fp) { | ||
| 689 | g_warning("can't open mark file\n"); | ||
| 690 | break; | ||
| 691 | } | ||
| 692 | item->updated = TRUE; | ||
| 693 | } | ||
| 694 | procmsg_write_flags(msginfo, fp); | ||
| 695 | prev_item = item; | ||
| 696 | } | ||
| 697 | |||
| 698 | if (fp) | ||
| 699 | fclose(fp); | ||
| 700 | g_slist_free(tmp_list); | ||
| 701 | } | ||
| 702 | |||
| 703 | void procmsg_flush_mark_queue(FolderItem *item, FILE *fp) | ||
| 704 | { | ||
| 705 | MsgFlagInfo *flaginfo; | ||
| 706 | MsgInfo msginfo = {0}; | ||
| 707 | gboolean append = FALSE; | ||
| 708 | GSList *qlist, *cur; | ||
| 709 | |||
| 710 | g_return_if_fail(item != NULL); | ||
| 711 | |||
| 712 | if (!item->mark_queue) | ||
| 713 | return; | ||
| 714 | |||
| 715 | debug_print("flushing mark_queue: %s ...\n", item->path); | ||
| 716 | |||
| 717 | if (!fp) { | ||
| 718 | append = TRUE; | ||
| 719 | fp = procmsg_open_mark_file(item, DATA_APPEND); | ||
| 720 | g_return_if_fail(fp != NULL); | ||
| 721 | } | ||
| 722 | |||
| 723 | qlist = g_slist_reverse(item->mark_queue); | ||
| 724 | item->mark_queue = NULL; | ||
| 725 | |||
| 726 | for (cur = qlist; cur != NULL; cur = cur->next) { | ||
| 727 | flaginfo = (MsgFlagInfo *)cur->data; | ||
| 728 | |||
| 729 | msginfo.msgnum = flaginfo->msgnum; | ||
| 730 | msginfo.flags = flaginfo->flags; | ||
| 731 | procmsg_write_flags(&msginfo, fp); | ||
| 732 | g_free(flaginfo); | ||
| 733 | } | ||
| 734 | |||
| 735 | g_slist_free(qlist); | ||
| 736 | |||
| 737 | if (append) | ||
| 738 | fclose(fp); | ||
| 739 | } | ||
| 740 | |||
| 741 | void procmsg_add_mark_queue(FolderItem *item, gint num, MsgFlags flags) | ||
| 742 | { | ||
| 743 | MsgFlagInfo *flaginfo; | ||
| 744 | |||
| 745 | flaginfo = g_new(MsgFlagInfo, 1); | ||
| 746 | flaginfo->msgnum = num; | ||
| 747 | flaginfo->flags = flags; | ||
| 748 | item->mark_queue = g_slist_prepend(item->mark_queue, flaginfo); | ||
| 749 | } | ||
| 750 | |||
| 751 | void procmsg_flaginfo_list_free(GSList *flaglist) | ||
| 752 | { | ||
| 753 | GSList *cur; | ||
| 754 | MsgFlagInfo *flaginfo; | ||
| 755 | |||
| 756 | for (cur = flaglist; cur != NULL; cur = cur->next) { | ||
| 757 | flaginfo = (MsgFlagInfo *)cur->data; | ||
| 758 | g_free(flaginfo); | ||
| 759 | } | ||
| 760 | g_slist_free(flaglist); | ||
| 761 | } | ||
| 762 | |||
| 763 | void procmsg_flush_cache_queue(FolderItem *item, FILE *fp) | ||
| 764 | { | ||
| 765 | MsgInfo *msginfo; | ||
| 766 | gboolean append = FALSE; | ||
| 767 | GSList *qlist, *cur; | ||
| 768 | |||
| 769 | g_return_if_fail(item != NULL); | ||
| 770 | |||
| 771 | if (!item->cache_queue) | ||
| 772 | return; | ||
| 773 | |||
| 774 | debug_print("flushing cache_queue: %s ...\n", item->path); | ||
| 775 | |||
| 776 | if (!fp) { | ||
| 777 | append = TRUE; | ||
| 778 | fp = procmsg_open_cache_file(item, DATA_APPEND); | ||
| 779 | g_return_if_fail(fp != NULL); | ||
| 780 | } | ||
| 781 | |||
| 782 | qlist = g_slist_reverse(item->cache_queue); | ||
| 783 | item->cache_queue = NULL; | ||
| 784 | |||
| 785 | for (cur = qlist; cur != NULL; cur = cur->next) { | ||
| 786 | msginfo = (MsgInfo *)cur->data; | ||
| 787 | |||
| 788 | debug_print("flush cache queue: %s/%d\n", | ||
| 789 | item->path, msginfo->msgnum); | ||
| 790 | procmsg_write_cache(msginfo, fp); | ||
| 791 | procmsg_msginfo_free(msginfo); | ||
| 792 | } | ||
| 793 | |||
| 794 | g_slist_free(qlist); | ||
| 795 | |||
| 796 | if (append) | ||
| 797 | fclose(fp); | ||
| 798 | } | ||
| 799 | |||
| 800 | void procmsg_add_cache_queue(FolderItem *item, gint num, MsgInfo *msginfo) | ||
| 801 | { | ||
| 802 | MsgInfo *queue_msginfo; | ||
| 803 | |||
| 804 | g_return_if_fail(msginfo != NULL); | ||
| 805 | |||
| 806 | queue_msginfo = procmsg_msginfo_copy(msginfo); | ||
| 807 | queue_msginfo->msgnum = num; | ||
| 808 | queue_msginfo->folder = item; | ||
| 809 | if (queue_msginfo->file_path) { | ||
| 810 | g_free(queue_msginfo->file_path); | ||
| 811 | queue_msginfo->file_path = NULL; | ||
| 812 | } | ||
| 813 | |||
| 814 | debug_print("procmsg_add_cache_queue: add msg cache: %s/%d\n", | ||
| 815 | item->path, num); | ||
| 816 | item->cache_queue = g_slist_prepend(item->cache_queue, queue_msginfo); | ||
| 817 | } | ||
| 818 | |||
| 819 | gboolean procmsg_flush_folder(FolderItem *item) | ||
| 820 | { | ||
| 821 | gboolean flushed = FALSE; | ||
| 822 | gint n_new, n_unread, n_total, n_min, n_max; | ||
| 823 | |||
| 824 | g_return_val_if_fail(item != NULL, FALSE); | ||
| 825 | g_return_val_if_fail(item->folder != NULL, FALSE); | ||
| 826 | |||
| 827 | if (FOLDER_TYPE(item->folder) != F_MH || item->last_num < 0) { | ||
| 828 | folder_item_scan(item); | ||
| 829 | return TRUE; | ||
| 830 | } | ||
| 831 | |||
| 832 | if (item->mark_queue && !item->opened) | ||
| 833 | flushed = TRUE; | ||
| 834 | procmsg_get_mark_sum(item, &n_new, &n_unread, &n_total, &n_min, &n_max, | ||
| 835 | 0); | ||
| 836 | item->unmarked_num = 0; | ||
| 837 | item->new = n_new; | ||
| 838 | item->unread = n_unread; | ||
| 839 | item->total = n_total; | ||
| 840 | |||
| 841 | if (item->cache_queue && !item->opened) { | ||
| 842 | procmsg_flush_cache_queue(item, NULL); | ||
| 843 | flushed = TRUE; | ||
| 844 | } | ||
| 845 | |||
| 846 | if (flushed) | ||
| 847 | debug_print("procmsg_flush_folder: flushed %s\n", item->path); | ||
| 848 | |||
| 849 | return flushed; | ||
| 850 | } | ||
| 851 | |||
| 852 | static void procmsg_flush_folder_foreach_func(gpointer key, gpointer val, | ||
| 853 | gpointer data) | ||
| 854 | { | ||
| 855 | procmsg_flush_folder(FOLDER_ITEM(key)); | ||
| 856 | } | ||
| 857 | |||
| 858 | void procmsg_flush_folder_foreach(GHashTable *folder_table) | ||
| 859 | { | ||
| 860 | g_hash_table_foreach(folder_table, procmsg_flush_folder_foreach_func, | ||
| 861 | NULL); | ||
| 862 | } | ||
| 863 | |||
| 864 | void procmsg_add_flags(FolderItem *item, gint num, MsgFlags flags) | ||
| 865 | { | ||
| 866 | FILE *fp; | ||
| 867 | MsgInfo msginfo; | ||
| 868 | |||
| 869 | g_return_if_fail(item != NULL); | ||
| 870 | |||
| 871 | if (item->opened) { | ||
| 872 | procmsg_add_mark_queue(item, num, flags); | ||
| 873 | return; | ||
| 874 | } | ||
| 875 | |||
| 876 | if ((fp = procmsg_open_mark_file(item, DATA_APPEND)) == NULL) { | ||
| 877 | g_warning(_("can't open mark file\n")); | ||
| 878 | return; | ||
| 879 | } | ||
| 880 | |||
| 881 | msginfo.msgnum = num; | ||
| 882 | msginfo.flags = flags; | ||
| 883 | |||
| 884 | procmsg_write_flags(&msginfo, fp); | ||
| 885 | fclose(fp); | ||
| 886 | } | ||
| 887 | |||
| 888 | struct MarkSum { | ||
| 889 | gint *new; | ||
| 890 | gint *unread; | ||
| 891 | gint *total; | ||
| 892 | gint *min; | ||
| 893 | gint *max; | ||
| 894 | gint first; | ||
| 895 | }; | ||
| 896 | |||
| 897 | static void mark_sum_func(gpointer key, gpointer value, gpointer data) | ||
| 898 | { | ||
| 899 | MsgFlags *flags = value; | ||
| 900 | gint num = GPOINTER_TO_INT(key); | ||
| 901 | struct MarkSum *marksum = data; | ||
| 902 | |||
| 903 | if (marksum->first <= num) { | ||
| 904 | if (MSG_IS_NEW(*flags)) (*marksum->new)++; | ||
| 905 | if (MSG_IS_UNREAD(*flags)) (*marksum->unread)++; | ||
| 906 | if (num > *marksum->max) *marksum->max = num; | ||
| 907 | if (num < *marksum->min || *marksum->min == 0) *marksum->min = num; | ||
| 908 | (*marksum->total)++; | ||
| 909 | } | ||
| 910 | |||
| 911 | g_free(flags); | ||
| 912 | } | ||
| 913 | |||
| 914 | void procmsg_get_mark_sum(FolderItem *item, | ||
| 915 | gint *new, gint *unread, gint *total, | ||
| 916 | gint *min, gint *max, | ||
| 917 | gint first) | ||
| 918 | { | ||
| 919 | GHashTable *mark_table; | ||
| 920 | struct MarkSum marksum; | ||
| 921 | |||
| 922 | *new = *unread = *total = *min = *max = 0; | ||
| 923 | marksum.new = new; | ||
| 924 | marksum.unread = unread; | ||
| 925 | marksum.total = total; | ||
| 926 | marksum.min = min; | ||
| 927 | marksum.max = max; | ||
| 928 | marksum.first = first; | ||
| 929 | |||
| 930 | mark_table = procmsg_read_mark_file(item); | ||
| 931 | |||
| 932 | if (mark_table) { | ||
| 933 | g_hash_table_foreach(mark_table, mark_sum_func, &marksum); | ||
| 934 | g_hash_table_destroy(mark_table); | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | static GHashTable *procmsg_read_mark_file(FolderItem *item) | ||
| 939 | { | ||
| 940 | FILE *fp; | ||
| 941 | GHashTable *mark_table = NULL; | ||
| 942 | guint32 idata; | ||
| 943 | guint num; | ||
| 944 | MsgFlags *flags; | ||
| 945 | MsgPermFlags perm_flags; | ||
| 946 | GSList *cur; | ||
| 947 | |||
| 948 | if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL) | ||
| 949 | return NULL; | ||
| 950 | |||
| 951 | mark_table = g_hash_table_new(NULL, g_direct_equal); | ||
| 952 | |||
| 953 | while (fread(&idata, sizeof(idata), 1, fp) == 1) { | ||
| 954 | num = idata; | ||
| 955 | if (fread(&idata, sizeof(idata), 1, fp) != 1) break; | ||
| 956 | perm_flags = idata; | ||
| 957 | |||
| 958 | flags = g_hash_table_lookup(mark_table, GUINT_TO_POINTER(num)); | ||
| 959 | if (flags != NULL) | ||
| 960 | g_free(flags); | ||
| 961 | |||
| 962 | flags = g_new0(MsgFlags, 1); | ||
| 963 | flags->perm_flags = perm_flags; | ||
| 964 | |||
| 965 | g_hash_table_insert(mark_table, GUINT_TO_POINTER(num), flags); | ||
| 966 | } | ||
| 967 | |||
| 968 | fclose(fp); | ||
| 969 | |||
| 970 | if (item->mark_queue) { | ||
| 971 | g_hash_table_foreach(mark_table, mark_unset_new_func, NULL); | ||
| 972 | item->mark_dirty = TRUE; | ||
| 973 | } | ||
| 974 | |||
| 975 | for (cur = item->mark_queue; cur != NULL; cur = cur->next) { | ||
| 976 | MsgFlagInfo *flaginfo = (MsgFlagInfo *)cur->data; | ||
| 977 | |||
| 978 | flags = g_hash_table_lookup(mark_table, | ||
| 979 | GUINT_TO_POINTER(flaginfo->msgnum)); | ||
| 980 | if (flags != NULL) | ||
| 981 | g_free(flags); | ||
| 982 | |||
| 983 | flags = g_new0(MsgFlags, 1); | ||
| 984 | flags->perm_flags = flaginfo->flags.perm_flags; | ||
| 985 | |||
| 986 | g_hash_table_insert(mark_table, | ||
| 987 | GUINT_TO_POINTER(flaginfo->msgnum), flags); | ||
| 988 | |||
| 989 | } | ||
| 990 | |||
| 991 | if (item->mark_queue && !item->opened) { | ||
| 992 | procmsg_write_mark_file(item, mark_table); | ||
| 993 | procmsg_flaginfo_list_free(item->mark_queue); | ||
| 994 | item->mark_queue = NULL; | ||
| 995 | item->mark_dirty = FALSE; | ||
| 996 | } | ||
| 997 | |||
| 998 | return mark_table; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | static void write_mark_func(gpointer key, gpointer value, gpointer data) | ||
| 1002 | { | ||
| 1003 | MsgInfo msginfo; | ||
| 1004 | |||
| 1005 | msginfo.msgnum = GPOINTER_TO_UINT(key); | ||
| 1006 | msginfo.flags.perm_flags = ((MsgFlags *)value)->perm_flags; | ||
| 1007 | procmsg_write_flags(&msginfo, (FILE *)data); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | static void procmsg_write_mark_file(FolderItem *item, GHashTable *mark_table) | ||
| 1011 | { | ||
| 1012 | FILE *fp; | ||
| 1013 | |||
| 1014 | if ((fp = procmsg_open_mark_file(item, DATA_WRITE)) == NULL) { | ||
| 1015 | g_warning("procmsg_write_mark_file: cannot open mark file."); | ||
| 1016 | return; | ||
| 1017 | } | ||
| 1018 | g_hash_table_foreach(mark_table, write_mark_func, fp); | ||
| 1019 | fclose(fp); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | FILE *procmsg_open_data_file(const gchar *file, guint version, | ||
| 1023 | DataOpenMode mode, gchar *buf, size_t buf_size) | ||
| 1024 | { | ||
| 1025 | FILE *fp; | ||
| 1026 | guint32 data_ver = 0; | ||
| 1027 | |||
| 1028 | g_return_val_if_fail(file != NULL, NULL); | ||
| 1029 | |||
| 1030 | if (mode == DATA_WRITE) { | ||
| 1031 | if ((fp = g_fopen(file, "wb")) == NULL) { | ||
| 1032 | if (errno == EACCES) { | ||
| 1033 | change_file_mode_rw(NULL, file); | ||
| 1034 | if ((fp = g_fopen(file, "wb")) == NULL) { | ||
| 1035 | FILE_OP_ERROR(file, "procmsg_open_data_file: fopen"); | ||
| 1036 | return NULL; | ||
| 1037 | } | ||
| 1038 | } else { | ||
| 1039 | FILE_OP_ERROR(file, "procmsg_open_data_file: fopen"); | ||
| 1040 | return NULL; | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | if (change_file_mode_rw(fp, file) < 0) | ||
| 1044 | FILE_OP_ERROR(file, "chmod"); | ||
| 1045 | |||
| 1046 | WRITE_CACHE_DATA_INT(version, fp); | ||
| 1047 | return fp; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | /* check version */ | ||
| 1051 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 1052 | if (errno == EACCES) { | ||
| 1053 | change_file_mode_rw(NULL, file); | ||
| 1054 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 1055 | FILE_OP_ERROR(file, "procmsg_open_data_file: fopen"); | ||
| 1056 | } | ||
| 1057 | } else { | ||
| 1058 | debug_print("Mark/Cache file '%s' not found\n", file); | ||
| 1059 | } | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | if (fp) { | ||
| 1063 | if (buf && buf_size > 0) | ||
| 1064 | setvbuf(fp, buf, _IOFBF, buf_size); | ||
| 1065 | if (fread(&data_ver, sizeof(data_ver), 1, fp) != 1) { | ||
| 1066 | g_warning("%s: cannot read mark/cache file (truncated?)\n", file); | ||
| 1067 | fclose(fp); | ||
| 1068 | fp = NULL; | ||
| 1069 | } else if (version != data_ver) { | ||
| 1070 | g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n", | ||
| 1071 | file, data_ver, version); | ||
| 1072 | fclose(fp); | ||
| 1073 | fp = NULL; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | if (mode == DATA_READ) | ||
| 1078 | return fp; | ||
| 1079 | |||
| 1080 | if (fp) { | ||
| 1081 | /* reopen with append mode */ | ||
| 1082 | fclose(fp); | ||
| 1083 | if ((fp = g_fopen(file, "ab")) == NULL) { | ||
| 1084 | if (errno == EACCES) { | ||
| 1085 | change_file_mode_rw(NULL, file); | ||
| 1086 | if ((fp = g_fopen(file, "ab")) == NULL) { | ||
| 1087 | FILE_OP_ERROR(file, "procmsg_open_data_file: fopen"); | ||
| 1088 | } | ||
| 1089 | } else { | ||
| 1090 | FILE_OP_ERROR(file, "procmsg_open_data_file: fopen"); | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | } else { | ||
| 1094 | /* open with overwrite mode if mark file doesn't exist or | ||
| 1095 | version is different */ | ||
| 1096 | fp = procmsg_open_data_file(file, version, DATA_WRITE, buf, | ||
| 1097 | buf_size); | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | return fp; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | static GMappedFile *procmsg_open_cache_file_mmap(FolderItem *item, | ||
| 1104 | DataOpenMode mode) | ||
| 1105 | { | ||
| 1106 | gchar *cachefile; | ||
| 1107 | GMappedFile *map = NULL; | ||
| 1108 | GError *error = NULL; | ||
| 1109 | gsize size; | ||
| 1110 | guint32 data_ver = 0; | ||
| 1111 | gchar *p; | ||
| 1112 | |||
| 1113 | if (mode != DATA_READ) | ||
| 1114 | return NULL; | ||
| 1115 | |||
| 1116 | cachefile = folder_item_get_cache_file(item); | ||
| 1117 | if (cachefile) { | ||
| 1118 | map = g_mapped_file_new(cachefile, FALSE, &error); | ||
| 1119 | if (!map) { | ||
| 1120 | if (error && error->code == G_FILE_ERROR_NOENT) | ||
| 1121 | debug_print("%s: mark/cache file not found\n", cachefile); | ||
| 1122 | else if (error) | ||
| 1123 | g_warning("%s: cannot open mark/cache file: %s", cachefile, error->message); | ||
| 1124 | else | ||
| 1125 | g_warning("%s: cannot open mark/cache file", cachefile); | ||
| 1126 | if (error) | ||
| 1127 | g_error_free(error); | ||
| 1128 | g_free(cachefile); | ||
| 1129 | return NULL; | ||
| 1130 | } | ||
| 1131 | size = g_mapped_file_get_length(map); | ||
| 1132 | if (size < sizeof(data_ver)) { | ||
| 1133 | g_warning("%s: cannot read mark/cache file (truncated?)", cachefile); | ||
| 1134 | g_mapped_file_free(map); | ||
| 1135 | g_free(cachefile); | ||
| 1136 | return NULL; | ||
| 1137 | } | ||
| 1138 | p = g_mapped_file_get_contents(map); | ||
| 1139 | data_ver = *(guint32 *)p; | ||
| 1140 | if (CACHE_VERSION != data_ver) { | ||
| 1141 | g_message("%s: Mark/Cache version is different (%u != %u). Discarding it.\n", | ||
| 1142 | cachefile, data_ver, CACHE_VERSION); | ||
| 1143 | g_mapped_file_free(map); | ||
| 1144 | g_free(cachefile); | ||
| 1145 | return NULL; | ||
| 1146 | } | ||
| 1147 | g_free(cachefile); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | return map; | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | FILE *procmsg_open_cache_file(FolderItem *item, DataOpenMode mode) | ||
| 1154 | { | ||
| 1155 | gchar *cachefile; | ||
| 1156 | FILE *fp; | ||
| 1157 | |||
| 1158 | cachefile = folder_item_get_cache_file(item); | ||
| 1159 | fp = procmsg_open_data_file(cachefile, CACHE_VERSION, mode, NULL, 0); | ||
| 1160 | g_free(cachefile); | ||
| 1161 | |||
| 1162 | return fp; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | FILE *procmsg_open_mark_file(FolderItem *item, DataOpenMode mode) | ||
| 1166 | { | ||
| 1167 | gchar *markfile; | ||
| 1168 | FILE *fp; | ||
| 1169 | |||
| 1170 | markfile = folder_item_get_mark_file(item); | ||
| 1171 | fp = procmsg_open_data_file(markfile, MARK_VERSION, mode, NULL, 0); | ||
| 1172 | g_free(markfile); | ||
| 1173 | |||
| 1174 | return fp; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | void procmsg_clear_cache(FolderItem *item) | ||
| 1178 | { | ||
| 1179 | FILE *fp; | ||
| 1180 | |||
| 1181 | fp = procmsg_open_cache_file(item, DATA_WRITE); | ||
| 1182 | if (fp) | ||
| 1183 | fclose(fp); | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | void procmsg_clear_mark(FolderItem *item) | ||
| 1187 | { | ||
| 1188 | FILE *fp; | ||
| 1189 | |||
| 1190 | fp = procmsg_open_mark_file(item, DATA_WRITE); | ||
| 1191 | if (fp) | ||
| 1192 | fclose(fp); | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | /* return the reversed thread tree */ | ||
| 1196 | GNode *procmsg_get_thread_tree(GSList *mlist) | ||
| 1197 | { | ||
| 1198 | GNode *root, *parent, *node, *next; | ||
| 1199 | GHashTable *table; | ||
| 1200 | MsgInfo *msginfo; | ||
| 1201 | const gchar *msgid; | ||
| 1202 | GSList *reflist; | ||
| 1203 | |||
| 1204 | root = g_node_new(NULL); | ||
| 1205 | table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 1206 | |||
| 1207 | for (; mlist != NULL; mlist = mlist->next) { | ||
| 1208 | msginfo = (MsgInfo *)mlist->data; | ||
| 1209 | parent = root; | ||
| 1210 | |||
| 1211 | /* only look for the real parent first */ | ||
| 1212 | if (msginfo->inreplyto) { | ||
| 1213 | parent = g_hash_table_lookup(table, msginfo->inreplyto); | ||
| 1214 | if (parent == NULL) | ||
| 1215 | parent = root; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | node = g_node_insert_data_before | ||
| 1219 | (parent, parent == root ? parent->children : NULL, | ||
| 1220 | msginfo); | ||
| 1221 | if ((msgid = msginfo->msgid) && | ||
| 1222 | g_hash_table_lookup(table, msgid) == NULL) | ||
| 1223 | g_hash_table_insert(table, (gchar *)msgid, node); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /* complete the unfinished threads */ | ||
| 1227 | for (node = root->children; node != NULL; ) { | ||
| 1228 | next = node->next; | ||
| 1229 | msginfo = (MsgInfo *)node->data; | ||
| 1230 | parent = NULL; | ||
| 1231 | |||
| 1232 | if (msginfo->inreplyto) | ||
| 1233 | parent = g_hash_table_lookup(table, msginfo->inreplyto); | ||
| 1234 | |||
| 1235 | /* try looking for the indirect parent */ | ||
| 1236 | if (!parent && msginfo->references) { | ||
| 1237 | for (reflist = msginfo->references; | ||
| 1238 | reflist != NULL; reflist = reflist->next) | ||
| 1239 | if ((parent = g_hash_table_lookup | ||
| 1240 | (table, reflist->data)) != NULL) | ||
| 1241 | break; | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | /* node should not be the parent, and node should not | ||
| 1245 | be an ancestor of parent (circular reference) */ | ||
| 1246 | if (parent && parent != node && | ||
| 1247 | !g_node_is_ancestor(node, parent)) { | ||
| 1248 | g_node_unlink(node); | ||
| 1249 | g_node_insert_before | ||
| 1250 | (parent, parent->children, node); | ||
| 1251 | } | ||
| 1252 | node = next; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | g_hash_table_destroy(table); | ||
| 1256 | |||
| 1257 | return root; | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | static gboolean procmsg_thread_date_func(GNode *node, gpointer data) | ||
| 1261 | { | ||
| 1262 | guint *tdate = (guint *)data; | ||
| 1263 | MsgInfo *msginfo = (MsgInfo *)node->data; | ||
| 1264 | |||
| 1265 | if (*tdate < msginfo->date_t) | ||
| 1266 | *tdate = msginfo->date_t; | ||
| 1267 | |||
| 1268 | return FALSE; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | guint procmsg_get_thread_date(GNode *node) | ||
| 1272 | { | ||
| 1273 | guint tdate = 0; | ||
| 1274 | |||
| 1275 | g_return_val_if_fail(node != NULL && node->parent != NULL && | ||
| 1276 | node->parent->parent == NULL, 0); | ||
| 1277 | |||
| 1278 | g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1279 | procmsg_thread_date_func, &tdate); | ||
| 1280 | |||
| 1281 | return tdate; | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | gint procmsg_move_messages(GSList *mlist) | ||
| 1285 | { | ||
| 1286 | GSList *cur, *movelist = NULL; | ||
| 1287 | MsgInfo *msginfo; | ||
| 1288 | FolderItem *dest = NULL; | ||
| 1289 | GHashTable *hash; | ||
| 1290 | gint val = 0; | ||
| 1291 | |||
| 1292 | if (!mlist) return 0; | ||
| 1293 | |||
| 1294 | hash = procmsg_to_folder_hash_table_create(mlist); | ||
| 1295 | folder_item_scan_foreach(hash); | ||
| 1296 | g_hash_table_destroy(hash); | ||
| 1297 | |||
| 1298 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1299 | msginfo = (MsgInfo *)cur->data; | ||
| 1300 | if (!dest) { | ||
| 1301 | dest = msginfo->to_folder; | ||
| 1302 | movelist = g_slist_append(movelist, msginfo); | ||
| 1303 | } else if (dest == msginfo->to_folder) { | ||
| 1304 | movelist = g_slist_append(movelist, msginfo); | ||
| 1305 | } else { | ||
| 1306 | val = folder_item_move_msgs(dest, movelist); | ||
| 1307 | g_slist_free(movelist); | ||
| 1308 | movelist = NULL; | ||
| 1309 | if (val == -1) | ||
| 1310 | return val; | ||
| 1311 | dest = msginfo->to_folder; | ||
| 1312 | movelist = g_slist_append(movelist, msginfo); | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | if (movelist) { | ||
| 1317 | val = folder_item_move_msgs(dest, movelist); | ||
| 1318 | g_slist_free(movelist); | ||
| 1319 | } | ||
| 1320 | |||
| 1321 | return val == -1 ? -1 : 0; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | gint procmsg_copy_messages(GSList *mlist) | ||
| 1325 | { | ||
| 1326 | GSList *cur, *copylist = NULL; | ||
| 1327 | MsgInfo *msginfo; | ||
| 1328 | FolderItem *dest = NULL; | ||
| 1329 | GHashTable *hash; | ||
| 1330 | gint val = 0; | ||
| 1331 | |||
| 1332 | if (!mlist) return 0; | ||
| 1333 | |||
| 1334 | hash = procmsg_to_folder_hash_table_create(mlist); | ||
| 1335 | folder_item_scan_foreach(hash); | ||
| 1336 | g_hash_table_destroy(hash); | ||
| 1337 | |||
| 1338 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1339 | msginfo = (MsgInfo *)cur->data; | ||
| 1340 | if (!dest) { | ||
| 1341 | dest = msginfo->to_folder; | ||
| 1342 | copylist = g_slist_append(copylist, msginfo); | ||
| 1343 | } else if (dest == msginfo->to_folder) { | ||
| 1344 | copylist = g_slist_append(copylist, msginfo); | ||
| 1345 | } else { | ||
| 1346 | val = folder_item_copy_msgs(dest, copylist); | ||
| 1347 | g_slist_free(copylist); | ||
| 1348 | copylist = NULL; | ||
| 1349 | if (val == -1) | ||
| 1350 | return val; | ||
| 1351 | dest = msginfo->to_folder; | ||
| 1352 | copylist = g_slist_append(copylist, msginfo); | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | if (copylist) { | ||
| 1357 | val = folder_item_copy_msgs(dest, copylist); | ||
| 1358 | g_slist_free(copylist); | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | return val == -1 ? -1 : 0; | ||
| 1362 | } | ||
| 1363 | |||
| 1364 | gint procmsg_add_messages_from_queue(FolderItem *dest, GSList *mlist, | ||
| 1365 | gboolean is_move) | ||
| 1366 | { | ||
| 1367 | MsgInfo *msginfo; | ||
| 1368 | GSList *cur; | ||
| 1369 | gchar *file; | ||
| 1370 | FILE *fp; | ||
| 1371 | gchar buf[BUFFSIZE]; | ||
| 1372 | gchar *dest_file; | ||
| 1373 | gboolean is_error = FALSE; | ||
| 1374 | FolderItem *src; | ||
| 1375 | MsgFlags flags; | ||
| 1376 | |||
| 1377 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1378 | g_return_val_if_fail(mlist != NULL, -1); | ||
| 1379 | |||
| 1380 | msginfo = (MsgInfo *)mlist->data; | ||
| 1381 | if (!msginfo || !msginfo->folder || msginfo->folder->stype != F_QUEUE || | ||
| 1382 | !MSG_IS_QUEUED(msginfo->flags) || dest->stype == F_QUEUE) | ||
| 1383 | return -1; | ||
| 1384 | |||
| 1385 | debug_print("procmsg_add_messages_from_queue: adding messages from queue folder\n"); | ||
| 1386 | |||
| 1387 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1388 | msginfo = (MsgInfo *)cur->data; | ||
| 1389 | flags = msginfo->flags; | ||
| 1390 | if (!MSG_IS_QUEUED(flags)) | ||
| 1391 | return -1; | ||
| 1392 | MSG_UNSET_TMP_FLAGS(flags, MSG_QUEUED); | ||
| 1393 | src = msginfo->folder; | ||
| 1394 | file = procmsg_get_message_file(msginfo); | ||
| 1395 | if (!file) | ||
| 1396 | return -1; | ||
| 1397 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 1398 | FILE_OP_ERROR(file, "folder_item_move_msgs: fopen"); | ||
| 1399 | g_free(file); | ||
| 1400 | return -1; | ||
| 1401 | } | ||
| 1402 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 1403 | if (buf[0] == '\r' || buf[0] == '\n') | ||
| 1404 | break; | ||
| 1405 | } | ||
| 1406 | if (ferror(fp)) { | ||
| 1407 | fclose(fp); | ||
| 1408 | g_free(file); | ||
| 1409 | return -1; | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | dest_file = get_tmp_file(); | ||
| 1413 | debug_print("copy queued msg: %s -> %s\n", file, dest_file); | ||
| 1414 | |||
| 1415 | if (copy_file_part(fp, ftell(fp), G_MAXINT, dest_file) < 0) { | ||
| 1416 | fclose(fp); | ||
| 1417 | is_error = TRUE; | ||
| 1418 | } else { | ||
| 1419 | fclose(fp); | ||
| 1420 | if (folder_item_add_msg(dest, dest_file, &flags, TRUE) < 0) { | ||
| 1421 | g_unlink(dest_file); | ||
| 1422 | is_error = TRUE; | ||
| 1423 | } else if (is_move && | ||
| 1424 | folder_item_remove_msg(src, msginfo) < 0) | ||
| 1425 | is_error = TRUE; | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | g_free(dest_file); | ||
| 1429 | g_free(file); | ||
| 1430 | if (is_error) | ||
| 1431 | return -1; | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | return 0; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | gchar *procmsg_get_message_file_path(MsgInfo *msginfo) | ||
| 1438 | { | ||
| 1439 | gchar *path, *file; | ||
| 1440 | |||
| 1441 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 1442 | |||
| 1443 | if (msginfo->encinfo && msginfo->encinfo->plaintext_file) | ||
| 1444 | file = g_strdup(msginfo->encinfo->plaintext_file); | ||
| 1445 | else if (msginfo->file_path) | ||
| 1446 | return g_strdup(msginfo->file_path); | ||
| 1447 | else { | ||
| 1448 | gchar nstr[16]; | ||
| 1449 | path = folder_item_get_path(msginfo->folder); | ||
| 1450 | file = g_strconcat(path, G_DIR_SEPARATOR_S, | ||
| 1451 | utos_buf(nstr, msginfo->msgnum), NULL); | ||
| 1452 | g_free(path); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | return file; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | gchar *procmsg_get_message_file(MsgInfo *msginfo) | ||
| 1459 | { | ||
| 1460 | gchar *filename = NULL; | ||
| 1461 | |||
| 1462 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 1463 | |||
| 1464 | if (msginfo->file_path) | ||
| 1465 | return g_strdup(msginfo->file_path); | ||
| 1466 | |||
| 1467 | filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum); | ||
| 1468 | if (!filename) | ||
| 1469 | debug_print(_("can't fetch message %d\n"), msginfo->msgnum); | ||
| 1470 | |||
| 1471 | return filename; | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | GSList *procmsg_get_message_file_list(GSList *mlist) | ||
| 1475 | { | ||
| 1476 | GSList *file_list = NULL; | ||
| 1477 | MsgInfo *msginfo; | ||
| 1478 | MsgFileInfo *fileinfo; | ||
| 1479 | gchar *file; | ||
| 1480 | |||
| 1481 | while (mlist != NULL) { | ||
| 1482 | msginfo = (MsgInfo *)mlist->data; | ||
| 1483 | file = procmsg_get_message_file(msginfo); | ||
| 1484 | if (!file) { | ||
| 1485 | procmsg_message_file_list_free(file_list); | ||
| 1486 | return NULL; | ||
| 1487 | } | ||
| 1488 | fileinfo = g_new(MsgFileInfo, 1); | ||
| 1489 | fileinfo->file = file; | ||
| 1490 | fileinfo->flags = g_new(MsgFlags, 1); | ||
| 1491 | *fileinfo->flags = msginfo->flags; | ||
| 1492 | file_list = g_slist_prepend(file_list, fileinfo); | ||
| 1493 | mlist = mlist->next; | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | file_list = g_slist_reverse(file_list); | ||
| 1497 | |||
| 1498 | return file_list; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | void procmsg_message_file_list_free(GSList *file_list) | ||
| 1502 | { | ||
| 1503 | GSList *cur; | ||
| 1504 | MsgFileInfo *fileinfo; | ||
| 1505 | |||
| 1506 | for (cur = file_list; cur != NULL; cur = cur->next) { | ||
| 1507 | fileinfo = (MsgFileInfo *)cur->data; | ||
| 1508 | g_free(fileinfo->file); | ||
| 1509 | g_free(fileinfo->flags); | ||
| 1510 | g_free(fileinfo); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | g_slist_free(file_list); | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | FILE *procmsg_open_message(MsgInfo *msginfo) | ||
| 1517 | { | ||
| 1518 | FILE *fp; | ||
| 1519 | gchar *file; | ||
| 1520 | |||
| 1521 | g_return_val_if_fail(msginfo != NULL, NULL); | ||
| 1522 | |||
| 1523 | file = procmsg_get_message_file_path(msginfo); | ||
| 1524 | g_return_val_if_fail(file != NULL, NULL); | ||
| 1525 | |||
| 1526 | if (!is_file_exist(file)) { | ||
| 1527 | g_free(file); | ||
| 1528 | file = procmsg_get_message_file(msginfo); | ||
| 1529 | if (!file) | ||
| 1530 | return NULL; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 1534 | FILE_OP_ERROR(file, "procmsg_open_message: fopen"); | ||
| 1535 | g_free(file); | ||
| 1536 | return NULL; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | g_free(file); | ||
| 1540 | |||
| 1541 | if (MSG_IS_QUEUED(msginfo->flags)) { | ||
| 1542 | gchar buf[BUFFSIZE]; | ||
| 1543 | |||
| 1544 | while (fgets(buf, sizeof(buf), fp) != NULL) | ||
| 1545 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | return fp; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | static DecryptMessageFunc decrypt_message_func = NULL; | ||
| 1552 | static gboolean auto_decrypt = TRUE; | ||
| 1553 | |||
| 1554 | void procmsg_set_decrypt_message_func(DecryptMessageFunc func) | ||
| 1555 | { | ||
| 1556 | decrypt_message_func = func; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | void procmsg_set_auto_decrypt_message(gboolean enabled) | ||
| 1560 | { | ||
| 1561 | auto_decrypt = enabled; | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | FILE *procmsg_open_message_decrypted(MsgInfo *msginfo, MimeInfo **mimeinfo) | ||
| 1565 | { | ||
| 1566 | FILE *fp; | ||
| 1567 | |||
| 1568 | if (decrypt_message_func && auto_decrypt) | ||
| 1569 | return decrypt_message_func(msginfo, mimeinfo); | ||
| 1570 | |||
| 1571 | *mimeinfo = NULL; | ||
| 1572 | if ((fp = procmsg_open_message(msginfo)) == NULL) | ||
| 1573 | return NULL; | ||
| 1574 | *mimeinfo = procmime_scan_mime_header(fp); | ||
| 1575 | |||
| 1576 | return fp; | ||
| 1577 | } | ||
| 1578 | |||
| 1579 | gboolean procmsg_msg_exist(MsgInfo *msginfo) | ||
| 1580 | { | ||
| 1581 | gchar *path; | ||
| 1582 | gboolean ret; | ||
| 1583 | |||
| 1584 | if (!msginfo) return FALSE; | ||
| 1585 | |||
| 1586 | path = folder_item_get_path(msginfo->folder); | ||
| 1587 | change_dir(path); | ||
| 1588 | ret = !folder_item_is_msg_changed(msginfo->folder, msginfo); | ||
| 1589 | g_free(path); | ||
| 1590 | |||
| 1591 | return ret; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | gboolean procmsg_trash_messages_exist(void) | ||
| 1595 | { | ||
| 1596 | FolderItem *trash; | ||
| 1597 | GList *cur; | ||
| 1598 | |||
| 1599 | for (cur = folder_get_list(); cur != NULL; cur = cur->next) { | ||
| 1600 | trash = FOLDER(cur->data)->trash; | ||
| 1601 | if (trash && trash->total > 0) | ||
| 1602 | return TRUE; | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | return FALSE; | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | void procmsg_empty_trash(FolderItem *trash) | ||
| 1609 | { | ||
| 1610 | if (!trash) | ||
| 1611 | return; | ||
| 1612 | |||
| 1613 | g_return_if_fail(trash->stype == F_TRASH || trash->stype == F_JUNK); | ||
| 1614 | |||
| 1615 | if (trash->total > 0) { | ||
| 1616 | debug_print("Emptying messages in %s ...\n", trash->path); | ||
| 1617 | |||
| 1618 | folder_item_remove_all_msg(trash); | ||
| 1619 | procmsg_clear_cache(trash); | ||
| 1620 | procmsg_clear_mark(trash); | ||
| 1621 | trash->cache_dirty = FALSE; | ||
| 1622 | trash->mark_dirty = FALSE; | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | void procmsg_empty_all_trash(void) | ||
| 1627 | { | ||
| 1628 | FolderItem *trash; | ||
| 1629 | GList *cur; | ||
| 1630 | |||
| 1631 | for (cur = folder_get_list(); cur != NULL; cur = cur->next) { | ||
| 1632 | trash = FOLDER(cur->data)->trash; | ||
| 1633 | procmsg_empty_trash(trash); | ||
| 1634 | } | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | static gboolean remove_all_cached_messages_func(GNode *node, gpointer data) | ||
| 1638 | { | ||
| 1639 | FolderItem *item; | ||
| 1640 | gchar *dir; | ||
| 1641 | |||
| 1642 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 1643 | |||
| 1644 | item = FOLDER_ITEM(node->data); | ||
| 1645 | if (!item->path || item->stype == F_VIRTUAL) | ||
| 1646 | return FALSE; | ||
| 1647 | |||
| 1648 | dir = folder_item_get_path(item); | ||
| 1649 | if (is_dir_exist(dir)) { | ||
| 1650 | debug_print("removing all cached messages in '%s' ...\n", | ||
| 1651 | item->path); | ||
| 1652 | remove_all_numbered_files(dir); | ||
| 1653 | } | ||
| 1654 | g_free(dir); | ||
| 1655 | |||
| 1656 | return FALSE; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | void procmsg_remove_all_cached_messages(Folder *folder) | ||
| 1660 | { | ||
| 1661 | g_return_if_fail(folder != NULL); | ||
| 1662 | g_return_if_fail(FOLDER_IS_REMOTE(folder)); | ||
| 1663 | |||
| 1664 | debug_print("Removing all caches in the mailbox '%s' ...\n", | ||
| 1665 | folder->name); | ||
| 1666 | |||
| 1667 | g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 1668 | remove_all_cached_messages_func, NULL); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file) | ||
| 1672 | { | ||
| 1673 | gint num; | ||
| 1674 | MsgFlags flag = {0, 0}; | ||
| 1675 | |||
| 1676 | debug_print("saving sent message...\n"); | ||
| 1677 | |||
| 1678 | if (!outbox) | ||
| 1679 | outbox = folder_get_default_outbox(); | ||
| 1680 | g_return_val_if_fail(outbox != NULL, -1); | ||
| 1681 | |||
| 1682 | folder_item_scan(outbox); | ||
| 1683 | if ((num = folder_item_add_msg(outbox, file, &flag, FALSE)) < 0) { | ||
| 1684 | g_warning("can't save message\n"); | ||
| 1685 | return -1; | ||
| 1686 | } | ||
| 1687 | procmsg_flush_folder(outbox); | ||
| 1688 | |||
| 1689 | return 0; | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | static guint print_id = 0; | ||
| 1693 | |||
| 1694 | static gint print_command_exec(const gchar *file, const gchar *cmdline) | ||
| 1695 | { | ||
| 1696 | static const gchar *def_cmd = "lpr %s"; | ||
| 1697 | gchar buf[1024]; | ||
| 1698 | |||
| 1699 | #ifdef G_OS_WIN32 | ||
| 1700 | if (canonicalize_file_replace(file) < 0) | ||
| 1701 | return -1; | ||
| 1702 | #endif | ||
| 1703 | |||
| 1704 | if (cmdline && str_find_format_times(cmdline, 's') == 1) | ||
| 1705 | g_snprintf(buf, sizeof(buf) - 1, cmdline, file); | ||
| 1706 | else { | ||
| 1707 | if (cmdline) { | ||
| 1708 | g_warning(_("Print command line is invalid: `%s'\n"), | ||
| 1709 | cmdline); | ||
| 1710 | return -1; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | #ifdef G_OS_WIN32 | ||
| 1714 | execute_print_file(file); | ||
| 1715 | return 0; | ||
| 1716 | #else | ||
| 1717 | g_snprintf(buf, sizeof(buf) - 1, def_cmd, file); | ||
| 1718 | #endif | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | g_strchomp(buf); | ||
| 1722 | if (buf[strlen(buf) - 1] != '&') | ||
| 1723 | strcat(buf, "&"); | ||
| 1724 | |||
| 1725 | if (system(buf) != 0) | ||
| 1726 | return -1; | ||
| 1727 | |||
| 1728 | return 0; | ||
| 1729 | } | ||
| 1730 | |||
| 1731 | static void procmsg_write_headers(MsgInfo *msginfo, MimeInfo *partinfo, | ||
| 1732 | FILE *fp, FILE *dest_fp, | ||
| 1733 | const gchar *encoding, gboolean all_headers) | ||
| 1734 | { | ||
| 1735 | GPtrArray *headers; | ||
| 1736 | gint i; | ||
| 1737 | |||
| 1738 | if (all_headers) | ||
| 1739 | headers = procheader_get_header_array_asis(fp, NULL); | ||
| 1740 | else | ||
| 1741 | headers = procheader_get_header_array_for_display(fp, NULL); | ||
| 1742 | |||
| 1743 | for (i = 0; i < headers->len; i++) { | ||
| 1744 | Header *hdr; | ||
| 1745 | gchar *file_str; | ||
| 1746 | const gchar *body; | ||
| 1747 | |||
| 1748 | hdr = g_ptr_array_index(headers, i); | ||
| 1749 | |||
| 1750 | if (partinfo) { | ||
| 1751 | if (!g_ascii_strcasecmp(hdr->name, "Subject") || | ||
| 1752 | !g_ascii_strcasecmp(hdr->name, "From") || | ||
| 1753 | !g_ascii_strcasecmp(hdr->name, "To") || | ||
| 1754 | !g_ascii_strcasecmp(hdr->name, "Cc")) { | ||
| 1755 | unfold_line(hdr->body); | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | body = hdr->body; | ||
| 1759 | while (g_ascii_isspace(*body)) | ||
| 1760 | body++; | ||
| 1761 | } else { | ||
| 1762 | if (!g_ascii_strcasecmp(hdr->name, "Subject")) | ||
| 1763 | body = msginfo->subject; | ||
| 1764 | else if (!g_ascii_strcasecmp(hdr->name, "From")) | ||
| 1765 | body = msginfo->from; | ||
| 1766 | else if (!g_ascii_strcasecmp(hdr->name, "To")) | ||
| 1767 | body = msginfo->to; | ||
| 1768 | else if (!g_ascii_strcasecmp(hdr->name, "Cc")) { | ||
| 1769 | unfold_line(hdr->body); | ||
| 1770 | body = hdr->body; | ||
| 1771 | while (g_ascii_isspace(*body)) | ||
| 1772 | body++; | ||
| 1773 | } else { | ||
| 1774 | body = hdr->body; | ||
| 1775 | while (g_ascii_isspace(*body)) | ||
| 1776 | body++; | ||
| 1777 | } | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | if (body && *body != '\0') { | ||
| 1781 | file_str = conv_codeset_strdup | ||
| 1782 | (body, CS_INTERNAL, encoding); | ||
| 1783 | fprintf(dest_fp, "%s: %s\n", hdr->name, | ||
| 1784 | file_str ? file_str : body); | ||
| 1785 | g_free(file_str); | ||
| 1786 | } else { | ||
| 1787 | fprintf(dest_fp, "%s: (none)\n", hdr->name); | ||
| 1788 | } | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | procheader_header_array_destroy(headers); | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | void procmsg_print_message(MsgInfo *msginfo, const gchar *cmdline, | ||
| 1795 | gboolean all_headers) | ||
| 1796 | { | ||
| 1797 | gchar *prtmp; | ||
| 1798 | |||
| 1799 | g_return_if_fail(msginfo != NULL); | ||
| 1800 | |||
| 1801 | prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt", | ||
| 1802 | get_mime_tmp_dir(), G_DIR_SEPARATOR, | ||
| 1803 | print_id++); | ||
| 1804 | |||
| 1805 | if (procmsg_save_message_as_text(msginfo, prtmp, | ||
| 1806 | conv_get_locale_charset_str(), | ||
| 1807 | all_headers) == 0) | ||
| 1808 | print_command_exec(prtmp, cmdline); | ||
| 1809 | |||
| 1810 | g_free(prtmp); | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | void procmsg_print_message_part(MsgInfo *msginfo, MimeInfo *partinfo, | ||
| 1814 | const gchar *cmdline, gboolean all_headers) | ||
| 1815 | { | ||
| 1816 | FILE *msgfp, *tmpfp, *prfp; | ||
| 1817 | gchar *prtmp; | ||
| 1818 | gchar buf[BUFFSIZE]; | ||
| 1819 | |||
| 1820 | if ((msgfp = procmsg_open_message(msginfo)) == NULL) { | ||
| 1821 | return; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | if ((tmpfp = procmime_get_text_content | ||
| 1825 | (partinfo, msgfp, conv_get_locale_charset_str())) == NULL) { | ||
| 1826 | fclose(msgfp); | ||
| 1827 | return; | ||
| 1828 | } | ||
| 1829 | fclose(msgfp); | ||
| 1830 | |||
| 1831 | prtmp = g_strdup_printf("%s%cprinttmp-%08x.txt", | ||
| 1832 | get_mime_tmp_dir(), G_DIR_SEPARATOR, | ||
| 1833 | print_id++); | ||
| 1834 | if ((prfp = g_fopen(prtmp, "w")) == NULL) { | ||
| 1835 | FILE_OP_ERROR(prtmp, "procmsg_print_message_part: fopen"); | ||
| 1836 | g_free(prtmp); | ||
| 1837 | fclose(tmpfp); | ||
| 1838 | return; | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | while (fgets(buf, sizeof(buf), tmpfp) != NULL) | ||
| 1842 | fputs(buf, prfp); | ||
| 1843 | |||
| 1844 | fclose(prfp); | ||
| 1845 | fclose(tmpfp); | ||
| 1846 | |||
| 1847 | print_command_exec(prtmp, cmdline); | ||
| 1848 | |||
| 1849 | g_free(prtmp); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | gint procmsg_save_message_as_text(MsgInfo *msginfo, const gchar *dest, | ||
| 1853 | const gchar *encoding, gboolean all_headers) | ||
| 1854 | { | ||
| 1855 | MimeInfo *mimeinfo, *partinfo; | ||
| 1856 | FILE *fp; | ||
| 1857 | FILE *tmpfp; | ||
| 1858 | FILE *destfp; | ||
| 1859 | gchar buf[BUFFSIZE]; | ||
| 1860 | gchar *part_str; | ||
| 1861 | gint ret = 0; | ||
| 1862 | |||
| 1863 | g_return_val_if_fail(msginfo != NULL, -1); | ||
| 1864 | g_return_val_if_fail(dest != NULL, -1); | ||
| 1865 | |||
| 1866 | mimeinfo = procmime_scan_message(msginfo); | ||
| 1867 | if (!mimeinfo) | ||
| 1868 | return -1; | ||
| 1869 | if ((fp = procmsg_open_message(msginfo)) == NULL) { | ||
| 1870 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1871 | return -1; | ||
| 1872 | } | ||
| 1873 | if ((destfp = g_fopen(dest, "w")) == NULL) { | ||
| 1874 | fclose(fp); | ||
| 1875 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1876 | return -1; | ||
| 1877 | } | ||
| 1878 | procmsg_write_headers(msginfo, mimeinfo, fp, destfp, encoding, all_headers); | ||
| 1879 | fputc('\n', destfp); | ||
| 1880 | |||
| 1881 | partinfo = mimeinfo; | ||
| 1882 | |||
| 1883 | while (partinfo != NULL) { | ||
| 1884 | if (fseek(fp, partinfo->fpos, SEEK_SET) < 0) | ||
| 1885 | break; | ||
| 1886 | |||
| 1887 | if (partinfo->filename || partinfo->name) | ||
| 1888 | g_snprintf(buf, sizeof(buf), "\n[%s %s (%s)]\n", | ||
| 1889 | partinfo->filename ? partinfo->filename : | ||
| 1890 | partinfo->name, | ||
| 1891 | partinfo->content_type, | ||
| 1892 | to_human_readable(partinfo->content_size)); | ||
| 1893 | else | ||
| 1894 | g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n", | ||
| 1895 | partinfo->content_type, | ||
| 1896 | to_human_readable(partinfo->content_size)); | ||
| 1897 | part_str = conv_codeset_strdup(buf, CS_INTERNAL, encoding); | ||
| 1898 | if (!part_str) | ||
| 1899 | part_str = g_strdup(buf); | ||
| 1900 | |||
| 1901 | if (partinfo->mime_type == MIME_TEXT || | ||
| 1902 | partinfo->mime_type == MIME_TEXT_HTML) { | ||
| 1903 | if (!partinfo->main && | ||
| 1904 | partinfo->parent && | ||
| 1905 | partinfo->parent->children != partinfo) { | ||
| 1906 | fputs(part_str, destfp); | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | if ((tmpfp = procmime_get_text_content(partinfo, fp, encoding)) == NULL) { | ||
| 1910 | g_free(part_str); | ||
| 1911 | break; | ||
| 1912 | } | ||
| 1913 | while (fgets(buf, sizeof(buf), tmpfp) != NULL) | ||
| 1914 | fputs(buf, destfp); | ||
| 1915 | |||
| 1916 | fclose(tmpfp); | ||
| 1917 | } else if (partinfo->mime_type == MIME_MESSAGE_RFC822) { | ||
| 1918 | fputs(part_str, destfp); | ||
| 1919 | while (fgets(buf, sizeof(buf), fp) != NULL) | ||
| 1920 | if (buf[0] == '\r' || buf[0] == '\n') break; | ||
| 1921 | procmsg_write_headers(msginfo, partinfo, fp, destfp, encoding, all_headers); | ||
| 1922 | fputc('\n', destfp); | ||
| 1923 | } else if (partinfo->mime_type != MIME_MULTIPART) { | ||
| 1924 | fputs(part_str, destfp); | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | g_free(part_str); | ||
| 1928 | |||
| 1929 | if (partinfo->parent && partinfo->parent->content_type && | ||
| 1930 | !g_ascii_strcasecmp(partinfo->parent->content_type, | ||
| 1931 | "multipart/alternative")) | ||
| 1932 | partinfo = partinfo->parent->next; | ||
| 1933 | else | ||
| 1934 | partinfo = procmime_mimeinfo_next(partinfo); | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | if (fclose(destfp) == EOF) { | ||
| 1938 | FILE_OP_ERROR(dest, "fclose"); | ||
| 1939 | g_unlink(dest); | ||
| 1940 | ret = -1; | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | fclose(fp); | ||
| 1944 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 1945 | |||
| 1946 | return ret; | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | /** | ||
| 1950 | * procmsg_concat_partial_messages: | ||
| 1951 | * @mlist: list of MsgInfo* including message/partial messages. | ||
| 1952 | * @file: output file name of concatenated message. | ||
| 1953 | * | ||
| 1954 | * Concatenate @mlist which consists of message/partial messages and | ||
| 1955 | * output to @file. If @mlist has different partial id, the first one | ||
| 1956 | * is used. | ||
| 1957 | * | ||
| 1958 | * Return value: 0 on success, or -1 if failed. | ||
| 1959 | **/ | ||
| 1960 | gint procmsg_concat_partial_messages(GSList *mlist, const gchar *file) | ||
| 1961 | { | ||
| 1962 | static HeaderEntry hentry[] = {{"Content-Type:", NULL, FALSE}, | ||
| 1963 | {NULL, NULL, FALSE}}; | ||
| 1964 | FILE *fp; | ||
| 1965 | gchar buf[BUFFSIZE]; | ||
| 1966 | FILE *tmp_fp; | ||
| 1967 | gchar *part_id = NULL; | ||
| 1968 | gint total = 0; | ||
| 1969 | MsgInfo *msg_array[100] = {NULL}; | ||
| 1970 | MsgInfo *msginfo; | ||
| 1971 | MimeInfo *mimeinfo; | ||
| 1972 | GSList *cur; | ||
| 1973 | gint i; | ||
| 1974 | |||
| 1975 | g_return_val_if_fail(mlist != NULL, -1); | ||
| 1976 | g_return_val_if_fail(file != NULL, -1); | ||
| 1977 | |||
| 1978 | debug_print("procmsg_concat_partial_messages\n"); | ||
| 1979 | |||
| 1980 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 1981 | gint n = 0; | ||
| 1982 | gint t = 0; | ||
| 1983 | gchar *cur_id = NULL; | ||
| 1984 | |||
| 1985 | msginfo = (MsgInfo *)cur->data; | ||
| 1986 | |||
| 1987 | fp = procmsg_open_message_decrypted(msginfo, &mimeinfo); | ||
| 1988 | if (!fp) | ||
| 1989 | continue; | ||
| 1990 | if (!mimeinfo->content_type || | ||
| 1991 | g_ascii_strcasecmp(mimeinfo->content_type, "message/partial") != 0) | ||
| 1992 | goto skip; | ||
| 1993 | |||
| 1994 | rewind(fp); | ||
| 1995 | if (procheader_get_one_field(buf, sizeof(buf), fp, hentry) == -1) | ||
| 1996 | goto skip; | ||
| 1997 | |||
| 1998 | procmime_scan_content_type_partial(buf + strlen(hentry[0].name), | ||
| 1999 | &t, &cur_id, &n); | ||
| 2000 | if (n == 0 || n > 100 || t > 100 || (t > 0 && n > t)) { | ||
| 2001 | debug_print("bad partial number (%d/%d), skip\n", n, t); | ||
| 2002 | g_free(cur_id); | ||
| 2003 | goto skip; | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | debug_print("partial: %d/%d id=%s\n", n, t, cur_id); | ||
| 2007 | if (!part_id) | ||
| 2008 | part_id = g_strdup(cur_id); | ||
| 2009 | if (total == 0) | ||
| 2010 | total = t; | ||
| 2011 | |||
| 2012 | if ((t > 0 && total != t) || (total > 0 && n > total) || | ||
| 2013 | strcmp(part_id, cur_id) != 0) { | ||
| 2014 | debug_print("skip\n"); | ||
| 2015 | g_free(cur_id); | ||
| 2016 | goto skip; | ||
| 2017 | } | ||
| 2018 | |||
| 2019 | msg_array[n - 1] = msginfo; | ||
| 2020 | |||
| 2021 | g_free(cur_id); | ||
| 2022 | skip: | ||
| 2023 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 2024 | fclose(fp); | ||
| 2025 | } | ||
| 2026 | |||
| 2027 | if (!part_id) { | ||
| 2028 | debug_print("piece not found\n"); | ||
| 2029 | return -1; | ||
| 2030 | } | ||
| 2031 | |||
| 2032 | debug_print("part_id = %s , total = %d\n", part_id, total); | ||
| 2033 | g_free(part_id); | ||
| 2034 | |||
| 2035 | if (total == 0) { | ||
| 2036 | debug_print("total number not found\n"); | ||
| 2037 | return -1; | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | /* check if all pieces exist */ | ||
| 2041 | for (i = 0; i < total; i++) { | ||
| 2042 | if (msg_array[i] == NULL) { | ||
| 2043 | debug_print("message part %d not exist\n", i + 1); | ||
| 2044 | return -1; | ||
| 2045 | } | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | /* concatenate parts */ | ||
| 2049 | if ((tmp_fp = g_fopen(file, "wb")) == NULL) { | ||
| 2050 | FILE_OP_ERROR(file, "fopen"); | ||
| 2051 | return -1; | ||
| 2052 | } | ||
| 2053 | |||
| 2054 | for (i = 0; i < total; i++) { | ||
| 2055 | msginfo = msg_array[i]; | ||
| 2056 | off_t out_size; | ||
| 2057 | gint empty_line_size = 0; | ||
| 2058 | |||
| 2059 | fp = procmsg_open_message_decrypted(msginfo, &mimeinfo); | ||
| 2060 | if (!fp) { | ||
| 2061 | g_warning("cannot open message part %d\n", i + 1); | ||
| 2062 | fclose(tmp_fp); | ||
| 2063 | g_unlink(file); | ||
| 2064 | return -1; | ||
| 2065 | } | ||
| 2066 | |||
| 2067 | /* write out first headers */ | ||
| 2068 | if (i == 0) { | ||
| 2069 | rewind(fp); | ||
| 2070 | while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) { | ||
| 2071 | if (!g_ascii_strncasecmp(buf, "Content-", 8) || | ||
| 2072 | !g_ascii_strncasecmp(buf, "Subject", 7) || | ||
| 2073 | !g_ascii_strncasecmp(buf, "Message-ID", 10) || | ||
| 2074 | !g_ascii_strncasecmp(buf, "Encrypted", 9) || | ||
| 2075 | !g_ascii_strncasecmp(buf, "MIME-Version", 12)) | ||
| 2076 | continue; | ||
| 2077 | fputs(buf, tmp_fp); | ||
| 2078 | fputs("\n", tmp_fp); | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) != -1) { | ||
| 2082 | if (!g_ascii_strncasecmp(buf, "Content-", 8) || | ||
| 2083 | !g_ascii_strncasecmp(buf, "Subject", 7) || | ||
| 2084 | !g_ascii_strncasecmp(buf, "Message-ID", 10) || | ||
| 2085 | !g_ascii_strncasecmp(buf, "Encrypted", 9) || | ||
| 2086 | !g_ascii_strncasecmp(buf, "MIME-Version", 12)) { | ||
| 2087 | fputs(buf, tmp_fp); | ||
| 2088 | fputs("\n", tmp_fp); | ||
| 2089 | } | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | /* header-body separator */ | ||
| 2093 | fputs("\n", tmp_fp); | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | out_size = get_left_file_size(fp); | ||
| 2097 | if (out_size < 0) { | ||
| 2098 | g_warning("cannot tell left file size of part %d\n", i + 1); | ||
| 2099 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 2100 | fclose(fp); | ||
| 2101 | fclose(tmp_fp); | ||
| 2102 | g_unlink(file); | ||
| 2103 | return -1; | ||
| 2104 | } | ||
| 2105 | empty_line_size = get_last_empty_line_size(fp, out_size); | ||
| 2106 | if (empty_line_size < 0) { | ||
| 2107 | g_warning("cannot get last empty line size of part %d\n", i + 1); | ||
| 2108 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 2109 | fclose(fp); | ||
| 2110 | fclose(tmp_fp); | ||
| 2111 | g_unlink(file); | ||
| 2112 | return -1; | ||
| 2113 | } | ||
| 2114 | |||
| 2115 | if (append_file_part(fp, ftell(fp), out_size - empty_line_size, | ||
| 2116 | tmp_fp) < 0) { | ||
| 2117 | g_warning("write failed\n"); | ||
| 2118 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 2119 | fclose(fp); | ||
| 2120 | fclose(tmp_fp); | ||
| 2121 | g_unlink(file); | ||
| 2122 | return -1; | ||
| 2123 | } | ||
| 2124 | |||
| 2125 | procmime_mimeinfo_free_all(mimeinfo); | ||
| 2126 | fclose(fp); | ||
| 2127 | } | ||
| 2128 | |||
| 2129 | fclose(tmp_fp); | ||
| 2130 | |||
| 2131 | return 0; | ||
| 2132 | } | ||
| 2133 | |||
| 2134 | static gboolean procmsg_get_flags(FolderItem *item, gint num, | ||
| 2135 | MsgPermFlags *flags) | ||
| 2136 | { | ||
| 2137 | FILE *fp; | ||
| 2138 | guint32 idata; | ||
| 2139 | gint read_num; | ||
| 2140 | MsgPermFlags perm_flags; | ||
| 2141 | gboolean found = FALSE; | ||
| 2142 | GSList *cur; | ||
| 2143 | |||
| 2144 | if ((fp = procmsg_open_mark_file(item, DATA_READ)) == NULL) | ||
| 2145 | return FALSE; | ||
| 2146 | |||
| 2147 | while (fread(&idata, sizeof(idata), 1, fp) == 1) { | ||
| 2148 | read_num = idata; | ||
| 2149 | if (fread(&idata, sizeof(idata), 1, fp) != 1) | ||
| 2150 | break; | ||
| 2151 | perm_flags = idata; | ||
| 2152 | if (read_num == num) { | ||
| 2153 | *flags = perm_flags; | ||
| 2154 | found = TRUE; | ||
| 2155 | break; | ||
| 2156 | } | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | fclose(fp); | ||
| 2160 | if (found) | ||
| 2161 | return TRUE; | ||
| 2162 | |||
| 2163 | for (cur = item->mark_queue; cur != NULL; cur = cur->next) { | ||
| 2164 | MsgFlagInfo *flaginfo = (MsgFlagInfo *)cur->data; | ||
| 2165 | |||
| 2166 | if (flaginfo->msgnum == num) { | ||
| 2167 | *flags = flaginfo->flags.perm_flags; | ||
| 2168 | found = TRUE; | ||
| 2169 | break; | ||
| 2170 | } | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | return found; | ||
| 2174 | } | ||
| 2175 | |||
| 2176 | MsgInfo *procmsg_get_msginfo(FolderItem *item, gint num) | ||
| 2177 | { | ||
| 2178 | MsgInfo *msginfo; | ||
| 2179 | FolderType type; | ||
| 2180 | |||
| 2181 | g_return_val_if_fail(item->folder != NULL, NULL); | ||
| 2182 | |||
| 2183 | msginfo = folder_item_get_msginfo(item, num); | ||
| 2184 | if (!msginfo) | ||
| 2185 | return NULL; | ||
| 2186 | |||
| 2187 | type = FOLDER_TYPE(item->folder); | ||
| 2188 | if (type == F_MH || type == F_IMAP) { | ||
| 2189 | if (item->stype == F_QUEUE) { | ||
| 2190 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED); | ||
| 2191 | } else if (item->stype == F_DRAFT) { | ||
| 2192 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT); | ||
| 2193 | } | ||
| 2194 | } | ||
| 2195 | if (type == F_IMAP) { | ||
| 2196 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_IMAP); | ||
| 2197 | } else if (type == F_NEWS) { | ||
| 2198 | MSG_SET_TMP_FLAGS(msginfo->flags, MSG_NEWS); | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | if (type == F_MH || type == F_NEWS) { | ||
| 2202 | MsgPermFlags flags = 0; | ||
| 2203 | if (procmsg_get_flags(item, num, &flags)) | ||
| 2204 | msginfo->flags.perm_flags = flags; | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | return msginfo; | ||
| 2208 | } | ||
| 2209 | |||
| 2210 | MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo) | ||
| 2211 | { | ||
| 2212 | MsgInfo *newmsginfo; | ||
| 2213 | |||
| 2214 | if (msginfo == NULL) return NULL; | ||
| 2215 | |||
| 2216 | newmsginfo = g_new0(MsgInfo, 1); | ||
| 2217 | |||
| 2218 | #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb | ||
| 2219 | #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \ | ||
| 2220 | g_strdup(msginfo->mmb) : NULL | ||
| 2221 | |||
| 2222 | MEMBCOPY(msgnum); | ||
| 2223 | MEMBCOPY(size); | ||
| 2224 | MEMBCOPY(mtime); | ||
| 2225 | MEMBCOPY(date_t); | ||
| 2226 | |||
| 2227 | MEMBCOPY(flags); | ||
| 2228 | |||
| 2229 | MEMBDUP(fromname); | ||
| 2230 | |||
| 2231 | MEMBDUP(date); | ||
| 2232 | MEMBDUP(from); | ||
| 2233 | MEMBDUP(to); | ||
| 2234 | MEMBDUP(cc); | ||
| 2235 | MEMBDUP(newsgroups); | ||
| 2236 | MEMBDUP(subject); | ||
| 2237 | MEMBDUP(msgid); | ||
| 2238 | MEMBDUP(inreplyto); | ||
| 2239 | |||
| 2240 | MEMBCOPY(folder); | ||
| 2241 | MEMBCOPY(to_folder); | ||
| 2242 | |||
| 2243 | MEMBDUP(xface); | ||
| 2244 | |||
| 2245 | MEMBDUP(file_path); | ||
| 2246 | |||
| 2247 | if (msginfo->encinfo) { | ||
| 2248 | newmsginfo->encinfo = g_new0(MsgEncryptInfo, 1); | ||
| 2249 | MEMBDUP(encinfo->plaintext_file); | ||
| 2250 | MEMBDUP(encinfo->sigstatus); | ||
| 2251 | MEMBDUP(encinfo->sigstatus_full); | ||
| 2252 | MEMBCOPY(encinfo->decryption_failed); | ||
| 2253 | } | ||
| 2254 | |||
| 2255 | return newmsginfo; | ||
| 2256 | } | ||
| 2257 | |||
| 2258 | MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo) | ||
| 2259 | { | ||
| 2260 | MsgInfo *full_msginfo; | ||
| 2261 | gchar *file; | ||
| 2262 | |||
| 2263 | if (msginfo == NULL) return NULL; | ||
| 2264 | |||
| 2265 | file = procmsg_get_message_file(msginfo); | ||
| 2266 | if (!file) { | ||
| 2267 | g_warning("procmsg_msginfo_get_full_info(): can't get message file.\n"); | ||
| 2268 | return NULL; | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE); | ||
| 2272 | g_free(file); | ||
| 2273 | if (!full_msginfo) return NULL; | ||
| 2274 | |||
| 2275 | full_msginfo->msgnum = msginfo->msgnum; | ||
| 2276 | full_msginfo->size = msginfo->size; | ||
| 2277 | full_msginfo->mtime = msginfo->mtime; | ||
| 2278 | full_msginfo->folder = msginfo->folder; | ||
| 2279 | full_msginfo->to_folder = msginfo->to_folder; | ||
| 2280 | |||
| 2281 | full_msginfo->file_path = g_strdup(msginfo->file_path); | ||
| 2282 | |||
| 2283 | if (msginfo->encinfo) { | ||
| 2284 | full_msginfo->encinfo = g_new0(MsgEncryptInfo, 1); | ||
| 2285 | full_msginfo->encinfo->plaintext_file = | ||
| 2286 | g_strdup(msginfo->encinfo->plaintext_file); | ||
| 2287 | full_msginfo->encinfo->sigstatus = | ||
| 2288 | g_strdup(msginfo->encinfo->sigstatus); | ||
| 2289 | full_msginfo->encinfo->sigstatus_full = | ||
| 2290 | g_strdup(msginfo->encinfo->sigstatus_full); | ||
| 2291 | full_msginfo->encinfo->decryption_failed = | ||
| 2292 | msginfo->encinfo->decryption_failed; | ||
| 2293 | } | ||
| 2294 | |||
| 2295 | return full_msginfo; | ||
| 2296 | } | ||
| 2297 | |||
| 2298 | gboolean procmsg_msginfo_equal(MsgInfo *msginfo_a, MsgInfo *msginfo_b) | ||
| 2299 | { | ||
| 2300 | if (!msginfo_a || !msginfo_b) | ||
| 2301 | return FALSE; | ||
| 2302 | |||
| 2303 | if (msginfo_a == msginfo_b) | ||
| 2304 | return TRUE; | ||
| 2305 | |||
| 2306 | if (msginfo_a->folder == msginfo_b->folder && | ||
| 2307 | msginfo_a->msgnum == msginfo_b->msgnum && | ||
| 2308 | msginfo_a->size == msginfo_b->size && | ||
| 2309 | msginfo_a->mtime == msginfo_b->mtime) | ||
| 2310 | return TRUE; | ||
| 2311 | |||
| 2312 | return FALSE; | ||
| 2313 | } | ||
| 2314 | |||
| 2315 | void procmsg_msginfo_free(MsgInfo *msginfo) | ||
| 2316 | { | ||
| 2317 | if (msginfo == NULL) return; | ||
| 2318 | |||
| 2319 | g_free(msginfo->xface); | ||
| 2320 | |||
| 2321 | g_free(msginfo->fromname); | ||
| 2322 | |||
| 2323 | g_free(msginfo->date); | ||
| 2324 | g_free(msginfo->from); | ||
| 2325 | g_free(msginfo->to); | ||
| 2326 | g_free(msginfo->cc); | ||
| 2327 | g_free(msginfo->newsgroups); | ||
| 2328 | g_free(msginfo->subject); | ||
| 2329 | g_free(msginfo->msgid); | ||
| 2330 | g_free(msginfo->inreplyto); | ||
| 2331 | |||
| 2332 | slist_free_strings(msginfo->references); | ||
| 2333 | g_slist_free(msginfo->references); | ||
| 2334 | |||
| 2335 | g_free(msginfo->file_path); | ||
| 2336 | |||
| 2337 | if (msginfo->encinfo) { | ||
| 2338 | g_free(msginfo->encinfo->plaintext_file); | ||
| 2339 | g_free(msginfo->encinfo->sigstatus); | ||
| 2340 | g_free(msginfo->encinfo->sigstatus_full); | ||
| 2341 | g_free(msginfo->encinfo); | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | g_free(msginfo); | ||
| 2345 | } | ||
| 2346 | |||
| 2347 | gint procmsg_cmp_msgnum_for_sort(gconstpointer a, gconstpointer b) | ||
| 2348 | { | ||
| 2349 | const MsgInfo *msginfo1 = a; | ||
| 2350 | const MsgInfo *msginfo2 = b; | ||
| 2351 | |||
| 2352 | if (!msginfo1 || !msginfo2) | ||
| 2353 | return 0; | ||
| 2354 | |||
| 2355 | return msginfo1->msgnum - msginfo2->msgnum; | ||
| 2356 | } | ||
| 2357 | |||
| 2358 | #define CMP_FUNC_DEF(func_name, val) \ | ||
| 2359 | static gint func_name(gconstpointer a, gconstpointer b) \ | ||
| 2360 | { \ | ||
| 2361 | const MsgInfo *msginfo1 = a; \ | ||
| 2362 | const MsgInfo *msginfo2 = b; \ | ||
| 2363 | gint ret; \ | ||
| 2364 | \ | ||
| 2365 | if (!msginfo1 || !msginfo2) \ | ||
| 2366 | return 0; \ | ||
| 2367 | \ | ||
| 2368 | ret = (val); \ | ||
| 2369 | if (ret == 0) \ | ||
| 2370 | ret = msginfo1->date_t - msginfo2->date_t; \ | ||
| 2371 | \ | ||
| 2372 | return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); \ | ||
| 2373 | } | ||
| 2374 | |||
| 2375 | CMP_FUNC_DEF(procmsg_cmp_by_mark, | ||
| 2376 | MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags)) | ||
| 2377 | CMP_FUNC_DEF(procmsg_cmp_by_unread, | ||
| 2378 | MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags)) | ||
| 2379 | CMP_FUNC_DEF(procmsg_cmp_by_mime, | ||
| 2380 | MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags)) | ||
| 2381 | CMP_FUNC_DEF(procmsg_cmp_by_label, | ||
| 2382 | MSG_GET_COLORLABEL(msginfo1->flags) - | ||
| 2383 | MSG_GET_COLORLABEL(msginfo2->flags)) | ||
| 2384 | CMP_FUNC_DEF(procmsg_cmp_by_size, msginfo1->size - msginfo2->size) | ||
| 2385 | |||
| 2386 | #undef CMP_FUNC_DEF | ||
| 2387 | #define CMP_FUNC_DEF(func_name, val) \ | ||
| 2388 | static gint func_name(gconstpointer a, gconstpointer b) \ | ||
| 2389 | { \ | ||
| 2390 | const MsgInfo *msginfo1 = a; \ | ||
| 2391 | const MsgInfo *msginfo2 = b; \ | ||
| 2392 | \ | ||
| 2393 | if (!msginfo1 || !msginfo2) \ | ||
| 2394 | return 0; \ | ||
| 2395 | \ | ||
| 2396 | return (val) * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); \ | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | CMP_FUNC_DEF(procmsg_cmp_by_number, msginfo1->msgnum - msginfo2->msgnum) | ||
| 2400 | CMP_FUNC_DEF(procmsg_cmp_by_date, msginfo1->date_t - msginfo2->date_t) | ||
| 2401 | |||
| 2402 | #undef CMP_FUNC_DEF | ||
| 2403 | #define CMP_FUNC_DEF(func_name, var_name) \ | ||
| 2404 | static gint func_name(gconstpointer a, gconstpointer b) \ | ||
| 2405 | { \ | ||
| 2406 | const MsgInfo *msginfo1 = a; \ | ||
| 2407 | const MsgInfo *msginfo2 = b; \ | ||
| 2408 | gint ret; \ | ||
| 2409 | \ | ||
| 2410 | if (!msginfo1->var_name) \ | ||
| 2411 | return (msginfo2->var_name != NULL) * \ | ||
| 2412 | (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1);\ | ||
| 2413 | if (!msginfo2->var_name) \ | ||
| 2414 | return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); \ | ||
| 2415 | \ | ||
| 2416 | ret = g_ascii_strcasecmp \ | ||
| 2417 | (msginfo1->var_name, msginfo2->var_name); \ | ||
| 2418 | if (ret == 0) \ | ||
| 2419 | ret = msginfo1->date_t - msginfo2->date_t; \ | ||
| 2420 | \ | ||
| 2421 | return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); \ | ||
| 2422 | } | ||
| 2423 | |||
| 2424 | CMP_FUNC_DEF(procmsg_cmp_by_from, fromname) | ||
| 2425 | CMP_FUNC_DEF(procmsg_cmp_by_to, to) | ||
| 2426 | |||
| 2427 | #undef CMP_FUNC_DEF | ||
| 2428 | |||
| 2429 | static gint procmsg_cmp_by_subject(gconstpointer a, gconstpointer b) | ||
| 2430 | { | ||
| 2431 | const MsgInfo *msginfo1 = a; | ||
| 2432 | const MsgInfo *msginfo2 = b; | ||
| 2433 | gint ret; | ||
| 2434 | |||
| 2435 | if (!msginfo1->subject) | ||
| 2436 | return (msginfo2->subject != NULL) * | ||
| 2437 | (cmp_func_sort_type == SORT_ASCENDING ? -1 : 1); | ||
| 2438 | if (!msginfo2->subject) | ||
| 2439 | return (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); | ||
| 2440 | |||
| 2441 | ret = subject_compare_for_sort(msginfo1->subject, msginfo2->subject); | ||
| 2442 | if (ret == 0) | ||
| 2443 | ret = msginfo1->date_t - msginfo2->date_t; | ||
| 2444 | |||
| 2445 | return ret * (cmp_func_sort_type == SORT_ASCENDING ? 1 : -1); | ||
| 2446 | } | ||
diff --git a/libsylph/procmsg.h b/libsylph/procmsg.h new file mode 100644 index 0000000..77f0d8a --- /dev/null +++ b/libsylph/procmsg.h | |||
| @@ -0,0 +1,355 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2013 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __PROCMSG_H__ | ||
| 21 | #define __PROCMSG_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <time.h> | ||
| 30 | #include <sys/types.h> | ||
| 31 | #include <string.h> | ||
| 32 | |||
| 33 | typedef struct _MsgInfo MsgInfo; | ||
| 34 | typedef struct _MsgFlags MsgFlags; | ||
| 35 | typedef struct _MsgFileInfo MsgFileInfo; | ||
| 36 | typedef struct _MsgEncryptInfo MsgEncryptInfo; | ||
| 37 | |||
| 38 | #include "folder.h" | ||
| 39 | #include "procmime.h" | ||
| 40 | #include "utils.h" | ||
| 41 | |||
| 42 | typedef enum | ||
| 43 | { | ||
| 44 | DATA_READ, | ||
| 45 | DATA_WRITE, | ||
| 46 | DATA_APPEND | ||
| 47 | } DataOpenMode; | ||
| 48 | |||
| 49 | #define MSG_NEW (1U << 0) | ||
| 50 | #define MSG_UNREAD (1U << 1) | ||
| 51 | #define MSG_MARKED (1U << 2) | ||
| 52 | #define MSG_DELETED (1U << 3) | ||
| 53 | #define MSG_REPLIED (1U << 4) | ||
| 54 | #define MSG_FORWARDED (1U << 5) | ||
| 55 | |||
| 56 | #define MSG_CLABEL_SBIT (7) /* start bit of color label */ | ||
| 57 | #define MAKE_MSG_CLABEL(h, m, l) (((h) << (MSG_CLABEL_SBIT + 2)) | \ | ||
| 58 | ((m) << (MSG_CLABEL_SBIT + 1)) | \ | ||
| 59 | ((l) << (MSG_CLABEL_SBIT + 0))) | ||
| 60 | |||
| 61 | #define MSG_CLABEL_NONE MAKE_MSG_CLABEL(0U, 0U, 0U) | ||
| 62 | #define MSG_CLABEL_1 MAKE_MSG_CLABEL(0U, 0U, 1U) | ||
| 63 | #define MSG_CLABEL_2 MAKE_MSG_CLABEL(0U, 1U, 0U) | ||
| 64 | #define MSG_CLABEL_3 MAKE_MSG_CLABEL(0U, 1U, 1U) | ||
| 65 | #define MSG_CLABEL_4 MAKE_MSG_CLABEL(1U, 0U, 0U) | ||
| 66 | #define MSG_CLABEL_5 MAKE_MSG_CLABEL(1U, 0U, 1U) | ||
| 67 | #define MSG_CLABEL_6 MAKE_MSG_CLABEL(1U, 1U, 0U) | ||
| 68 | #define MSG_CLABEL_7 MAKE_MSG_CLABEL(1U, 1U, 1U) | ||
| 69 | |||
| 70 | #define MSG_CLABEL_ORANGE MSG_CLABEL_1 | ||
| 71 | #define MSG_CLABEL_RED MSG_CLABEL_2 | ||
| 72 | #define MSG_CLABEL_PINK MSG_CLABEL_3 | ||
| 73 | #define MSG_CLABEL_SKYBLUE MSG_CLABEL_4 | ||
| 74 | #define MSG_CLABEL_BLUE MSG_CLABEL_5 | ||
| 75 | #define MSG_CLABEL_GREEN MSG_CLABEL_6 | ||
| 76 | #define MSG_CLABEL_BROWN MSG_CLABEL_7 | ||
| 77 | |||
| 78 | /* RESERVED */ | ||
| 79 | #define MSG_RESERVED (1U << 31) | ||
| 80 | |||
| 81 | #define MSG_CLABEL_FLAG_MASK (MSG_CLABEL_7) | ||
| 82 | |||
| 83 | typedef guint32 MsgPermFlags; | ||
| 84 | |||
| 85 | #define MSG_MOVE (1U << 0) | ||
| 86 | #define MSG_COPY (1U << 1) | ||
| 87 | #define MSG_QUEUED (1U << 16) | ||
| 88 | #define MSG_DRAFT (1U << 17) | ||
| 89 | #define MSG_ENCRYPTED (1U << 18) | ||
| 90 | #define MSG_IMAP (1U << 19) | ||
| 91 | #define MSG_NEWS (1U << 20) | ||
| 92 | #define MSG_SIGNED (1U << 21) | ||
| 93 | #define MSG_MIME_HTML (1U << 26) | ||
| 94 | #define MSG_FLAG_CHANGED (1U << 27) | ||
| 95 | #define MSG_CACHED (1U << 28) | ||
| 96 | #define MSG_MIME (1U << 29) | ||
| 97 | #define MSG_INVALID (1U << 30) | ||
| 98 | #define MSG_RECEIVED (1U << 31) | ||
| 99 | |||
| 100 | #define MSG_CACHED_FLAG_MASK (MSG_MIME|MSG_MIME_HTML) | ||
| 101 | |||
| 102 | typedef guint32 MsgTmpFlags; | ||
| 103 | |||
| 104 | #define MSG_SET_FLAGS(msg, flags) { (msg) |= (flags); } | ||
| 105 | #define MSG_UNSET_FLAGS(msg, flags) { (msg) &= ~(flags); } | ||
| 106 | #define MSG_SET_PERM_FLAGS(msg, flags) \ | ||
| 107 | MSG_SET_FLAGS((msg).perm_flags, flags) | ||
| 108 | #define MSG_SET_TMP_FLAGS(msg, flags) \ | ||
| 109 | MSG_SET_FLAGS((msg).tmp_flags, flags) | ||
| 110 | #define MSG_UNSET_PERM_FLAGS(msg, flags) \ | ||
| 111 | MSG_UNSET_FLAGS((msg).perm_flags, flags) | ||
| 112 | #define MSG_UNSET_TMP_FLAGS(msg, flags) \ | ||
| 113 | MSG_UNSET_FLAGS((msg).tmp_flags, flags) | ||
| 114 | |||
| 115 | #define MSG_IS_NEW(msg) (((msg).perm_flags & MSG_NEW) != 0) | ||
| 116 | #define MSG_IS_UNREAD(msg) (((msg).perm_flags & MSG_UNREAD) != 0) | ||
| 117 | #define MSG_IS_MARKED(msg) (((msg).perm_flags & MSG_MARKED) != 0) | ||
| 118 | #define MSG_IS_DELETED(msg) (((msg).perm_flags & MSG_DELETED) != 0) | ||
| 119 | #define MSG_IS_REPLIED(msg) (((msg).perm_flags & MSG_REPLIED) != 0) | ||
| 120 | #define MSG_IS_FORWARDED(msg) (((msg).perm_flags & MSG_FORWARDED) != 0) | ||
| 121 | |||
| 122 | #define MSG_GET_COLORLABEL(msg) (((msg).perm_flags & MSG_CLABEL_FLAG_MASK)) | ||
| 123 | #define MSG_GET_COLORLABEL_VALUE(msg) (MSG_GET_COLORLABEL(msg) >> MSG_CLABEL_SBIT) | ||
| 124 | #define MSG_SET_COLORLABEL_VALUE(msg, val) \ | ||
| 125 | MSG_SET_PERM_FLAGS(msg, ((((guint)(val)) & 7) << MSG_CLABEL_SBIT)) | ||
| 126 | |||
| 127 | #define MSG_IS_MOVE(msg) (((msg).tmp_flags & MSG_MOVE) != 0) | ||
| 128 | #define MSG_IS_COPY(msg) (((msg).tmp_flags & MSG_COPY) != 0) | ||
| 129 | |||
| 130 | #define MSG_IS_QUEUED(msg) (((msg).tmp_flags & MSG_QUEUED) != 0) | ||
| 131 | #define MSG_IS_DRAFT(msg) (((msg).tmp_flags & MSG_DRAFT) != 0) | ||
| 132 | #define MSG_IS_ENCRYPTED(msg) (((msg).tmp_flags & MSG_ENCRYPTED) != 0) | ||
| 133 | #define MSG_IS_IMAP(msg) (((msg).tmp_flags & MSG_IMAP) != 0) | ||
| 134 | #define MSG_IS_NEWS(msg) (((msg).tmp_flags & MSG_NEWS) != 0) | ||
| 135 | #define MSG_IS_SIGNED(msg) (((msg).tmp_flags & MSG_SIGNED) != 0) | ||
| 136 | #define MSG_IS_MIME_HTML(msg) (((msg).tmp_flags & MSG_MIME_HTML) != 0) | ||
| 137 | #define MSG_IS_FLAG_CHANGED(msg) (((msg).tmp_flags & MSG_FLAG_CHANGED) != 0) | ||
| 138 | #define MSG_IS_CACHED(msg) (((msg).tmp_flags & MSG_CACHED) != 0) | ||
| 139 | #define MSG_IS_MIME(msg) (((msg).tmp_flags & MSG_MIME) != 0) | ||
| 140 | #define MSG_IS_INVALID(msg) (((msg).tmp_flags & MSG_INVALID) != 0) | ||
| 141 | #define MSG_IS_RECEIVED(msg) (((msg).tmp_flags & MSG_RECEIVED) != 0) | ||
| 142 | |||
| 143 | #define WRITE_CACHE_DATA_INT(n, fp) \ | ||
| 144 | { \ | ||
| 145 | guint32 idata; \ | ||
| 146 | \ | ||
| 147 | idata = (guint32)n; \ | ||
| 148 | fwrite(&idata, sizeof(idata), 1, fp); \ | ||
| 149 | } | ||
| 150 | |||
| 151 | #define WRITE_CACHE_DATA(data, fp) \ | ||
| 152 | { \ | ||
| 153 | size_t len; \ | ||
| 154 | \ | ||
| 155 | if (data == NULL) { \ | ||
| 156 | len = 0; \ | ||
| 157 | WRITE_CACHE_DATA_INT(len, fp); \ | ||
| 158 | } else { \ | ||
| 159 | len = strlen(data); \ | ||
| 160 | WRITE_CACHE_DATA_INT(len, fp); \ | ||
| 161 | if (len > 0) \ | ||
| 162 | fwrite(data, len, 1, fp); \ | ||
| 163 | } \ | ||
| 164 | } | ||
| 165 | |||
| 166 | struct _MsgFlags | ||
| 167 | { | ||
| 168 | MsgPermFlags perm_flags; | ||
| 169 | MsgTmpFlags tmp_flags; | ||
| 170 | }; | ||
| 171 | |||
| 172 | struct _MsgInfo | ||
| 173 | { | ||
| 174 | guint msgnum; | ||
| 175 | gsize size; | ||
| 176 | stime_t mtime; | ||
| 177 | stime_t date_t; | ||
| 178 | |||
| 179 | MsgFlags flags; | ||
| 180 | |||
| 181 | gchar *fromname; | ||
| 182 | |||
| 183 | gchar *date; | ||
| 184 | gchar *from; | ||
| 185 | gchar *to; | ||
| 186 | gchar *cc; | ||
| 187 | gchar *newsgroups; | ||
| 188 | gchar *subject; | ||
| 189 | gchar *msgid; | ||
| 190 | gchar *inreplyto; | ||
| 191 | |||
| 192 | GSList *references; | ||
| 193 | |||
| 194 | FolderItem *folder; | ||
| 195 | FolderItem *to_folder; | ||
| 196 | |||
| 197 | gchar *xface; | ||
| 198 | |||
| 199 | /* used only for temporary messages */ | ||
| 200 | gchar *file_path; | ||
| 201 | |||
| 202 | /* used only for encrypted (and signed) messages */ | ||
| 203 | MsgEncryptInfo *encinfo; | ||
| 204 | }; | ||
| 205 | |||
| 206 | struct _MsgFileInfo | ||
| 207 | { | ||
| 208 | gchar *file; | ||
| 209 | MsgFlags *flags; | ||
| 210 | }; | ||
| 211 | |||
| 212 | struct _MsgEncryptInfo | ||
| 213 | { | ||
| 214 | gchar *plaintext_file; | ||
| 215 | gchar *sigstatus; | ||
| 216 | gchar *sigstatus_full; | ||
| 217 | gboolean decryption_failed; | ||
| 218 | }; | ||
| 219 | |||
| 220 | typedef FILE * (*DecryptMessageFunc) (MsgInfo *msginfo, | ||
| 221 | MimeInfo **mimeinfo); | ||
| 222 | |||
| 223 | GHashTable *procmsg_msg_hash_table_create (GSList *mlist); | ||
| 224 | void procmsg_msg_hash_table_append (GHashTable *msg_table, | ||
| 225 | GSList *mlist); | ||
| 226 | GHashTable *procmsg_to_folder_hash_table_create (GSList *mlist); | ||
| 227 | |||
| 228 | gint procmsg_read_cache_data_str (FILE *fp, | ||
| 229 | gchar **str); | ||
| 230 | |||
| 231 | GSList *procmsg_read_cache (FolderItem *item, | ||
| 232 | gboolean scan_file); | ||
| 233 | void procmsg_set_flags (GSList *mlist, | ||
| 234 | FolderItem *item); | ||
| 235 | void procmsg_mark_all_read (FolderItem *item); | ||
| 236 | GSList *procmsg_sort_msg_list (GSList *mlist, | ||
| 237 | FolderSortKey sort_key, | ||
| 238 | FolderSortType sort_type); | ||
| 239 | gint procmsg_get_last_num_in_msg_list(GSList *mlist); | ||
| 240 | void procmsg_msg_list_free (GSList *mlist); | ||
| 241 | |||
| 242 | void procmsg_write_cache (MsgInfo *msginfo, | ||
| 243 | FILE *fp); | ||
| 244 | void procmsg_write_flags (MsgInfo *msginfo, | ||
| 245 | FILE *fp); | ||
| 246 | void procmsg_write_cache_list (FolderItem *item, | ||
| 247 | GSList *mlist); | ||
| 248 | void procmsg_write_flags_list (FolderItem *item, | ||
| 249 | GSList *mlist); | ||
| 250 | void procmsg_write_flags_for_multiple_folders | ||
| 251 | (GSList *mlist); | ||
| 252 | |||
| 253 | void procmsg_flaginfo_list_free (GSList *flaglist); | ||
| 254 | |||
| 255 | void procmsg_flush_mark_queue (FolderItem *item, | ||
| 256 | FILE *fp); | ||
| 257 | void procmsg_add_mark_queue (FolderItem *item, | ||
| 258 | gint num, | ||
| 259 | MsgFlags flags); | ||
| 260 | void procmsg_flush_cache_queue (FolderItem *item, | ||
| 261 | FILE *fp); | ||
| 262 | void procmsg_add_cache_queue (FolderItem *item, | ||
| 263 | gint num, | ||
| 264 | MsgInfo *msginfo); | ||
| 265 | |||
| 266 | gboolean procmsg_flush_folder (FolderItem *item); | ||
| 267 | void procmsg_flush_folder_foreach (GHashTable *folder_table); | ||
| 268 | |||
| 269 | void procmsg_add_flags (FolderItem *item, | ||
| 270 | gint num, | ||
| 271 | MsgFlags flags); | ||
| 272 | |||
| 273 | void procmsg_get_mark_sum (FolderItem *item, | ||
| 274 | gint *new, | ||
| 275 | gint *unread, | ||
| 276 | gint *total, | ||
| 277 | gint *min, | ||
| 278 | gint *max, | ||
| 279 | gint first); | ||
| 280 | |||
| 281 | FILE *procmsg_open_data_file (const gchar *file, | ||
| 282 | guint version, | ||
| 283 | DataOpenMode mode, | ||
| 284 | gchar *buf, | ||
| 285 | size_t buf_size); | ||
| 286 | |||
| 287 | FILE *procmsg_open_cache_file (FolderItem *item, | ||
| 288 | DataOpenMode mode); | ||
| 289 | FILE *procmsg_open_mark_file (FolderItem *item, | ||
| 290 | DataOpenMode mode); | ||
| 291 | |||
| 292 | void procmsg_clear_cache (FolderItem *item); | ||
| 293 | void procmsg_clear_mark (FolderItem *item); | ||
| 294 | |||
| 295 | GNode *procmsg_get_thread_tree (GSList *mlist); | ||
| 296 | guint procmsg_get_thread_date (GNode *node); | ||
| 297 | |||
| 298 | gint procmsg_move_messages (GSList *mlist); | ||
| 299 | gint procmsg_copy_messages (GSList *mlist); | ||
| 300 | |||
| 301 | gint procmsg_add_messages_from_queue (FolderItem *dest, | ||
| 302 | GSList *mlist, | ||
| 303 | gboolean is_move); | ||
| 304 | |||
| 305 | gchar *procmsg_get_message_file_path (MsgInfo *msginfo); | ||
| 306 | gchar *procmsg_get_message_file (MsgInfo *msginfo); | ||
| 307 | GSList *procmsg_get_message_file_list (GSList *mlist); | ||
| 308 | void procmsg_message_file_list_free (GSList *file_list); | ||
| 309 | FILE *procmsg_open_message (MsgInfo *msginfo); | ||
| 310 | |||
| 311 | void procmsg_set_decrypt_message_func (DecryptMessageFunc func); | ||
| 312 | void procmsg_set_auto_decrypt_message (gboolean enabled); | ||
| 313 | FILE *procmsg_open_message_decrypted (MsgInfo *msginfo, | ||
| 314 | MimeInfo **mimeinfo); | ||
| 315 | |||
| 316 | gboolean procmsg_msg_exist (MsgInfo *msginfo); | ||
| 317 | |||
| 318 | gboolean procmsg_trash_messages_exist (void); | ||
| 319 | void procmsg_empty_trash (FolderItem *trash); | ||
| 320 | void procmsg_empty_all_trash (void); | ||
| 321 | |||
| 322 | void procmsg_remove_all_cached_messages | ||
| 323 | (Folder *folder); | ||
| 324 | |||
| 325 | gint procmsg_save_to_outbox (FolderItem *outbox, | ||
| 326 | const gchar *file); | ||
| 327 | void procmsg_print_message (MsgInfo *msginfo, | ||
| 328 | const gchar *cmdline, | ||
| 329 | gboolean all_headers); | ||
| 330 | void procmsg_print_message_part (MsgInfo *msginfo, | ||
| 331 | MimeInfo *partinfo, | ||
| 332 | const gchar *cmdline, | ||
| 333 | gboolean all_headers); | ||
| 334 | |||
| 335 | gint procmsg_save_message_as_text (MsgInfo *msginfo, | ||
| 336 | const gchar *dest, | ||
| 337 | const gchar *encoding, | ||
| 338 | gboolean all_headers); | ||
| 339 | |||
| 340 | gint procmsg_concat_partial_messages (GSList *mlist, | ||
| 341 | const gchar *file); | ||
| 342 | |||
| 343 | MsgInfo *procmsg_get_msginfo (FolderItem *item, | ||
| 344 | gint num); | ||
| 345 | |||
| 346 | MsgInfo *procmsg_msginfo_copy (MsgInfo *msginfo); | ||
| 347 | MsgInfo *procmsg_msginfo_get_full_info (MsgInfo *msginfo); | ||
| 348 | gboolean procmsg_msginfo_equal (MsgInfo *msginfo_a, | ||
| 349 | MsgInfo *msginfo_b); | ||
| 350 | void procmsg_msginfo_free (MsgInfo *msginfo); | ||
| 351 | |||
| 352 | gint procmsg_cmp_msgnum_for_sort (gconstpointer a, | ||
| 353 | gconstpointer b); | ||
| 354 | |||
| 355 | #endif /* __PROCMSG_H__ */ | ||
diff --git a/libsylph/quoted-printable.c b/libsylph/quoted-printable.c new file mode 100644 index 0000000..adcdb05 --- /dev/null +++ b/libsylph/quoted-printable.c | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <glib.h> | ||
| 21 | #include <ctype.h> | ||
| 22 | |||
| 23 | static gboolean get_hex_value(guchar *out, gchar c1, gchar c2); | ||
| 24 | static void get_hex_str(gchar *out, guchar ch); | ||
| 25 | |||
| 26 | #define MAX_LINELEN 76 | ||
| 27 | |||
| 28 | #define IS_LBREAK(p) \ | ||
| 29 | (*(p) == '\0' || *(p) == '\n' || (*(p) == '\r' && *((p) + 1) == '\n')) | ||
| 30 | |||
| 31 | #define SOFT_LBREAK_IF_REQUIRED(n) \ | ||
| 32 | if (len + (n) > MAX_LINELEN || \ | ||
| 33 | (len + (n) == MAX_LINELEN && (!IS_LBREAK(inp + 1)))) { \ | ||
| 34 | *outp++ = '='; \ | ||
| 35 | *outp++ = '\n'; \ | ||
| 36 | len = 0; \ | ||
| 37 | } | ||
| 38 | |||
| 39 | void qp_encode_line(gchar *out, const guchar *in) | ||
| 40 | { | ||
| 41 | const guchar *inp = in; | ||
| 42 | gchar *outp = out; | ||
| 43 | guchar ch; | ||
| 44 | gint len = 0; | ||
| 45 | |||
| 46 | while (*inp != '\0') { | ||
| 47 | ch = *inp; | ||
| 48 | |||
| 49 | if (IS_LBREAK(inp)) { | ||
| 50 | *outp++ = '\n'; | ||
| 51 | len = 0; | ||
| 52 | if (*inp == '\r') | ||
| 53 | inp++; | ||
| 54 | inp++; | ||
| 55 | } else if (ch == '\t' || ch == ' ') { | ||
| 56 | if (IS_LBREAK(inp + 1)) { | ||
| 57 | SOFT_LBREAK_IF_REQUIRED(3); | ||
| 58 | *outp++ = '='; | ||
| 59 | get_hex_str(outp, ch); | ||
| 60 | outp += 2; | ||
| 61 | len += 3; | ||
| 62 | inp++; | ||
| 63 | } else { | ||
| 64 | SOFT_LBREAK_IF_REQUIRED(1); | ||
| 65 | *outp++ = *inp++; | ||
| 66 | len++; | ||
| 67 | } | ||
| 68 | } else if ((ch >= 33 && ch <= 60) || (ch >= 62 && ch <= 126)) { | ||
| 69 | SOFT_LBREAK_IF_REQUIRED(1); | ||
| 70 | *outp++ = *inp++; | ||
| 71 | len++; | ||
| 72 | } else { | ||
| 73 | SOFT_LBREAK_IF_REQUIRED(3); | ||
| 74 | *outp++ = '='; | ||
| 75 | get_hex_str(outp, ch); | ||
| 76 | outp += 2; | ||
| 77 | len += 3; | ||
| 78 | inp++; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | if (len > 0) | ||
| 83 | *outp++ = '\n'; | ||
| 84 | |||
| 85 | *outp = '\0'; | ||
| 86 | } | ||
| 87 | |||
| 88 | gint qp_decode_line(gchar *str) | ||
| 89 | { | ||
| 90 | gchar *inp = str, *outp = str; | ||
| 91 | |||
| 92 | while (*inp != '\0') { | ||
| 93 | if (*inp == '=') { | ||
| 94 | if (inp[1] && inp[2] && | ||
| 95 | get_hex_value((guchar *)outp, inp[1], inp[2]) | ||
| 96 | == TRUE) { | ||
| 97 | inp += 3; | ||
| 98 | } else if (inp[1] == '\0' || g_ascii_isspace(inp[1])) { | ||
| 99 | /* soft line break */ | ||
| 100 | break; | ||
| 101 | } else { | ||
| 102 | /* broken QP string */ | ||
| 103 | *outp = *inp++; | ||
| 104 | } | ||
| 105 | } else { | ||
| 106 | *outp = *inp++; | ||
| 107 | } | ||
| 108 | outp++; | ||
| 109 | } | ||
| 110 | |||
| 111 | *outp = '\0'; | ||
| 112 | |||
| 113 | return outp - str; | ||
| 114 | } | ||
| 115 | |||
| 116 | gint qp_decode_q_encoding(guchar *out, const gchar *in, gint inlen) | ||
| 117 | { | ||
| 118 | const gchar *inp = in; | ||
| 119 | guchar *outp = out; | ||
| 120 | |||
| 121 | if (inlen < 0) | ||
| 122 | inlen = G_MAXINT; | ||
| 123 | |||
| 124 | while (inp - in < inlen && *inp != '\0') { | ||
| 125 | if (*inp == '=' && inp + 3 - in <= inlen) { | ||
| 126 | if (get_hex_value(outp, inp[1], inp[2]) == TRUE) { | ||
| 127 | inp += 3; | ||
| 128 | } else { | ||
| 129 | *outp = *inp++; | ||
| 130 | } | ||
| 131 | } else if (*inp == '_') { | ||
| 132 | *outp = ' '; | ||
| 133 | inp++; | ||
| 134 | } else { | ||
| 135 | *outp = *inp++; | ||
| 136 | } | ||
| 137 | outp++; | ||
| 138 | } | ||
| 139 | |||
| 140 | *outp = '\0'; | ||
| 141 | |||
| 142 | return outp - out; | ||
| 143 | } | ||
| 144 | |||
| 145 | gint qp_get_q_encoding_len(const guchar *str) | ||
| 146 | { | ||
| 147 | const guchar *inp = str; | ||
| 148 | gint len = 0; | ||
| 149 | |||
| 150 | while (*inp != '\0') { | ||
| 151 | if (*inp == 0x20) | ||
| 152 | len++; | ||
| 153 | else if (*inp == '=' || *inp == '?' || *inp == '_' || | ||
| 154 | *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) | ||
| 155 | len += 3; | ||
| 156 | else | ||
| 157 | len++; | ||
| 158 | |||
| 159 | inp++; | ||
| 160 | } | ||
| 161 | |||
| 162 | return len; | ||
| 163 | } | ||
| 164 | |||
| 165 | void qp_q_encode(gchar *out, const guchar *in) | ||
| 166 | { | ||
| 167 | const guchar *inp = in; | ||
| 168 | gchar *outp = out; | ||
| 169 | |||
| 170 | while (*inp != '\0') { | ||
| 171 | if (*inp == 0x20) | ||
| 172 | *outp++ = '_'; | ||
| 173 | else if (*inp == '=' || *inp == '?' || *inp == '_' || | ||
| 174 | *inp < 32 || *inp > 127 || g_ascii_isspace(*inp)) { | ||
| 175 | *outp++ = '='; | ||
| 176 | get_hex_str(outp, *inp); | ||
| 177 | outp += 2; | ||
| 178 | } else | ||
| 179 | *outp++ = *inp; | ||
| 180 | |||
| 181 | inp++; | ||
| 182 | } | ||
| 183 | |||
| 184 | *outp = '\0'; | ||
| 185 | } | ||
| 186 | |||
| 187 | #define HEX_TO_INT(val, hex) \ | ||
| 188 | { \ | ||
| 189 | gchar c = hex; \ | ||
| 190 | \ | ||
| 191 | if ('0' <= c && c <= '9') { \ | ||
| 192 | val = c - '0'; \ | ||
| 193 | } else if ('a' <= c && c <= 'f') { \ | ||
| 194 | val = c - 'a' + 10; \ | ||
| 195 | } else if ('A' <= c && c <= 'F') { \ | ||
| 196 | val = c - 'A' + 10; \ | ||
| 197 | } else { \ | ||
| 198 | val = -1; \ | ||
| 199 | } \ | ||
| 200 | } | ||
| 201 | |||
| 202 | static gboolean get_hex_value(guchar *out, gchar c1, gchar c2) | ||
| 203 | { | ||
| 204 | gint hi, lo; | ||
| 205 | |||
| 206 | HEX_TO_INT(hi, c1); | ||
| 207 | HEX_TO_INT(lo, c2); | ||
| 208 | |||
| 209 | if (hi == -1 || lo == -1) | ||
| 210 | return FALSE; | ||
| 211 | |||
| 212 | *out = (hi << 4) + lo; | ||
| 213 | return TRUE; | ||
| 214 | } | ||
| 215 | |||
| 216 | #define INT_TO_HEX(hex, val) \ | ||
| 217 | { \ | ||
| 218 | if ((val) < 10) \ | ||
| 219 | hex = '0' + (val); \ | ||
| 220 | else \ | ||
| 221 | hex = 'A' + (val) - 10; \ | ||
| 222 | } | ||
| 223 | |||
| 224 | static void get_hex_str(gchar *out, guchar ch) | ||
| 225 | { | ||
| 226 | gchar hex; | ||
| 227 | |||
| 228 | INT_TO_HEX(hex, ch >> 4); | ||
| 229 | *out++ = hex; | ||
| 230 | INT_TO_HEX(hex, ch & 0x0f); | ||
| 231 | *out++ = hex; | ||
| 232 | } | ||
diff --git a/libsylph/quoted-printable.h b/libsylph/quoted-printable.h new file mode 100644 index 0000000..b6f11f7 --- /dev/null +++ b/libsylph/quoted-printable.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __QUOTED_PRINTABLE_H__ | ||
| 21 | #define __QUOTED_PRINTABLE_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | void qp_encode_line (gchar *out, | ||
| 26 | const guchar *in); | ||
| 27 | gint qp_decode_line (gchar *str); | ||
| 28 | |||
| 29 | gint qp_decode_q_encoding (guchar *out, | ||
| 30 | const gchar *in, | ||
| 31 | gint inlen); | ||
| 32 | gint qp_get_q_encoding_len (const guchar *str); | ||
| 33 | void qp_q_encode (gchar *out, | ||
| 34 | const guchar *in); | ||
| 35 | |||
| 36 | #endif /* __QUOTED_PRINTABLE_H__ */ | ||
diff --git a/libsylph/recv.c b/libsylph/recv.c new file mode 100644 index 0000000..6ed4db2 --- /dev/null +++ b/libsylph/recv.c | |||
| @@ -0,0 +1,245 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <unistd.h> | ||
| 31 | |||
| 32 | #include "recv.h" | ||
| 33 | #include "socket.h" | ||
| 34 | #include "utils.h" | ||
| 35 | |||
| 36 | static RecvUIFunc recv_ui_func; | ||
| 37 | static gpointer recv_ui_func_data; | ||
| 38 | |||
| 39 | |||
| 40 | gchar *recv_bytes(SockInfo *sock, glong size) | ||
| 41 | { | ||
| 42 | gchar *buf; | ||
| 43 | glong count = 0; | ||
| 44 | |||
| 45 | if (size == 0) | ||
| 46 | return NULL; | ||
| 47 | |||
| 48 | buf = g_malloc(size + 1); | ||
| 49 | |||
| 50 | do { | ||
| 51 | gint read_count; | ||
| 52 | |||
| 53 | read_count = sock_read(sock, buf + count, | ||
| 54 | MIN(BUFFSIZE, size - count)); | ||
| 55 | if (read_count <= 0) { | ||
| 56 | g_free(buf); | ||
| 57 | return NULL; | ||
| 58 | } | ||
| 59 | count += read_count; | ||
| 60 | } while (count < size); | ||
| 61 | |||
| 62 | buf[size] = '\0'; | ||
| 63 | |||
| 64 | return buf; | ||
| 65 | } | ||
| 66 | |||
| 67 | gint recv_write_to_file(SockInfo *sock, const gchar *filename) | ||
| 68 | { | ||
| 69 | FILE *fp; | ||
| 70 | gint ret; | ||
| 71 | |||
| 72 | g_return_val_if_fail(filename != NULL, -1); | ||
| 73 | |||
| 74 | if ((fp = g_fopen(filename, "wb")) == NULL) { | ||
| 75 | FILE_OP_ERROR(filename, "fopen"); | ||
| 76 | recv_write(sock, NULL); | ||
| 77 | return -1; | ||
| 78 | } | ||
| 79 | |||
| 80 | if (change_file_mode_rw(fp, filename) < 0) | ||
| 81 | FILE_OP_ERROR(filename, "chmod"); | ||
| 82 | |||
| 83 | if ((ret = recv_write(sock, fp)) < 0) { | ||
| 84 | fclose(fp); | ||
| 85 | g_unlink(filename); | ||
| 86 | return ret; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (fclose(fp) == EOF) { | ||
| 90 | FILE_OP_ERROR(filename, "fclose"); | ||
| 91 | g_unlink(filename); | ||
| 92 | return -1; | ||
| 93 | } | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | gint recv_bytes_write_to_file(SockInfo *sock, glong size, const gchar *filename) | ||
| 99 | { | ||
| 100 | FILE *fp; | ||
| 101 | gint ret; | ||
| 102 | |||
| 103 | g_return_val_if_fail(filename != NULL, -1); | ||
| 104 | |||
| 105 | if ((fp = g_fopen(filename, "wb")) == NULL) { | ||
| 106 | FILE_OP_ERROR(filename, "fopen"); | ||
| 107 | return recv_bytes_write(sock, size, NULL); | ||
| 108 | } | ||
| 109 | |||
| 110 | if (change_file_mode_rw(fp, filename) < 0) | ||
| 111 | FILE_OP_ERROR(filename, "chmod"); | ||
| 112 | |||
| 113 | if ((ret = recv_bytes_write(sock, size, fp)) < 0) { | ||
| 114 | fclose(fp); | ||
| 115 | g_unlink(filename); | ||
| 116 | return ret; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (fclose(fp) == EOF) { | ||
| 120 | FILE_OP_ERROR(filename, "fclose"); | ||
| 121 | g_unlink(filename); | ||
| 122 | return -1; | ||
| 123 | } | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | gint recv_write(SockInfo *sock, FILE *fp) | ||
| 129 | { | ||
| 130 | gchar buf[BUFFSIZE]; | ||
| 131 | gint len; | ||
| 132 | gint count = 0; | ||
| 133 | gint bytes = 0; | ||
| 134 | gchar *p; | ||
| 135 | GTimeVal tv_prev, tv_cur; | ||
| 136 | |||
| 137 | g_get_current_time(&tv_prev); | ||
| 138 | |||
| 139 | for (;;) { | ||
| 140 | if (sock_gets(sock, buf, sizeof(buf)) < 0) { | ||
| 141 | g_warning(_("error occurred while retrieving data.\n")); | ||
| 142 | return -2; | ||
| 143 | } | ||
| 144 | |||
| 145 | len = strlen(buf); | ||
| 146 | if (len > 1 && buf[0] == '.' && buf[1] == '\r') { | ||
| 147 | if (recv_ui_func) | ||
| 148 | recv_ui_func(sock, count, bytes, | ||
| 149 | recv_ui_func_data); | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | count++; | ||
| 153 | bytes += len; | ||
| 154 | |||
| 155 | if (recv_ui_func) { | ||
| 156 | g_get_current_time(&tv_cur); | ||
| 157 | /* if elapsed time from previous update is greater | ||
| 158 | than 50msec, update UI */ | ||
| 159 | if (tv_cur.tv_sec - tv_prev.tv_sec > 0 || | ||
| 160 | tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) { | ||
| 161 | gboolean ret; | ||
| 162 | ret = recv_ui_func(sock, count, bytes, | ||
| 163 | recv_ui_func_data); | ||
| 164 | if (ret == FALSE) return -1; | ||
| 165 | g_get_current_time(&tv_prev); | ||
| 166 | } | ||
| 167 | } | ||
| 168 | |||
| 169 | p = buf; | ||
| 170 | |||
| 171 | if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') { | ||
| 172 | buf[len - 2] = '\n'; | ||
| 173 | buf[len - 1] = '\0'; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (buf[0] == '.' && buf[1] == '.') | ||
| 177 | p++; | ||
| 178 | else if (!strncmp(buf, ">From ", 6)) | ||
| 179 | p++; | ||
| 180 | |||
| 181 | if (fp && fputs(p, fp) == EOF) { | ||
| 182 | perror("fputs"); | ||
| 183 | g_warning(_("Can't write to file.\n")); | ||
| 184 | fp = NULL; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | if (!fp) return -1; | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | gint recv_bytes_write(SockInfo *sock, glong size, FILE *fp) | ||
| 194 | { | ||
| 195 | gchar *buf; | ||
| 196 | gchar *prev, *cur; | ||
| 197 | |||
| 198 | if (size == 0) | ||
| 199 | return 0; | ||
| 200 | |||
| 201 | buf = recv_bytes(sock, size); | ||
| 202 | if (!buf) | ||
| 203 | return -2; | ||
| 204 | |||
| 205 | /* +------------------+----------------+--------------------------+ * | ||
| 206 | * ^buf ^prev ^cur buf+size-1^ */ | ||
| 207 | |||
| 208 | prev = buf; | ||
| 209 | while ((cur = memchr(prev, '\r', size - (prev - buf))) != NULL) { | ||
| 210 | if (cur == buf + size - 1) break; | ||
| 211 | |||
| 212 | if (fp && (fwrite(prev, sizeof(gchar), cur - prev, fp) == EOF || | ||
| 213 | fwrite("\n", sizeof(gchar), 1, fp) == EOF)) { | ||
| 214 | perror("fwrite"); | ||
| 215 | g_warning(_("Can't write to file.\n")); | ||
| 216 | fp = NULL; | ||
| 217 | } | ||
| 218 | |||
| 219 | if (*(cur + 1) == '\n') | ||
| 220 | prev = cur + 2; | ||
| 221 | else | ||
| 222 | prev = cur + 1; | ||
| 223 | |||
| 224 | if (prev - buf >= size) break; | ||
| 225 | } | ||
| 226 | |||
| 227 | if (prev - buf < size && fp && | ||
| 228 | fwrite(prev, sizeof(gchar), size - (prev - buf), fp) == EOF) { | ||
| 229 | perror("fwrite"); | ||
| 230 | g_warning(_("Can't write to file.\n")); | ||
| 231 | fp = NULL; | ||
| 232 | } | ||
| 233 | |||
| 234 | g_free(buf); | ||
| 235 | |||
| 236 | if (!fp) return -1; | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | void recv_set_ui_func(RecvUIFunc func, gpointer data) | ||
| 242 | { | ||
| 243 | recv_ui_func = func; | ||
| 244 | recv_ui_func_data = data; | ||
| 245 | } | ||
diff --git a/libsylph/recv.h b/libsylph/recv.h new file mode 100644 index 0000000..bc31b9c --- /dev/null +++ b/libsylph/recv.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __RECV_H__ | ||
| 21 | #define __RECV_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "socket.h" | ||
| 26 | |||
| 27 | typedef gboolean (*RecvUIFunc) (SockInfo *sock, | ||
| 28 | gint count, | ||
| 29 | gint read_bytes, | ||
| 30 | gpointer data); | ||
| 31 | |||
| 32 | gchar *recv_bytes (SockInfo *sock, | ||
| 33 | glong size); | ||
| 34 | |||
| 35 | gint recv_write_to_file (SockInfo *sock, | ||
| 36 | const gchar *filename); | ||
| 37 | gint recv_bytes_write_to_file (SockInfo *sock, | ||
| 38 | glong size, | ||
| 39 | const gchar *filename); | ||
| 40 | gint recv_write (SockInfo *sock, | ||
| 41 | FILE *fp); | ||
| 42 | gint recv_bytes_write (SockInfo *sock, | ||
| 43 | glong size, | ||
| 44 | FILE *fp); | ||
| 45 | |||
| 46 | void recv_set_ui_func (RecvUIFunc func, | ||
| 47 | gpointer data); | ||
| 48 | |||
| 49 | #endif /* __RECV_H__ */ | ||
diff --git a/libsylph/session.c b/libsylph/session.c new file mode 100644 index 0000000..08a493e --- /dev/null +++ b/libsylph/session.c | |||
| @@ -0,0 +1,1320 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | |||
| 28 | #include <stdio.h> | ||
| 29 | #include <stdlib.h> | ||
| 30 | #include <string.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | #include <time.h> | ||
| 33 | #include <errno.h> | ||
| 34 | |||
| 35 | #include "session.h" | ||
| 36 | #include "utils.h" | ||
| 37 | |||
| 38 | typedef struct _SessionPrivData SessionPrivData; | ||
| 39 | |||
| 40 | struct _SessionPrivData { | ||
| 41 | Session *session; | ||
| 42 | SocksInfo *socks_info; | ||
| 43 | SessionErrorValue error_val; | ||
| 44 | gpointer data; | ||
| 45 | }; | ||
| 46 | |||
| 47 | static GList *priv_list = NULL; | ||
| 48 | |||
| 49 | static SessionPrivData *session_get_priv(Session *session); | ||
| 50 | |||
| 51 | static gint session_connect_cb (SockInfo *sock, | ||
| 52 | gpointer data); | ||
| 53 | static gint session_close (Session *session); | ||
| 54 | |||
| 55 | static gboolean session_timeout_cb (gpointer data); | ||
| 56 | |||
| 57 | #ifdef G_OS_WIN32 | ||
| 58 | static gboolean session_ping_cb (gpointer data); | ||
| 59 | #endif | ||
| 60 | |||
| 61 | static gboolean session_recv_msg_idle_cb (gpointer data); | ||
| 62 | static gboolean session_recv_data_idle_cb (gpointer data); | ||
| 63 | |||
| 64 | static gboolean session_recv_data_as_file_idle_cb (gpointer data); | ||
| 65 | |||
| 66 | static gboolean session_read_msg_cb (SockInfo *source, | ||
| 67 | GIOCondition condition, | ||
| 68 | gpointer data); | ||
| 69 | static gboolean session_read_data_cb (SockInfo *source, | ||
| 70 | GIOCondition condition, | ||
| 71 | gpointer data); | ||
| 72 | |||
| 73 | static gboolean session_read_data_as_file_cb (SockInfo *source, | ||
| 74 | GIOCondition condition, | ||
| 75 | gpointer data); | ||
| 76 | |||
| 77 | static gboolean session_write_msg_cb (SockInfo *source, | ||
| 78 | GIOCondition condition, | ||
| 79 | gpointer data); | ||
| 80 | static gboolean session_write_data_cb (SockInfo *source, | ||
| 81 | GIOCondition condition, | ||
| 82 | gpointer data); | ||
| 83 | |||
| 84 | |||
| 85 | void session_init(Session *session) | ||
| 86 | { | ||
| 87 | SessionPrivData *priv; | ||
| 88 | |||
| 89 | session->type = SESSION_UNKNOWN; | ||
| 90 | session->sock = NULL; | ||
| 91 | session->server = NULL; | ||
| 92 | session->port = 0; | ||
| 93 | #if USE_SSL | ||
| 94 | session->ssl_type = SSL_NONE; | ||
| 95 | #endif | ||
| 96 | session->nonblocking = TRUE; | ||
| 97 | session->state = SESSION_READY; | ||
| 98 | session->last_access_time = time(NULL); | ||
| 99 | |||
| 100 | g_get_current_time(&session->tv_prev); | ||
| 101 | |||
| 102 | session->conn_id = 0; | ||
| 103 | |||
| 104 | session->io_tag = 0; | ||
| 105 | |||
| 106 | session->read_buf_p = session->read_buf; | ||
| 107 | session->read_buf_len = 0; | ||
| 108 | |||
| 109 | session->read_msg_buf = g_string_sized_new(1024); | ||
| 110 | session->read_data_buf = g_byte_array_new(); | ||
| 111 | session->read_data_terminator = NULL; | ||
| 112 | |||
| 113 | session->read_data_fp = NULL; | ||
| 114 | session->read_data_pos = 0; | ||
| 115 | |||
| 116 | session->preread_len = 0; | ||
| 117 | |||
| 118 | session->write_buf = NULL; | ||
| 119 | session->write_buf_p = NULL; | ||
| 120 | session->write_buf_len = 0; | ||
| 121 | |||
| 122 | session->write_data_fp = NULL; | ||
| 123 | session->write_data_pos = 0; | ||
| 124 | session->write_data_len = 0; | ||
| 125 | |||
| 126 | session->timeout_tag = 0; | ||
| 127 | session->timeout_interval = 0; | ||
| 128 | |||
| 129 | session->idle_tag = 0; | ||
| 130 | session->ping_tag = 0; | ||
| 131 | |||
| 132 | session->data = NULL; | ||
| 133 | |||
| 134 | priv = g_new0(SessionPrivData, 1); | ||
| 135 | priv->session = session; | ||
| 136 | priv->socks_info = NULL; | ||
| 137 | priv->error_val = SESSION_ERROR_OK; | ||
| 138 | priv_list = g_list_prepend(priv_list, priv); | ||
| 139 | } | ||
| 140 | |||
| 141 | static SessionPrivData *session_get_priv(Session *session) | ||
| 142 | { | ||
| 143 | SessionPrivData *priv; | ||
| 144 | GList *cur; | ||
| 145 | |||
| 146 | g_return_val_if_fail(session != NULL, NULL); | ||
| 147 | |||
| 148 | for (cur = priv_list; cur != NULL; cur = cur->next) { | ||
| 149 | priv = (SessionPrivData *)cur->data; | ||
| 150 | if (priv->session == session) | ||
| 151 | return priv; | ||
| 152 | } | ||
| 153 | |||
| 154 | return NULL; | ||
| 155 | } | ||
| 156 | |||
| 157 | gint session_connect(Session *session, const gchar *server, gushort port) | ||
| 158 | { | ||
| 159 | return session_connect_full(session, server, port, NULL); | ||
| 160 | } | ||
| 161 | |||
| 162 | gint session_connect_full(Session *session, const gchar *server, gushort port, | ||
| 163 | SocksInfo *socks_info) | ||
| 164 | { | ||
| 165 | SessionPrivData *priv; | ||
| 166 | #ifndef G_OS_UNIX | ||
| 167 | SockInfo *sock = NULL; | ||
| 168 | #endif | ||
| 169 | g_return_val_if_fail(session != NULL, -1); | ||
| 170 | g_return_val_if_fail(server != NULL, -1); | ||
| 171 | g_return_val_if_fail(port > 0, -1); | ||
| 172 | |||
| 173 | priv = session_get_priv(session); | ||
| 174 | g_return_val_if_fail(priv != NULL, -1); | ||
| 175 | priv->socks_info = socks_info; | ||
| 176 | |||
| 177 | if (session->server != server) { | ||
| 178 | g_free(session->server); | ||
| 179 | session->server = g_strdup(server); | ||
| 180 | } | ||
| 181 | session->port = port; | ||
| 182 | |||
| 183 | if (socks_info) { | ||
| 184 | g_return_val_if_fail(socks_info->proxy_host != NULL, -1); | ||
| 185 | server = socks_info->proxy_host; | ||
| 186 | port = socks_info->proxy_port; | ||
| 187 | } | ||
| 188 | |||
| 189 | #ifdef G_OS_UNIX | ||
| 190 | session->conn_id = sock_connect_async(server, port, session_connect_cb, | ||
| 191 | session); | ||
| 192 | if (session->conn_id < 0) { | ||
| 193 | g_warning("can't connect to server."); | ||
| 194 | session->state = SESSION_ERROR; | ||
| 195 | priv->error_val = SESSION_ERROR_CONNFAIL; | ||
| 196 | return -1; | ||
| 197 | } | ||
| 198 | #elif USE_THREADS | ||
| 199 | session->conn_id = sock_connect_async_thread(server, port); | ||
| 200 | if (session->conn_id < 0) { | ||
| 201 | g_warning("can't connect to server."); | ||
| 202 | session->state = SESSION_ERROR; | ||
| 203 | priv->error_val = SESSION_ERROR_CONNFAIL; | ||
| 204 | return -1; | ||
| 205 | } | ||
| 206 | if (sock_info_connect_async_thread_wait(session->conn_id, &sock) < 0) { | ||
| 207 | session_connect_cb(sock, session); | ||
| 208 | if (sock) | ||
| 209 | sock_close(sock); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | #else /* !USE_THREADS */ | ||
| 213 | sock = sock_new(server, port); | ||
| 214 | if (sock_info_connect(sock) < 0) { | ||
| 215 | session_connect_cb(sock, session); | ||
| 216 | sock_close(sock); | ||
| 217 | return -1; | ||
| 218 | } | ||
| 219 | #endif | ||
| 220 | |||
| 221 | #ifdef G_OS_UNIX | ||
| 222 | return 0; | ||
| 223 | #else | ||
| 224 | return session_connect_cb(sock, session); | ||
| 225 | #endif | ||
| 226 | } | ||
| 227 | |||
| 228 | static gint session_connect_cb(SockInfo *sock, gpointer data) | ||
| 229 | { | ||
| 230 | Session *session = SESSION(data); | ||
| 231 | SessionPrivData *priv; | ||
| 232 | |||
| 233 | priv = session_get_priv(session); | ||
| 234 | session->conn_id = 0; | ||
| 235 | |||
| 236 | if (!sock) { | ||
| 237 | g_warning("can't connect to server."); | ||
| 238 | session->state = SESSION_ERROR; | ||
| 239 | priv->error_val = SESSION_ERROR_CONNFAIL; | ||
| 240 | return -1; | ||
| 241 | } | ||
| 242 | if (sock->state == CONN_LOOKUPFAILED) { | ||
| 243 | g_warning("DNS lookup failed."); | ||
| 244 | session->state = SESSION_ERROR; | ||
| 245 | priv->error_val = SESSION_ERROR_LOOKUP; | ||
| 246 | return -1; | ||
| 247 | } else if (sock->state != CONN_ESTABLISHED) { | ||
| 248 | g_warning("can't connect to server (ConnectionState: %d).", | ||
| 249 | sock->state); | ||
| 250 | session->state = SESSION_ERROR; | ||
| 251 | priv->error_val = SESSION_ERROR_CONNFAIL; | ||
| 252 | return -1; | ||
| 253 | } | ||
| 254 | |||
| 255 | session->sock = sock; | ||
| 256 | |||
| 257 | if (priv->socks_info) { | ||
| 258 | sock_set_nonblocking_mode(sock, FALSE); | ||
| 259 | if (socks_connect(sock, session->server, session->port, | ||
| 260 | priv->socks_info) < 0) { | ||
| 261 | g_warning("can't establish SOCKS connection."); | ||
| 262 | session->state = SESSION_ERROR; | ||
| 263 | priv->error_val = SESSION_ERROR_CONNFAIL; | ||
| 264 | return -1; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | #if USE_SSL | ||
| 269 | if (session->ssl_type == SSL_TUNNEL) { | ||
| 270 | sock_set_nonblocking_mode(sock, FALSE); | ||
| 271 | if (!ssl_init_socket(sock)) { | ||
| 272 | g_warning("can't initialize SSL."); | ||
| 273 | session->state = SESSION_ERROR; | ||
| 274 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 275 | return -1; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | #endif | ||
| 279 | |||
| 280 | debug_print("session (%p): connected\n", session); | ||
| 281 | |||
| 282 | sock_set_nonblocking_mode(sock, session->nonblocking); | ||
| 283 | |||
| 284 | session->state = SESSION_RECV; | ||
| 285 | priv->error_val = SESSION_ERROR_OK; | ||
| 286 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 287 | session_read_msg_cb, | ||
| 288 | session); | ||
| 289 | |||
| 290 | #ifdef G_OS_WIN32 | ||
| 291 | session->ping_tag = g_timeout_add_full(G_PRIORITY_LOW, 1000, session_ping_cb, session, NULL); | ||
| 292 | #endif | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | gint session_disconnect(Session *session) | ||
| 298 | { | ||
| 299 | session_close(session); | ||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | void session_destroy(Session *session) | ||
| 304 | { | ||
| 305 | SessionPrivData *priv; | ||
| 306 | |||
| 307 | g_return_if_fail(session != NULL); | ||
| 308 | g_return_if_fail(session->destroy != NULL); | ||
| 309 | |||
| 310 | session_close(session); | ||
| 311 | session->destroy(session); | ||
| 312 | g_free(session->server); | ||
| 313 | g_string_free(session->read_msg_buf, TRUE); | ||
| 314 | g_byte_array_free(session->read_data_buf, TRUE); | ||
| 315 | g_free(session->read_data_terminator); | ||
| 316 | if (session->read_data_fp) | ||
| 317 | fclose(session->read_data_fp); | ||
| 318 | g_free(session->write_buf); | ||
| 319 | |||
| 320 | priv = session_get_priv(session); | ||
| 321 | if (priv) { | ||
| 322 | priv_list = g_list_remove(priv_list, priv); | ||
| 323 | socks_info_free(priv->socks_info); | ||
| 324 | g_free(priv); | ||
| 325 | } | ||
| 326 | |||
| 327 | debug_print("session (%p): destroyed\n", session); | ||
| 328 | |||
| 329 | g_free(session); | ||
| 330 | } | ||
| 331 | |||
| 332 | gboolean session_is_connected(Session *session) | ||
| 333 | { | ||
| 334 | return (session->state == SESSION_READY || | ||
| 335 | session->state == SESSION_SEND || | ||
| 336 | session->state == SESSION_RECV); | ||
| 337 | } | ||
| 338 | |||
| 339 | SessionErrorValue session_get_error(Session *session) | ||
| 340 | { | ||
| 341 | SessionPrivData *priv; | ||
| 342 | |||
| 343 | priv = session_get_priv(session); | ||
| 344 | if (priv) | ||
| 345 | return priv->error_val; | ||
| 346 | else | ||
| 347 | return SESSION_ERROR_ERROR; | ||
| 348 | } | ||
| 349 | |||
| 350 | void session_set_access_time(Session *session) | ||
| 351 | { | ||
| 352 | session->last_access_time = time(NULL); | ||
| 353 | } | ||
| 354 | |||
| 355 | void session_set_timeout(Session *session, guint interval) | ||
| 356 | { | ||
| 357 | if (session->timeout_tag > 0) | ||
| 358 | g_source_remove(session->timeout_tag); | ||
| 359 | |||
| 360 | session->timeout_interval = interval; | ||
| 361 | if (interval > 0) | ||
| 362 | session->timeout_tag = | ||
| 363 | g_timeout_add_full(G_PRIORITY_LOW, interval, session_timeout_cb, session, NULL); | ||
| 364 | else | ||
| 365 | session->timeout_tag = 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | static gboolean session_timeout_cb(gpointer data) | ||
| 369 | { | ||
| 370 | Session *session = SESSION(data); | ||
| 371 | SessionPrivData *priv; | ||
| 372 | |||
| 373 | g_warning("session timeout.\n"); | ||
| 374 | |||
| 375 | if (session->io_tag > 0) { | ||
| 376 | g_source_remove(session->io_tag); | ||
| 377 | session->io_tag = 0; | ||
| 378 | } | ||
| 379 | |||
| 380 | session->timeout_tag = 0; | ||
| 381 | session->state = SESSION_TIMEOUT; | ||
| 382 | priv = session_get_priv(session); | ||
| 383 | priv->error_val = SESSION_ERROR_TIMEOUT; | ||
| 384 | |||
| 385 | return FALSE; | ||
| 386 | } | ||
| 387 | |||
| 388 | #ifdef G_OS_WIN32 | ||
| 389 | /* hack for state machine freeze problem in GLib >= 2.8.x */ | ||
| 390 | static gboolean session_ping_cb(gpointer data) | ||
| 391 | { | ||
| 392 | Session *session = SESSION(data); | ||
| 393 | SockInfo *sock = session->sock; | ||
| 394 | |||
| 395 | if (!session_is_connected(session)) | ||
| 396 | return FALSE; | ||
| 397 | |||
| 398 | if (session->io_tag > 0 && sock && sock->callback) { | ||
| 399 | GTimeVal tv_cur, tv_result; | ||
| 400 | |||
| 401 | g_get_current_time(&tv_cur); | ||
| 402 | tv_result.tv_sec = tv_cur.tv_sec - session->tv_prev.tv_sec; | ||
| 403 | tv_result.tv_usec = tv_cur.tv_usec - session->tv_prev.tv_usec; | ||
| 404 | if (tv_result.tv_usec < 0) { | ||
| 405 | tv_result.tv_sec--; | ||
| 406 | tv_result.tv_usec += G_USEC_PER_SEC; | ||
| 407 | } | ||
| 408 | if (tv_result.tv_sec * G_USEC_PER_SEC + tv_result.tv_usec > | ||
| 409 | G_USEC_PER_SEC) { | ||
| 410 | SockFlags save_flags; | ||
| 411 | |||
| 412 | debug_print("state machine freeze for 1 second detected, forcing dispatch.\n"); | ||
| 413 | save_flags = sock->flags; | ||
| 414 | SOCK_UNSET_FLAGS(sock->flags, SYL_SOCK_CHECK_IO); | ||
| 415 | sock->callback(sock, sock->condition, sock->data); | ||
| 416 | sock->flags = save_flags; | ||
| 417 | } | ||
| 418 | } | ||
| 419 | |||
| 420 | return TRUE; | ||
| 421 | } | ||
| 422 | #endif | ||
| 423 | |||
| 424 | void session_set_recv_message_notify(Session *session, | ||
| 425 | RecvMsgNotify notify_func, gpointer data) | ||
| 426 | { | ||
| 427 | session->recv_msg_notify = notify_func; | ||
| 428 | session->recv_msg_notify_data = data; | ||
| 429 | } | ||
| 430 | |||
| 431 | void session_set_recv_data_progressive_notify | ||
| 432 | (Session *session, | ||
| 433 | RecvDataProgressiveNotify notify_func, | ||
| 434 | gpointer data) | ||
| 435 | { | ||
| 436 | session->recv_data_progressive_notify = notify_func, | ||
| 437 | session->recv_data_progressive_notify_data = data; | ||
| 438 | } | ||
| 439 | |||
| 440 | void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func, | ||
| 441 | gpointer data) | ||
| 442 | { | ||
| 443 | session->recv_data_notify = notify_func; | ||
| 444 | session->recv_data_notify_data = data; | ||
| 445 | } | ||
| 446 | |||
| 447 | void session_set_send_data_progressive_notify | ||
| 448 | (Session *session, | ||
| 449 | SendDataProgressiveNotify notify_func, | ||
| 450 | gpointer data) | ||
| 451 | { | ||
| 452 | session->send_data_progressive_notify = notify_func; | ||
| 453 | session->send_data_progressive_notify_data = data; | ||
| 454 | } | ||
| 455 | |||
| 456 | void session_set_send_data_notify(Session *session, SendDataNotify notify_func, | ||
| 457 | gpointer data) | ||
| 458 | { | ||
| 459 | session->send_data_notify = notify_func; | ||
| 460 | session->send_data_notify_data = data; | ||
| 461 | } | ||
| 462 | |||
| 463 | static gint session_close(Session *session) | ||
| 464 | { | ||
| 465 | g_return_val_if_fail(session != NULL, -1); | ||
| 466 | |||
| 467 | #ifdef G_OS_UNIX | ||
| 468 | if (session->conn_id > 0) { | ||
| 469 | sock_connect_async_cancel(session->conn_id); | ||
| 470 | session->conn_id = 0; | ||
| 471 | debug_print("session (%p): connection cancelled\n", session); | ||
| 472 | } | ||
| 473 | #endif | ||
| 474 | |||
| 475 | session_set_timeout(session, 0); | ||
| 476 | |||
| 477 | if (session->idle_tag > 0) { | ||
| 478 | g_source_remove(session->idle_tag); | ||
| 479 | session->idle_tag = 0; | ||
| 480 | } | ||
| 481 | |||
| 482 | #ifdef G_OS_WIN32 | ||
| 483 | if (session->ping_tag > 0) { | ||
| 484 | g_source_remove(session->ping_tag); | ||
| 485 | session->ping_tag = 0; | ||
| 486 | } | ||
| 487 | #endif | ||
| 488 | |||
| 489 | if (session->io_tag > 0) { | ||
| 490 | g_source_remove(session->io_tag); | ||
| 491 | session->io_tag = 0; | ||
| 492 | } | ||
| 493 | |||
| 494 | if (session->sock) { | ||
| 495 | sock_close(session->sock); | ||
| 496 | session->sock = NULL; | ||
| 497 | session->state = SESSION_DISCONNECTED; | ||
| 498 | debug_print("session (%p): closed\n", session); | ||
| 499 | } | ||
| 500 | |||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | |||
| 504 | #if USE_SSL | ||
| 505 | gint session_start_tls(Session *session) | ||
| 506 | { | ||
| 507 | gboolean nb_mode; | ||
| 508 | |||
| 509 | nb_mode = sock_is_nonblocking_mode(session->sock); | ||
| 510 | |||
| 511 | sock_set_nonblocking_mode(session->sock, FALSE); | ||
| 512 | |||
| 513 | if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) { | ||
| 514 | g_warning("can't start TLS session.\n"); | ||
| 515 | if (nb_mode) | ||
| 516 | sock_set_nonblocking_mode(session->sock, TRUE); | ||
| 517 | return -1; | ||
| 518 | } | ||
| 519 | |||
| 520 | sock_set_nonblocking_mode(session->sock, session->nonblocking); | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | #endif | ||
| 525 | |||
| 526 | gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg) | ||
| 527 | { | ||
| 528 | gboolean ret; | ||
| 529 | |||
| 530 | g_return_val_if_fail(session->sock != NULL, -1); | ||
| 531 | g_return_val_if_fail(session->write_buf == NULL, -1); | ||
| 532 | g_return_val_if_fail(msg != NULL, -1); | ||
| 533 | g_return_val_if_fail(msg[0] != '\0', -1); | ||
| 534 | |||
| 535 | session->state = SESSION_SEND; | ||
| 536 | session->write_buf = g_strconcat(msg, "\r\n", NULL); | ||
| 537 | session->write_buf_p = session->write_buf; | ||
| 538 | session->write_buf_len = strlen(msg) + 2; | ||
| 539 | |||
| 540 | ret = session_write_msg_cb(session->sock, G_IO_OUT, session); | ||
| 541 | |||
| 542 | if (ret == TRUE) | ||
| 543 | session->io_tag = sock_add_watch(session->sock, G_IO_OUT, | ||
| 544 | session_write_msg_cb, session); | ||
| 545 | else if (session->state == SESSION_ERROR) | ||
| 546 | return -1; | ||
| 547 | |||
| 548 | return 0; | ||
| 549 | } | ||
| 550 | |||
| 551 | gint session_recv_msg(Session *session) | ||
| 552 | { | ||
| 553 | g_return_val_if_fail(session->sock != NULL, -1); | ||
| 554 | g_return_val_if_fail(session->read_msg_buf->len == 0, -1); | ||
| 555 | |||
| 556 | session->state = SESSION_RECV; | ||
| 557 | |||
| 558 | if (session->read_buf_len > 0) | ||
| 559 | session->idle_tag = g_idle_add(session_recv_msg_idle_cb, | ||
| 560 | session); | ||
| 561 | else | ||
| 562 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 563 | session_read_msg_cb, session); | ||
| 564 | |||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | static gboolean session_recv_msg_idle_cb(gpointer data) | ||
| 569 | { | ||
| 570 | Session *session = SESSION(data); | ||
| 571 | gboolean ret; | ||
| 572 | |||
| 573 | #if GLIB_CHECK_VERSION(2, 12, 0) | ||
| 574 | if (g_source_is_destroyed(g_main_current_source())) | ||
| 575 | return FALSE; | ||
| 576 | #endif | ||
| 577 | |||
| 578 | session->idle_tag = 0; | ||
| 579 | ret = session_read_msg_cb(session->sock, G_IO_IN, session); | ||
| 580 | |||
| 581 | if (ret == TRUE) | ||
| 582 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 583 | session_read_msg_cb, session); | ||
| 584 | |||
| 585 | return FALSE; | ||
| 586 | } | ||
| 587 | |||
| 588 | gint session_send_data(Session *session, FILE *data_fp, guint size) | ||
| 589 | { | ||
| 590 | gboolean ret; | ||
| 591 | |||
| 592 | g_return_val_if_fail(session->sock != NULL, -1); | ||
| 593 | g_return_val_if_fail(session->write_data_fp == NULL, -1); | ||
| 594 | g_return_val_if_fail(data_fp != NULL, -1); | ||
| 595 | g_return_val_if_fail(size != 0, -1); | ||
| 596 | |||
| 597 | session->state = SESSION_SEND; | ||
| 598 | |||
| 599 | session->write_data_fp = data_fp; | ||
| 600 | session->write_data_pos = 0; | ||
| 601 | session->write_data_len = size; | ||
| 602 | g_get_current_time(&session->tv_prev); | ||
| 603 | |||
| 604 | #ifdef G_OS_WIN32 | ||
| 605 | sock_set_nonblocking_mode(session->sock, FALSE); | ||
| 606 | #endif | ||
| 607 | |||
| 608 | ret = session_write_data_cb(session->sock, G_IO_OUT, session); | ||
| 609 | |||
| 610 | if (ret == TRUE) | ||
| 611 | #ifdef G_OS_WIN32 | ||
| 612 | session->io_tag = sock_add_watch_poll(session->sock, G_IO_OUT, | ||
| 613 | session_write_data_cb, | ||
| 614 | session); | ||
| 615 | #else | ||
| 616 | session->io_tag = sock_add_watch(session->sock, G_IO_OUT, | ||
| 617 | session_write_data_cb, | ||
| 618 | session); | ||
| 619 | #endif | ||
| 620 | else if (session->state == SESSION_ERROR) | ||
| 621 | return -1; | ||
| 622 | |||
| 623 | return 0; | ||
| 624 | } | ||
| 625 | |||
| 626 | gint session_recv_data(Session *session, guint size, const gchar *terminator) | ||
| 627 | { | ||
| 628 | g_return_val_if_fail(session->sock != NULL, -1); | ||
| 629 | g_return_val_if_fail(session->read_data_buf->len == 0, -1); | ||
| 630 | |||
| 631 | session->state = SESSION_RECV; | ||
| 632 | |||
| 633 | g_free(session->read_data_terminator); | ||
| 634 | session->read_data_terminator = g_strdup(terminator); | ||
| 635 | g_get_current_time(&session->tv_prev); | ||
| 636 | |||
| 637 | if (session->read_buf_len > 0) | ||
| 638 | session->idle_tag = g_idle_add(session_recv_data_idle_cb, | ||
| 639 | session); | ||
| 640 | else | ||
| 641 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 642 | session_read_data_cb, session); | ||
| 643 | |||
| 644 | return 0; | ||
| 645 | } | ||
| 646 | |||
| 647 | static gboolean session_recv_data_idle_cb(gpointer data) | ||
| 648 | { | ||
| 649 | Session *session = SESSION(data); | ||
| 650 | gboolean ret; | ||
| 651 | |||
| 652 | #if GLIB_CHECK_VERSION(2, 12, 0) | ||
| 653 | if (g_source_is_destroyed(g_main_current_source())) | ||
| 654 | return FALSE; | ||
| 655 | #endif | ||
| 656 | |||
| 657 | session->idle_tag = 0; | ||
| 658 | ret = session_read_data_cb(session->sock, G_IO_IN, session); | ||
| 659 | |||
| 660 | if (ret == TRUE) | ||
| 661 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 662 | session_read_data_cb, session); | ||
| 663 | |||
| 664 | return FALSE; | ||
| 665 | } | ||
| 666 | |||
| 667 | gint session_recv_data_as_file(Session *session, guint size, | ||
| 668 | const gchar *terminator) | ||
| 669 | { | ||
| 670 | g_return_val_if_fail(session->sock != NULL, -1); | ||
| 671 | g_return_val_if_fail(session->read_data_pos == 0, -1); | ||
| 672 | g_return_val_if_fail(session->read_data_fp == NULL, -1); | ||
| 673 | |||
| 674 | session->state = SESSION_RECV; | ||
| 675 | |||
| 676 | g_free(session->read_data_terminator); | ||
| 677 | session->read_data_terminator = g_strdup(terminator); | ||
| 678 | g_get_current_time(&session->tv_prev); | ||
| 679 | |||
| 680 | session->read_data_fp = my_tmpfile(); | ||
| 681 | if (!session->read_data_fp) { | ||
| 682 | FILE_OP_ERROR("session_recv_data_as_file", "my_tmpfile"); | ||
| 683 | return -1; | ||
| 684 | } | ||
| 685 | |||
| 686 | if (session->read_buf_len > 0) | ||
| 687 | session->idle_tag = | ||
| 688 | g_idle_add(session_recv_data_as_file_idle_cb, session); | ||
| 689 | else | ||
| 690 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 691 | session_read_data_as_file_cb, | ||
| 692 | session); | ||
| 693 | |||
| 694 | return 0; | ||
| 695 | } | ||
| 696 | |||
| 697 | static gboolean session_recv_data_as_file_idle_cb(gpointer data) | ||
| 698 | { | ||
| 699 | Session *session = SESSION(data); | ||
| 700 | gboolean ret; | ||
| 701 | |||
| 702 | #if GLIB_CHECK_VERSION(2, 12, 0) | ||
| 703 | if (g_source_is_destroyed(g_main_current_source())) | ||
| 704 | return FALSE; | ||
| 705 | #endif | ||
| 706 | |||
| 707 | session->idle_tag = 0; | ||
| 708 | ret = session_read_data_as_file_cb(session->sock, G_IO_IN, session); | ||
| 709 | |||
| 710 | if (ret == TRUE) | ||
| 711 | session->io_tag = sock_add_watch(session->sock, G_IO_IN, | ||
| 712 | session_read_data_as_file_cb, | ||
| 713 | session); | ||
| 714 | |||
| 715 | return FALSE; | ||
| 716 | } | ||
| 717 | |||
| 718 | static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, | ||
| 719 | gpointer data) | ||
| 720 | { | ||
| 721 | Session *session = SESSION(data); | ||
| 722 | SessionPrivData *priv; | ||
| 723 | gchar buf[SESSION_BUFFSIZE]; | ||
| 724 | gint line_len; | ||
| 725 | gchar *newline; | ||
| 726 | gchar *msg; | ||
| 727 | gint ret; | ||
| 728 | |||
| 729 | g_return_val_if_fail(condition == G_IO_IN, FALSE); | ||
| 730 | |||
| 731 | if (session->read_buf_len == 0) { | ||
| 732 | gint read_len; | ||
| 733 | |||
| 734 | read_len = sock_read(session->sock, session->read_buf, | ||
| 735 | SESSION_BUFFSIZE - 1); | ||
| 736 | |||
| 737 | if (read_len == 0) { | ||
| 738 | g_warning("sock_read: received EOF\n"); | ||
| 739 | session->state = SESSION_EOF; | ||
| 740 | return FALSE; | ||
| 741 | } | ||
| 742 | |||
| 743 | if (read_len < 0) { | ||
| 744 | switch (errno) { | ||
| 745 | case EAGAIN: | ||
| 746 | return TRUE; | ||
| 747 | default: | ||
| 748 | g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); | ||
| 749 | session->state = SESSION_ERROR; | ||
| 750 | priv = session_get_priv(session); | ||
| 751 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 752 | return FALSE; | ||
| 753 | } | ||
| 754 | } | ||
| 755 | |||
| 756 | session->read_buf_len = read_len; | ||
| 757 | } | ||
| 758 | |||
| 759 | session_set_timeout(session, session->timeout_interval); | ||
| 760 | |||
| 761 | if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len)) | ||
| 762 | != NULL) | ||
| 763 | line_len = newline - session->read_buf_p + 1; | ||
| 764 | else | ||
| 765 | line_len = session->read_buf_len; | ||
| 766 | |||
| 767 | if (line_len == 0) | ||
| 768 | return TRUE; | ||
| 769 | |||
| 770 | memcpy(buf, session->read_buf_p, line_len); | ||
| 771 | buf[line_len] = '\0'; | ||
| 772 | |||
| 773 | g_string_append(session->read_msg_buf, buf); | ||
| 774 | |||
| 775 | session->read_buf_len -= line_len; | ||
| 776 | if (session->read_buf_len == 0) | ||
| 777 | session->read_buf_p = session->read_buf; | ||
| 778 | else | ||
| 779 | session->read_buf_p += line_len; | ||
| 780 | |||
| 781 | /* incomplete read */ | ||
| 782 | if (buf[line_len - 1] != '\n') | ||
| 783 | return TRUE; | ||
| 784 | |||
| 785 | /* complete */ | ||
| 786 | if (session->io_tag > 0) { | ||
| 787 | g_source_remove(session->io_tag); | ||
| 788 | session->io_tag = 0; | ||
| 789 | } | ||
| 790 | |||
| 791 | /* callback */ | ||
| 792 | msg = g_strdup(session->read_msg_buf->str); | ||
| 793 | strretchomp(msg); | ||
| 794 | g_string_truncate(session->read_msg_buf, 0); | ||
| 795 | |||
| 796 | ret = session->recv_msg(session, msg); | ||
| 797 | if (session->recv_msg_notify) | ||
| 798 | session->recv_msg_notify(session, msg, | ||
| 799 | session->recv_msg_notify_data); | ||
| 800 | |||
| 801 | g_free(msg); | ||
| 802 | |||
| 803 | if (ret < 0) { | ||
| 804 | session->state = SESSION_ERROR; | ||
| 805 | priv = session_get_priv(session); | ||
| 806 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 807 | } | ||
| 808 | |||
| 809 | return FALSE; | ||
| 810 | } | ||
| 811 | |||
| 812 | static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, | ||
| 813 | gpointer data) | ||
| 814 | { | ||
| 815 | Session *session = SESSION(data); | ||
| 816 | SessionPrivData *priv; | ||
| 817 | GByteArray *data_buf; | ||
| 818 | gint terminator_len; | ||
| 819 | gboolean complete = FALSE; | ||
| 820 | guint data_len; | ||
| 821 | gint ret; | ||
| 822 | |||
| 823 | g_return_val_if_fail(condition == G_IO_IN, FALSE); | ||
| 824 | |||
| 825 | if (session->read_buf_len == 0) { | ||
| 826 | gint read_len; | ||
| 827 | |||
| 828 | read_len = sock_read(session->sock, session->read_buf, | ||
| 829 | SESSION_BUFFSIZE); | ||
| 830 | |||
| 831 | if (read_len == 0) { | ||
| 832 | g_warning("sock_read: received EOF\n"); | ||
| 833 | session->state = SESSION_EOF; | ||
| 834 | return FALSE; | ||
| 835 | } | ||
| 836 | |||
| 837 | if (read_len < 0) { | ||
| 838 | switch (errno) { | ||
| 839 | case EAGAIN: | ||
| 840 | return TRUE; | ||
| 841 | default: | ||
| 842 | g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); | ||
| 843 | session->state = SESSION_ERROR; | ||
| 844 | priv = session_get_priv(session); | ||
| 845 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 846 | return FALSE; | ||
| 847 | } | ||
| 848 | } | ||
| 849 | |||
| 850 | session->read_buf_len = read_len; | ||
| 851 | } | ||
| 852 | |||
| 853 | session_set_timeout(session, session->timeout_interval); | ||
| 854 | |||
| 855 | data_buf = session->read_data_buf; | ||
| 856 | terminator_len = strlen(session->read_data_terminator); | ||
| 857 | |||
| 858 | if (session->read_buf_len == 0) | ||
| 859 | return TRUE; | ||
| 860 | |||
| 861 | g_byte_array_append(data_buf, (guchar *)session->read_buf_p, | ||
| 862 | session->read_buf_len); | ||
| 863 | |||
| 864 | session->read_buf_len = 0; | ||
| 865 | session->read_buf_p = session->read_buf; | ||
| 866 | |||
| 867 | /* check if data is terminated */ | ||
| 868 | if (data_buf->len >= terminator_len) { | ||
| 869 | if (memcmp(data_buf->data, session->read_data_terminator, | ||
| 870 | terminator_len) == 0) | ||
| 871 | complete = TRUE; | ||
| 872 | else if (data_buf->len >= terminator_len + 2 && | ||
| 873 | memcmp(data_buf->data + data_buf->len - | ||
| 874 | (terminator_len + 2), "\r\n", 2) == 0 && | ||
| 875 | memcmp(data_buf->data + data_buf->len - | ||
| 876 | terminator_len, session->read_data_terminator, | ||
| 877 | terminator_len) == 0) | ||
| 878 | complete = TRUE; | ||
| 879 | } | ||
| 880 | |||
| 881 | /* incomplete read */ | ||
| 882 | if (!complete) { | ||
| 883 | GTimeVal tv_cur; | ||
| 884 | |||
| 885 | g_get_current_time(&tv_cur); | ||
| 886 | if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || | ||
| 887 | tv_cur.tv_usec - session->tv_prev.tv_usec > | ||
| 888 | UI_REFRESH_INTERVAL) { | ||
| 889 | if (session->recv_data_progressive_notify) | ||
| 890 | session->recv_data_progressive_notify | ||
| 891 | (session, data_buf->len, 0, | ||
| 892 | session->recv_data_progressive_notify_data); | ||
| 893 | g_get_current_time(&session->tv_prev); | ||
| 894 | } | ||
| 895 | return TRUE; | ||
| 896 | } | ||
| 897 | |||
| 898 | /* complete */ | ||
| 899 | if (session->io_tag > 0) { | ||
| 900 | g_source_remove(session->io_tag); | ||
| 901 | session->io_tag = 0; | ||
| 902 | } | ||
| 903 | |||
| 904 | data_len = data_buf->len - terminator_len; | ||
| 905 | |||
| 906 | /* callback */ | ||
| 907 | ret = session->recv_data_finished(session, (guchar *)data_buf->data, | ||
| 908 | data_len); | ||
| 909 | |||
| 910 | g_byte_array_set_size(data_buf, 0); | ||
| 911 | |||
| 912 | if (session->recv_data_notify) | ||
| 913 | session->recv_data_notify(session, data_len, | ||
| 914 | session->recv_data_notify_data); | ||
| 915 | |||
| 916 | if (ret < 0) { | ||
| 917 | session->state = SESSION_ERROR; | ||
| 918 | priv = session_get_priv(session); | ||
| 919 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 920 | } | ||
| 921 | |||
| 922 | return FALSE; | ||
| 923 | } | ||
| 924 | |||
| 925 | #define READ_BUF_LEFT() \ | ||
| 926 | (SESSION_BUFFSIZE - (session->read_buf_p - session->read_buf) - \ | ||
| 927 | session->read_buf_len) | ||
| 928 | #define PREREAD_SIZE 8 | ||
| 929 | |||
| 930 | static gboolean session_read_data_as_file_cb(SockInfo *source, | ||
| 931 | GIOCondition condition, | ||
| 932 | gpointer data) | ||
| 933 | { | ||
| 934 | Session *session = SESSION(data); | ||
| 935 | SessionPrivData *priv; | ||
| 936 | gint terminator_len; | ||
| 937 | gchar *data_begin_p; | ||
| 938 | gint buf_data_len; | ||
| 939 | gboolean complete = FALSE; | ||
| 940 | gint read_len; | ||
| 941 | gint write_len; | ||
| 942 | gint ret; | ||
| 943 | |||
| 944 | g_return_val_if_fail(condition == G_IO_IN, FALSE); | ||
| 945 | |||
| 946 | if (session->read_buf_len == 0) { | ||
| 947 | read_len = sock_read(session->sock, session->read_buf_p, | ||
| 948 | READ_BUF_LEFT()); | ||
| 949 | |||
| 950 | if (read_len == 0) { | ||
| 951 | g_warning("sock_read: received EOF\n"); | ||
| 952 | session->state = SESSION_EOF; | ||
| 953 | return FALSE; | ||
| 954 | } | ||
| 955 | |||
| 956 | if (read_len < 0) { | ||
| 957 | switch (errno) { | ||
| 958 | case EAGAIN: | ||
| 959 | return TRUE; | ||
| 960 | default: | ||
| 961 | g_warning("%s: sock_read: %s\n", G_STRFUNC, g_strerror(errno)); | ||
| 962 | session->state = SESSION_ERROR; | ||
| 963 | priv = session_get_priv(session); | ||
| 964 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 965 | return FALSE; | ||
| 966 | } | ||
| 967 | } | ||
| 968 | |||
| 969 | session->read_buf_len = read_len; | ||
| 970 | } | ||
| 971 | |||
| 972 | session_set_timeout(session, session->timeout_interval); | ||
| 973 | |||
| 974 | terminator_len = strlen(session->read_data_terminator); | ||
| 975 | |||
| 976 | if (session->read_buf_len == 0) | ||
| 977 | return TRUE; | ||
| 978 | |||
| 979 | /* +---------------buf_data_len---------------+ | ||
| 980 | * +--preread_len--+-------read_buf_len-------+ | ||
| 981 | * +---------------+--------------------------+-------------------+ * | ||
| 982 | * ^data_begin_p ^read_buf_p | ||
| 983 | * ^read_buf | ||
| 984 | */ | ||
| 985 | |||
| 986 | data_begin_p = session->read_buf_p - session->preread_len; | ||
| 987 | buf_data_len = session->preread_len + session->read_buf_len; | ||
| 988 | |||
| 989 | /* check if data is terminated */ | ||
| 990 | if (buf_data_len >= terminator_len) { | ||
| 991 | if (session->read_data_pos == 0 && | ||
| 992 | buf_data_len == terminator_len && | ||
| 993 | memcmp(data_begin_p, session->read_data_terminator, | ||
| 994 | terminator_len) == 0) | ||
| 995 | complete = TRUE; | ||
| 996 | else if (buf_data_len >= terminator_len + 2 && | ||
| 997 | memcmp(data_begin_p + buf_data_len - | ||
| 998 | (terminator_len + 2), "\r\n", 2) == 0 && | ||
| 999 | memcmp(data_begin_p + buf_data_len - | ||
| 1000 | terminator_len, session->read_data_terminator, | ||
| 1001 | terminator_len) == 0) | ||
| 1002 | complete = TRUE; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* incomplete read */ | ||
| 1006 | if (!complete) { | ||
| 1007 | GTimeVal tv_cur; | ||
| 1008 | |||
| 1009 | if (buf_data_len <= PREREAD_SIZE) { | ||
| 1010 | if (data_begin_p > session->read_buf) { | ||
| 1011 | g_memmove(session->read_buf, data_begin_p, | ||
| 1012 | buf_data_len); | ||
| 1013 | data_begin_p = session->read_buf; | ||
| 1014 | session->read_buf_p = session->read_buf + | ||
| 1015 | session->preread_len; | ||
| 1016 | } | ||
| 1017 | session->read_buf_p += session->read_buf_len; | ||
| 1018 | session->preread_len = buf_data_len; | ||
| 1019 | session->read_buf_len = 0; | ||
| 1020 | return TRUE; | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | if (READ_BUF_LEFT() >= (SESSION_BUFFSIZE / 2)) { | ||
| 1024 | session->read_buf_p += session->read_buf_len; | ||
| 1025 | session->preread_len = buf_data_len; | ||
| 1026 | session->read_buf_len = 0; | ||
| 1027 | return TRUE; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | write_len = buf_data_len - PREREAD_SIZE; | ||
| 1031 | if (fwrite(data_begin_p, write_len, 1, | ||
| 1032 | session->read_data_fp) < 1) { | ||
| 1033 | g_warning("session_read_data_as_file_cb: " | ||
| 1034 | "writing data to file failed\n"); | ||
| 1035 | session->state = SESSION_ERROR; | ||
| 1036 | priv = session_get_priv(session); | ||
| 1037 | priv->error_val = SESSION_ERROR_IO; | ||
| 1038 | return FALSE; | ||
| 1039 | } | ||
| 1040 | session->read_data_pos += write_len; | ||
| 1041 | |||
| 1042 | g_memmove(session->read_buf, data_begin_p + write_len, | ||
| 1043 | PREREAD_SIZE); | ||
| 1044 | session->read_buf_p = session->read_buf + PREREAD_SIZE; | ||
| 1045 | session->preread_len = PREREAD_SIZE; | ||
| 1046 | session->read_buf_len = 0; | ||
| 1047 | |||
| 1048 | g_get_current_time(&tv_cur); | ||
| 1049 | if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || | ||
| 1050 | tv_cur.tv_usec - session->tv_prev.tv_usec > | ||
| 1051 | UI_REFRESH_INTERVAL) { | ||
| 1052 | if (session->recv_data_progressive_notify) | ||
| 1053 | session->recv_data_progressive_notify | ||
| 1054 | (session, session->read_data_pos, 0, | ||
| 1055 | session->recv_data_progressive_notify_data); | ||
| 1056 | g_get_current_time(&session->tv_prev); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | return TRUE; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | /* complete */ | ||
| 1063 | if (session->io_tag > 0) { | ||
| 1064 | g_source_remove(session->io_tag); | ||
| 1065 | session->io_tag = 0; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | write_len = buf_data_len - terminator_len; | ||
| 1069 | if (write_len > 0 && fwrite(data_begin_p, write_len, 1, | ||
| 1070 | session->read_data_fp) < 1) { | ||
| 1071 | g_warning("session_read_data_as_file_cb: " | ||
| 1072 | "writing data to file failed\n"); | ||
| 1073 | session->state = SESSION_ERROR; | ||
| 1074 | priv = session_get_priv(session); | ||
| 1075 | priv->error_val = SESSION_ERROR_IO; | ||
| 1076 | return FALSE; | ||
| 1077 | } | ||
| 1078 | session->read_data_pos += write_len; | ||
| 1079 | |||
| 1080 | if (fflush(session->read_data_fp) == EOF) { | ||
| 1081 | perror("fflush"); | ||
| 1082 | g_warning("session_read_data_as_file_cb: " | ||
| 1083 | "writing data to file failed\n"); | ||
| 1084 | session->state = SESSION_ERROR; | ||
| 1085 | priv = session_get_priv(session); | ||
| 1086 | priv->error_val = SESSION_ERROR_IO; | ||
| 1087 | return FALSE; | ||
| 1088 | } | ||
| 1089 | rewind(session->read_data_fp); | ||
| 1090 | |||
| 1091 | session->preread_len = 0; | ||
| 1092 | session->read_buf_len = 0; | ||
| 1093 | session->read_buf_p = session->read_buf; | ||
| 1094 | |||
| 1095 | /* callback */ | ||
| 1096 | ret = session->recv_data_as_file_finished | ||
| 1097 | (session, session->read_data_fp, session->read_data_pos); | ||
| 1098 | |||
| 1099 | fclose(session->read_data_fp); | ||
| 1100 | session->read_data_fp = NULL; | ||
| 1101 | |||
| 1102 | if (session->recv_data_notify) | ||
| 1103 | session->recv_data_notify(session, session->read_data_pos, | ||
| 1104 | session->recv_data_notify_data); | ||
| 1105 | |||
| 1106 | session->read_data_pos = 0; | ||
| 1107 | |||
| 1108 | if (ret < 0) { | ||
| 1109 | session->state = SESSION_ERROR; | ||
| 1110 | priv = session_get_priv(session); | ||
| 1111 | priv->error_val = SESSION_ERROR_IO; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | return FALSE; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | static gint session_write_buf(Session *session) | ||
| 1118 | { | ||
| 1119 | gint write_len; | ||
| 1120 | gint to_write_len; | ||
| 1121 | SessionPrivData *priv; | ||
| 1122 | |||
| 1123 | g_return_val_if_fail(session->write_buf != NULL, -1); | ||
| 1124 | g_return_val_if_fail(session->write_buf_p != NULL, -1); | ||
| 1125 | g_return_val_if_fail(session->write_buf_len > 0, -1); | ||
| 1126 | |||
| 1127 | to_write_len = session->write_buf_len - | ||
| 1128 | (session->write_buf_p - session->write_buf); | ||
| 1129 | to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); | ||
| 1130 | |||
| 1131 | write_len = sock_write(session->sock, session->write_buf_p, | ||
| 1132 | to_write_len); | ||
| 1133 | |||
| 1134 | if (write_len < 0) { | ||
| 1135 | switch (errno) { | ||
| 1136 | case EAGAIN: | ||
| 1137 | write_len = 0; | ||
| 1138 | break; | ||
| 1139 | default: | ||
| 1140 | g_warning("sock_write: %s\n", g_strerror(errno)); | ||
| 1141 | session->state = SESSION_ERROR; | ||
| 1142 | priv = session_get_priv(session); | ||
| 1143 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 1144 | return -1; | ||
| 1145 | } | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | /* incomplete write */ | ||
| 1149 | if (session->write_buf_p - session->write_buf + write_len < | ||
| 1150 | session->write_buf_len) { | ||
| 1151 | session->write_buf_p += write_len; | ||
| 1152 | return 1; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | g_free(session->write_buf); | ||
| 1156 | session->write_buf = NULL; | ||
| 1157 | session->write_buf_p = NULL; | ||
| 1158 | session->write_buf_len = 0; | ||
| 1159 | |||
| 1160 | return 0; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | #define WRITE_DATA_BUFFSIZE 8192 | ||
| 1164 | |||
| 1165 | static gint session_write_data(Session *session, gint *nwritten) | ||
| 1166 | { | ||
| 1167 | gchar buf[WRITE_DATA_BUFFSIZE]; | ||
| 1168 | gint write_len; | ||
| 1169 | gint to_write_len; | ||
| 1170 | SessionPrivData *priv; | ||
| 1171 | |||
| 1172 | g_return_val_if_fail(session->write_data_fp != NULL, -1); | ||
| 1173 | g_return_val_if_fail(session->write_data_pos >= 0, -1); | ||
| 1174 | g_return_val_if_fail(session->write_data_len > 0, -1); | ||
| 1175 | |||
| 1176 | to_write_len = session->write_data_len - session->write_data_pos; | ||
| 1177 | to_write_len = MIN(to_write_len, WRITE_DATA_BUFFSIZE); | ||
| 1178 | if (fread(buf, to_write_len, 1, session->write_data_fp) < 1) { | ||
| 1179 | g_warning("session_write_data: reading data from file failed\n"); | ||
| 1180 | session->state = SESSION_ERROR; | ||
| 1181 | priv = session_get_priv(session); | ||
| 1182 | priv->error_val = SESSION_ERROR_IO; | ||
| 1183 | return -1; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | write_len = sock_write(session->sock, buf, to_write_len); | ||
| 1187 | |||
| 1188 | if (write_len < 0) { | ||
| 1189 | switch (errno) { | ||
| 1190 | case EAGAIN: | ||
| 1191 | write_len = 0; | ||
| 1192 | break; | ||
| 1193 | default: | ||
| 1194 | g_warning("sock_write: %s\n", g_strerror(errno)); | ||
| 1195 | session->state = SESSION_ERROR; | ||
| 1196 | priv = session_get_priv(session); | ||
| 1197 | priv->error_val = SESSION_ERROR_SOCKET; | ||
| 1198 | *nwritten = write_len; | ||
| 1199 | return -1; | ||
| 1200 | } | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | *nwritten = write_len; | ||
| 1204 | |||
| 1205 | /* incomplete write */ | ||
| 1206 | if (session->write_data_pos + write_len < session->write_data_len) { | ||
| 1207 | session->write_data_pos += write_len; | ||
| 1208 | if (write_len < to_write_len) { | ||
| 1209 | if (fseek(session->write_data_fp, | ||
| 1210 | session->write_data_pos, SEEK_SET) < 0) { | ||
| 1211 | g_warning("session_write_data: file seek failed\n"); | ||
| 1212 | session->state = SESSION_ERROR; | ||
| 1213 | priv = session_get_priv(session); | ||
| 1214 | priv->error_val = SESSION_ERROR_IO; | ||
| 1215 | return -1; | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | return 1; | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | session->write_data_fp = NULL; | ||
| 1222 | session->write_data_pos = 0; | ||
| 1223 | session->write_data_len = 0; | ||
| 1224 | |||
| 1225 | return 0; | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, | ||
| 1229 | gpointer data) | ||
| 1230 | { | ||
| 1231 | Session *session = SESSION(data); | ||
| 1232 | SessionPrivData *priv; | ||
| 1233 | gint ret; | ||
| 1234 | |||
| 1235 | g_return_val_if_fail(condition == G_IO_OUT, FALSE); | ||
| 1236 | g_return_val_if_fail(session->write_buf != NULL, FALSE); | ||
| 1237 | g_return_val_if_fail(session->write_buf_p != NULL, FALSE); | ||
| 1238 | g_return_val_if_fail(session->write_buf_len > 0, FALSE); | ||
| 1239 | |||
| 1240 | ret = session_write_buf(session); | ||
| 1241 | |||
| 1242 | if (ret < 0) { | ||
| 1243 | session->state = SESSION_ERROR; | ||
| 1244 | priv = session_get_priv(session); | ||
| 1245 | if (priv->error_val == SESSION_ERROR_OK) | ||
| 1246 | priv->error_val = SESSION_ERROR_IO; | ||
| 1247 | return FALSE; | ||
| 1248 | } else if (ret > 0) | ||
| 1249 | return TRUE; | ||
| 1250 | |||
| 1251 | if (session->io_tag > 0) { | ||
| 1252 | g_source_remove(session->io_tag); | ||
| 1253 | session->io_tag = 0; | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | session_recv_msg(session); | ||
| 1257 | |||
| 1258 | return FALSE; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | static gboolean session_write_data_cb(SockInfo *source, | ||
| 1262 | GIOCondition condition, gpointer data) | ||
| 1263 | { | ||
| 1264 | Session *session = SESSION(data); | ||
| 1265 | SessionPrivData *priv; | ||
| 1266 | guint write_data_len; | ||
| 1267 | gint write_len; | ||
| 1268 | gint ret; | ||
| 1269 | |||
| 1270 | g_return_val_if_fail(condition == G_IO_OUT, FALSE); | ||
| 1271 | g_return_val_if_fail(session->write_data_fp != NULL, FALSE); | ||
| 1272 | g_return_val_if_fail(session->write_data_pos >= 0, FALSE); | ||
| 1273 | g_return_val_if_fail(session->write_data_len > 0, FALSE); | ||
| 1274 | |||
| 1275 | write_data_len = session->write_data_len; | ||
| 1276 | |||
| 1277 | ret = session_write_data(session, &write_len); | ||
| 1278 | |||
| 1279 | if (ret < 0) { | ||
| 1280 | session->state = SESSION_ERROR; | ||
| 1281 | priv = session_get_priv(session); | ||
| 1282 | if (priv->error_val == SESSION_ERROR_OK) | ||
| 1283 | priv->error_val = SESSION_ERROR_IO; | ||
| 1284 | return FALSE; | ||
| 1285 | } else if (ret > 0) { | ||
| 1286 | GTimeVal tv_cur; | ||
| 1287 | |||
| 1288 | g_get_current_time(&tv_cur); | ||
| 1289 | if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || | ||
| 1290 | tv_cur.tv_usec - session->tv_prev.tv_usec > | ||
| 1291 | UI_REFRESH_INTERVAL) { | ||
| 1292 | session_set_timeout(session, session->timeout_interval); | ||
| 1293 | if (session->send_data_progressive_notify) | ||
| 1294 | session->send_data_progressive_notify | ||
| 1295 | (session, | ||
| 1296 | session->write_data_pos, | ||
| 1297 | write_data_len, | ||
| 1298 | session->send_data_progressive_notify_data); | ||
| 1299 | g_get_current_time(&session->tv_prev); | ||
| 1300 | } | ||
| 1301 | return TRUE; | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | if (session->io_tag > 0) { | ||
| 1305 | g_source_remove(session->io_tag); | ||
| 1306 | session->io_tag = 0; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | /* callback */ | ||
| 1310 | ret = session->send_data_finished(session, write_data_len); | ||
| 1311 | if (session->send_data_notify) | ||
| 1312 | session->send_data_notify(session, write_data_len, | ||
| 1313 | session->send_data_notify_data); | ||
| 1314 | |||
| 1315 | #ifdef G_OS_WIN32 | ||
| 1316 | sock_set_nonblocking_mode(session->sock, session->nonblocking); | ||
| 1317 | #endif | ||
| 1318 | |||
| 1319 | return FALSE; | ||
| 1320 | } | ||
diff --git a/libsylph/session.h b/libsylph/session.h new file mode 100644 index 0000000..41c10e2 --- /dev/null +++ b/libsylph/session.h | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SESSION_H__ | ||
| 21 | #define __SESSION_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <time.h> | ||
| 30 | #include <unistd.h> | ||
| 31 | |||
| 32 | #include "socket.h" | ||
| 33 | #include "socks.h" | ||
| 34 | #include "utils.h" | ||
| 35 | |||
| 36 | #define SESSION_BUFFSIZE 8192 | ||
| 37 | |||
| 38 | typedef struct _Session Session; | ||
| 39 | |||
| 40 | #define SESSION(obj) ((Session *)obj) | ||
| 41 | |||
| 42 | typedef enum { | ||
| 43 | SESSION_UNKNOWN, | ||
| 44 | SESSION_IMAP, | ||
| 45 | SESSION_NEWS, | ||
| 46 | SESSION_SMTP, | ||
| 47 | SESSION_POP3 | ||
| 48 | } SessionType; | ||
| 49 | |||
| 50 | typedef enum { | ||
| 51 | SESSION_READY, | ||
| 52 | SESSION_SEND, | ||
| 53 | SESSION_RECV, | ||
| 54 | SESSION_EOF, | ||
| 55 | SESSION_TIMEOUT, | ||
| 56 | SESSION_ERROR, | ||
| 57 | SESSION_DISCONNECTED | ||
| 58 | } SessionState; | ||
| 59 | |||
| 60 | typedef enum | ||
| 61 | { | ||
| 62 | SESSION_MSG_NORMAL, | ||
| 63 | SESSION_MSG_SEND_DATA, | ||
| 64 | SESSION_MSG_RECV_DATA, | ||
| 65 | SESSION_MSG_CONTROL, | ||
| 66 | SESSION_MSG_ERROR, | ||
| 67 | SESSION_MSG_UNKNOWN | ||
| 68 | } SessionMsgType; | ||
| 69 | |||
| 70 | #ifndef USE_SSL | ||
| 71 | typedef enum | ||
| 72 | { | ||
| 73 | SSL_NONE | ||
| 74 | } SSLType; | ||
| 75 | #endif | ||
| 76 | |||
| 77 | typedef enum { | ||
| 78 | SESSION_ERROR_OK, | ||
| 79 | SESSION_ERROR_LOOKUP, | ||
| 80 | SESSION_ERROR_CONNFAIL, | ||
| 81 | SESSION_ERROR_IO, | ||
| 82 | SESSION_ERROR_SOCKET, | ||
| 83 | SESSION_ERROR_TIMEOUT, | ||
| 84 | SESSION_ERROR_ERROR | ||
| 85 | } SessionErrorValue; | ||
| 86 | |||
| 87 | typedef gint (*RecvMsgNotify) (Session *session, | ||
| 88 | const gchar *msg, | ||
| 89 | gpointer user_data); | ||
| 90 | typedef gint (*RecvDataProgressiveNotify) (Session *session, | ||
| 91 | guint cur_len, | ||
| 92 | guint total_len, | ||
| 93 | gpointer user_data); | ||
| 94 | typedef gint (*RecvDataNotify) (Session *session, | ||
| 95 | guint len, | ||
| 96 | gpointer user_data); | ||
| 97 | typedef gint (*SendDataProgressiveNotify) (Session *session, | ||
| 98 | guint cur_len, | ||
| 99 | guint total_len, | ||
| 100 | gpointer user_data); | ||
| 101 | typedef gint (*SendDataNotify) (Session *session, | ||
| 102 | guint len, | ||
| 103 | gpointer user_data); | ||
| 104 | |||
| 105 | struct _Session | ||
| 106 | { | ||
| 107 | SessionType type; | ||
| 108 | |||
| 109 | SockInfo *sock; | ||
| 110 | |||
| 111 | gchar *server; | ||
| 112 | gushort port; | ||
| 113 | |||
| 114 | SSLType ssl_type; | ||
| 115 | |||
| 116 | gboolean nonblocking; | ||
| 117 | |||
| 118 | SessionState state; | ||
| 119 | |||
| 120 | stime_t last_access_time; | ||
| 121 | GTimeVal tv_prev; | ||
| 122 | |||
| 123 | gint conn_id; | ||
| 124 | |||
| 125 | gint io_tag; | ||
| 126 | |||
| 127 | gchar read_buf[SESSION_BUFFSIZE]; | ||
| 128 | gchar *read_buf_p; | ||
| 129 | gint read_buf_len; | ||
| 130 | |||
| 131 | /* buffer for short messages */ | ||
| 132 | GString *read_msg_buf; | ||
| 133 | |||
| 134 | /* buffer for relatively short multiple lines data */ | ||
| 135 | GByteArray *read_data_buf; | ||
| 136 | gchar *read_data_terminator; | ||
| 137 | |||
| 138 | /* buffer for large data */ | ||
| 139 | FILE *read_data_fp; | ||
| 140 | gint read_data_pos; | ||
| 141 | |||
| 142 | gint preread_len; | ||
| 143 | |||
| 144 | /* buffer for short messages */ | ||
| 145 | gchar *write_buf; | ||
| 146 | gchar *write_buf_p; | ||
| 147 | gint write_buf_len; | ||
| 148 | |||
| 149 | /* buffer for large data */ | ||
| 150 | FILE *write_data_fp; | ||
| 151 | gint write_data_pos; | ||
| 152 | gint write_data_len; | ||
| 153 | |||
| 154 | guint timeout_tag; | ||
| 155 | guint timeout_interval; | ||
| 156 | |||
| 157 | guint idle_tag; | ||
| 158 | guint ping_tag; | ||
| 159 | |||
| 160 | gpointer data; | ||
| 161 | |||
| 162 | /* virtual methods to parse server responses */ | ||
| 163 | gint (*recv_msg) (Session *session, | ||
| 164 | const gchar *msg); | ||
| 165 | |||
| 166 | gint (*send_data_finished) (Session *session, | ||
| 167 | guint len); | ||
| 168 | gint (*recv_data_finished) (Session *session, | ||
| 169 | guchar *data, | ||
| 170 | guint len); | ||
| 171 | |||
| 172 | gint (*recv_data_as_file_finished) (Session *session, | ||
| 173 | FILE *fp, | ||
| 174 | guint len); | ||
| 175 | |||
| 176 | void (*destroy) (Session *session); | ||
| 177 | |||
| 178 | /* notification functions */ | ||
| 179 | RecvMsgNotify recv_msg_notify; | ||
| 180 | RecvDataProgressiveNotify recv_data_progressive_notify; | ||
| 181 | RecvDataNotify recv_data_notify; | ||
| 182 | SendDataProgressiveNotify send_data_progressive_notify; | ||
| 183 | SendDataNotify send_data_notify; | ||
| 184 | |||
| 185 | gpointer recv_msg_notify_data; | ||
| 186 | gpointer recv_data_progressive_notify_data; | ||
| 187 | gpointer recv_data_notify_data; | ||
| 188 | gpointer send_data_progressive_notify_data; | ||
| 189 | gpointer send_data_notify_data; | ||
| 190 | }; | ||
| 191 | |||
| 192 | void session_init (Session *session); | ||
| 193 | gint session_connect (Session *session, | ||
| 194 | const gchar *server, | ||
| 195 | gushort port); | ||
| 196 | gint session_connect_full (Session *session, | ||
| 197 | const gchar *server, | ||
| 198 | gushort port, | ||
| 199 | SocksInfo *socks_info); | ||
| 200 | gint session_disconnect (Session *session); | ||
| 201 | void session_destroy (Session *session); | ||
| 202 | gboolean session_is_connected (Session *session); | ||
| 203 | |||
| 204 | SessionErrorValue session_get_error (Session *session); | ||
| 205 | |||
| 206 | void session_set_access_time (Session *session); | ||
| 207 | |||
| 208 | void session_set_timeout (Session *session, | ||
| 209 | guint interval); | ||
| 210 | |||
| 211 | void session_set_recv_message_notify (Session *session, | ||
| 212 | RecvMsgNotify notify_func, | ||
| 213 | gpointer data); | ||
| 214 | void session_set_recv_data_progressive_notify | ||
| 215 | (Session *session, | ||
| 216 | RecvDataProgressiveNotify notify_func, | ||
| 217 | gpointer data); | ||
| 218 | void session_set_recv_data_notify (Session *session, | ||
| 219 | RecvDataNotify notify_func, | ||
| 220 | gpointer data); | ||
| 221 | void session_set_send_data_progressive_notify | ||
| 222 | (Session *session, | ||
| 223 | SendDataProgressiveNotify notify_func, | ||
| 224 | gpointer data); | ||
| 225 | void session_set_send_data_notify (Session *session, | ||
| 226 | SendDataNotify notify_func, | ||
| 227 | gpointer data); | ||
| 228 | |||
| 229 | #if USE_SSL | ||
| 230 | gint session_start_tls (Session *session); | ||
| 231 | #endif | ||
| 232 | |||
| 233 | gint session_send_msg (Session *session, | ||
| 234 | SessionMsgType type, | ||
| 235 | const gchar *msg); | ||
| 236 | gint session_recv_msg (Session *session); | ||
| 237 | gint session_send_data (Session *session, | ||
| 238 | FILE *data_fp, | ||
| 239 | guint size); | ||
| 240 | gint session_recv_data (Session *session, | ||
| 241 | guint size, | ||
| 242 | const gchar *terminator); | ||
| 243 | |||
| 244 | gint session_recv_data_as_file (Session *session, | ||
| 245 | guint size, | ||
| 246 | const gchar *terminator); | ||
| 247 | |||
| 248 | #endif /* __SESSION_H__ */ | ||
diff --git a/libsylph/smtp.c b/libsylph/smtp.c new file mode 100644 index 0000000..da41a97 --- /dev/null +++ b/libsylph/smtp.c | |||
| @@ -0,0 +1,617 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2008 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <glib/gi18n.h> | ||
| 26 | #include <stdio.h> | ||
| 27 | #include <string.h> | ||
| 28 | |||
| 29 | #include "smtp.h" | ||
| 30 | #include "md5_hmac.h" | ||
| 31 | #include "base64.h" | ||
| 32 | #include "utils.h" | ||
| 33 | |||
| 34 | static void smtp_session_destroy(Session *session); | ||
| 35 | |||
| 36 | static gint smtp_from(SMTPSession *session); | ||
| 37 | |||
| 38 | static gint smtp_auth(SMTPSession *session); | ||
| 39 | static gint smtp_starttls(SMTPSession *session); | ||
| 40 | static gint smtp_auth_cram_md5(SMTPSession *session); | ||
| 41 | static gint smtp_auth_plain(SMTPSession *session); | ||
| 42 | static gint smtp_auth_login(SMTPSession *session); | ||
| 43 | |||
| 44 | static gint smtp_ehlo(SMTPSession *session); | ||
| 45 | static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg); | ||
| 46 | |||
| 47 | static gint smtp_helo(SMTPSession *session); | ||
| 48 | static gint smtp_rcpt(SMTPSession *session); | ||
| 49 | static gint smtp_data(SMTPSession *session); | ||
| 50 | static gint smtp_send_data(SMTPSession *session); | ||
| 51 | /* static gint smtp_rset(SMTPSession *session); */ | ||
| 52 | static gint smtp_quit(SMTPSession *session); | ||
| 53 | static gint smtp_eom(SMTPSession *session); | ||
| 54 | |||
| 55 | static gint smtp_session_recv_msg(Session *session, const gchar *msg); | ||
| 56 | static gint smtp_session_send_data_finished(Session *session, guint len); | ||
| 57 | |||
| 58 | |||
| 59 | Session *smtp_session_new(void) | ||
| 60 | { | ||
| 61 | SMTPSession *session; | ||
| 62 | |||
| 63 | session = g_new0(SMTPSession, 1); | ||
| 64 | |||
| 65 | session_init(SESSION(session)); | ||
| 66 | |||
| 67 | SESSION(session)->type = SESSION_SMTP; | ||
| 68 | |||
| 69 | SESSION(session)->recv_msg = smtp_session_recv_msg; | ||
| 70 | |||
| 71 | SESSION(session)->recv_data_finished = NULL; | ||
| 72 | SESSION(session)->send_data_finished = smtp_session_send_data_finished; | ||
| 73 | |||
| 74 | SESSION(session)->destroy = smtp_session_destroy; | ||
| 75 | |||
| 76 | session->state = SMTP_READY; | ||
| 77 | |||
| 78 | #if USE_SSL | ||
| 79 | session->tls_init_done = FALSE; | ||
| 80 | #endif | ||
| 81 | |||
| 82 | session->hostname = NULL; | ||
| 83 | session->user = NULL; | ||
| 84 | session->pass = NULL; | ||
| 85 | |||
| 86 | session->from = NULL; | ||
| 87 | session->to_list = NULL; | ||
| 88 | session->cur_to = NULL; | ||
| 89 | |||
| 90 | session->send_data_fp = NULL; | ||
| 91 | session->send_data_len = 0; | ||
| 92 | |||
| 93 | session->avail_auth_type = 0; | ||
| 94 | session->forced_auth_type = 0; | ||
| 95 | session->auth_type = 0; | ||
| 96 | |||
| 97 | session->error_val = SM_OK; | ||
| 98 | session->error_msg = NULL; | ||
| 99 | |||
| 100 | return SESSION(session); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void smtp_session_destroy(Session *session) | ||
| 104 | { | ||
| 105 | SMTPSession *smtp_session = SMTP_SESSION(session); | ||
| 106 | |||
| 107 | g_free(smtp_session->hostname); | ||
| 108 | g_free(smtp_session->user); | ||
| 109 | g_free(smtp_session->pass); | ||
| 110 | g_free(smtp_session->from); | ||
| 111 | |||
| 112 | if (smtp_session->send_data_fp) | ||
| 113 | fclose(smtp_session->send_data_fp); | ||
| 114 | |||
| 115 | g_free(smtp_session->error_msg); | ||
| 116 | } | ||
| 117 | |||
| 118 | static gint smtp_from(SMTPSession *session) | ||
| 119 | { | ||
| 120 | gchar buf[SMTPBUFSIZE]; | ||
| 121 | |||
| 122 | g_return_val_if_fail(session->from != NULL, SM_ERROR); | ||
| 123 | |||
| 124 | session->state = SMTP_FROM; | ||
| 125 | |||
| 126 | if (strchr(session->from, '<')) | ||
| 127 | g_snprintf(buf, sizeof(buf), "MAIL FROM:%s", session->from); | ||
| 128 | else | ||
| 129 | g_snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", session->from); | ||
| 130 | |||
| 131 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 132 | log_print("SMTP> %s\n", buf); | ||
| 133 | |||
| 134 | return SM_OK; | ||
| 135 | } | ||
| 136 | |||
| 137 | static gint smtp_auth(SMTPSession *session) | ||
| 138 | { | ||
| 139 | |||
| 140 | g_return_val_if_fail(session->user != NULL, SM_ERROR); | ||
| 141 | |||
| 142 | session->state = SMTP_AUTH; | ||
| 143 | |||
| 144 | if (session->forced_auth_type == SMTPAUTH_CRAM_MD5 || | ||
| 145 | (session->forced_auth_type == 0 && | ||
| 146 | (session->avail_auth_type & SMTPAUTH_CRAM_MD5) != 0)) | ||
| 147 | smtp_auth_cram_md5(session); | ||
| 148 | else if (session->forced_auth_type == SMTPAUTH_PLAIN || | ||
| 149 | (session->forced_auth_type == 0 && | ||
| 150 | (session->avail_auth_type & SMTPAUTH_PLAIN) != 0)) | ||
| 151 | smtp_auth_plain(session); | ||
| 152 | else if (session->forced_auth_type == SMTPAUTH_LOGIN || | ||
| 153 | (session->forced_auth_type == 0 && | ||
| 154 | (session->avail_auth_type & SMTPAUTH_LOGIN) != 0)) | ||
| 155 | smtp_auth_login(session); | ||
| 156 | else { | ||
| 157 | log_warning(_("SMTP AUTH not available\n")); | ||
| 158 | return SM_AUTHFAIL; | ||
| 159 | } | ||
| 160 | |||
| 161 | return SM_OK; | ||
| 162 | } | ||
| 163 | |||
| 164 | static gint smtp_auth_recv(SMTPSession *session, const gchar *msg) | ||
| 165 | { | ||
| 166 | gchar buf[SMTPBUFSIZE]; | ||
| 167 | |||
| 168 | switch (session->auth_type) { | ||
| 169 | case SMTPAUTH_LOGIN: | ||
| 170 | session->state = SMTP_AUTH_LOGIN_USER; | ||
| 171 | |||
| 172 | if (!strncmp(msg, "334 ", 4)) { | ||
| 173 | base64_encode(buf, (guchar *)session->user, | ||
| 174 | strlen(session->user)); | ||
| 175 | |||
| 176 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, | ||
| 177 | buf); | ||
| 178 | log_print("ESMTP> [USERID]\n"); | ||
| 179 | } else { | ||
| 180 | /* Server rejects AUTH */ | ||
| 181 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, | ||
| 182 | "*"); | ||
| 183 | log_print("ESMTP> *\n"); | ||
| 184 | } | ||
| 185 | break; | ||
| 186 | case SMTPAUTH_CRAM_MD5: | ||
| 187 | session->state = SMTP_AUTH_CRAM_MD5; | ||
| 188 | |||
| 189 | if (!strncmp(msg, "334 ", 4)) { | ||
| 190 | gchar *response; | ||
| 191 | gchar *response64; | ||
| 192 | guchar *challenge; | ||
| 193 | gint challengelen; | ||
| 194 | gchar hexdigest[33]; | ||
| 195 | |||
| 196 | challenge = g_malloc(strlen(msg + 4) + 1); | ||
| 197 | challengelen = base64_decode(challenge, msg + 4, -1); | ||
| 198 | challenge[challengelen] = '\0'; | ||
| 199 | log_print("ESMTP< [Decoded: %s]\n", challenge); | ||
| 200 | |||
| 201 | g_snprintf(buf, sizeof(buf), "%s", session->pass); | ||
| 202 | md5_hex_hmac(hexdigest, challenge, challengelen, | ||
| 203 | (guchar *)buf, strlen(buf)); | ||
| 204 | g_free(challenge); | ||
| 205 | |||
| 206 | response = g_strdup_printf | ||
| 207 | ("%s %s", session->user, hexdigest); | ||
| 208 | log_print("ESMTP> [Encoded: %s]\n", response); | ||
| 209 | |||
| 210 | response64 = g_malloc((strlen(response) + 3) * 2 + 1); | ||
| 211 | base64_encode(response64, (guchar *)response, | ||
| 212 | strlen(response)); | ||
| 213 | g_free(response); | ||
| 214 | |||
| 215 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, | ||
| 216 | response64); | ||
| 217 | log_print("ESMTP> %s\n", response64); | ||
| 218 | g_free(response64); | ||
| 219 | } else { | ||
| 220 | /* Server rejects AUTH */ | ||
| 221 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, | ||
| 222 | "*"); | ||
| 223 | log_print("ESMTP> *\n"); | ||
| 224 | } | ||
| 225 | break; | ||
| 226 | case SMTPAUTH_DIGEST_MD5: | ||
| 227 | default: | ||
| 228 | /* stop smtp_auth when no correct authtype */ | ||
| 229 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "*"); | ||
| 230 | log_print("ESMTP> *\n"); | ||
| 231 | break; | ||
| 232 | } | ||
| 233 | |||
| 234 | return SM_OK; | ||
| 235 | } | ||
| 236 | |||
| 237 | static gint smtp_auth_login_user_recv(SMTPSession *session, const gchar *msg) | ||
| 238 | { | ||
| 239 | gchar buf[SMTPBUFSIZE]; | ||
| 240 | |||
| 241 | session->state = SMTP_AUTH_LOGIN_PASS; | ||
| 242 | |||
| 243 | if (!strncmp(msg, "334 ", 4)) | ||
| 244 | base64_encode(buf, (guchar *)session->pass, | ||
| 245 | strlen(session->pass)); | ||
| 246 | else | ||
| 247 | /* Server rejects AUTH */ | ||
| 248 | g_snprintf(buf, sizeof(buf), "*"); | ||
| 249 | |||
| 250 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 251 | log_print("ESMTP> [PASSWORD]\n"); | ||
| 252 | |||
| 253 | return SM_OK; | ||
| 254 | } | ||
| 255 | |||
| 256 | static gint smtp_ehlo(SMTPSession *session) | ||
| 257 | { | ||
| 258 | gchar buf[SMTPBUFSIZE]; | ||
| 259 | |||
| 260 | session->state = SMTP_EHLO; | ||
| 261 | |||
| 262 | session->avail_auth_type = 0; | ||
| 263 | |||
| 264 | g_snprintf(buf, sizeof(buf), "EHLO %s", | ||
| 265 | session->hostname ? session->hostname : get_domain_name()); | ||
| 266 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 267 | log_print("ESMTP> %s\n", buf); | ||
| 268 | |||
| 269 | return SM_OK; | ||
| 270 | } | ||
| 271 | |||
| 272 | static gint smtp_ehlo_recv(SMTPSession *session, const gchar *msg) | ||
| 273 | { | ||
| 274 | if (strncmp(msg, "250", 3) == 0) { | ||
| 275 | const gchar *p = msg; | ||
| 276 | p += 3; | ||
| 277 | if (*p == '-' || *p == ' ') p++; | ||
| 278 | if (g_ascii_strncasecmp(p, "AUTH", 4) == 0 && p[4] != '\0') { | ||
| 279 | p += 5; | ||
| 280 | if (strcasestr(p, "PLAIN")) | ||
| 281 | session->avail_auth_type |= SMTPAUTH_PLAIN; | ||
| 282 | if (strcasestr(p, "LOGIN")) | ||
| 283 | session->avail_auth_type |= SMTPAUTH_LOGIN; | ||
| 284 | if (strcasestr(p, "CRAM-MD5")) | ||
| 285 | session->avail_auth_type |= SMTPAUTH_CRAM_MD5; | ||
| 286 | if (strcasestr(p, "DIGEST-MD5")) | ||
| 287 | session->avail_auth_type |= SMTPAUTH_DIGEST_MD5; | ||
| 288 | } | ||
| 289 | return SM_OK; | ||
| 290 | } else if ((msg[0] == '1' || msg[0] == '2' || msg[0] == '3') && | ||
| 291 | (msg[3] == ' ' || msg[3] == '\0')) | ||
| 292 | return SM_OK; | ||
| 293 | else if (msg[0] == '5' && msg[1] == '0' && | ||
| 294 | (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) | ||
| 295 | return SM_ERROR; | ||
| 296 | |||
| 297 | return SM_ERROR; | ||
| 298 | } | ||
| 299 | |||
| 300 | static gint smtp_starttls(SMTPSession *session) | ||
| 301 | { | ||
| 302 | session->state = SMTP_STARTTLS; | ||
| 303 | |||
| 304 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "STARTTLS"); | ||
| 305 | log_print("ESMTP> STARTTLS\n"); | ||
| 306 | |||
| 307 | return SM_OK; | ||
| 308 | } | ||
| 309 | |||
| 310 | static gint smtp_auth_cram_md5(SMTPSession *session) | ||
| 311 | { | ||
| 312 | session->state = SMTP_AUTH; | ||
| 313 | session->auth_type = SMTPAUTH_CRAM_MD5; | ||
| 314 | |||
| 315 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH CRAM-MD5"); | ||
| 316 | log_print("ESMTP> AUTH CRAM-MD5\n"); | ||
| 317 | |||
| 318 | return SM_OK; | ||
| 319 | } | ||
| 320 | |||
| 321 | static gint smtp_auth_plain(SMTPSession *session) | ||
| 322 | { | ||
| 323 | gchar *authstr; | ||
| 324 | gint authlen; | ||
| 325 | gchar *outbuf; | ||
| 326 | gchar *p; | ||
| 327 | |||
| 328 | session->state = SMTP_AUTH_PLAIN; | ||
| 329 | session->auth_type = SMTPAUTH_PLAIN; | ||
| 330 | |||
| 331 | /* | ||
| 332 | * construct the string: \0<user>\0<pass> | ||
| 333 | */ | ||
| 334 | |||
| 335 | authlen = 1 + strlen(session->user) + 1 + strlen(session->pass); | ||
| 336 | authstr = g_malloc(authlen + 1); | ||
| 337 | |||
| 338 | p = authstr; | ||
| 339 | |||
| 340 | *p++ = '\0'; | ||
| 341 | strcpy(p, session->user); | ||
| 342 | p += strlen(p) + 1; | ||
| 343 | strcpy(p, session->pass); | ||
| 344 | |||
| 345 | outbuf = g_malloc(sizeof("AUTH PLAIN ") + authlen * 2 + 1); | ||
| 346 | |||
| 347 | strcpy(outbuf, "AUTH PLAIN "); | ||
| 348 | p = outbuf + strlen(outbuf); | ||
| 349 | base64_encode(p, (guchar *)authstr, authlen); | ||
| 350 | |||
| 351 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, outbuf); | ||
| 352 | log_print("ESMTP> AUTH PLAIN ********\n"); | ||
| 353 | |||
| 354 | g_free(outbuf); | ||
| 355 | g_free(authstr); | ||
| 356 | |||
| 357 | return SM_OK; | ||
| 358 | } | ||
| 359 | |||
| 360 | static gint smtp_auth_login(SMTPSession *session) | ||
| 361 | { | ||
| 362 | session->state = SMTP_AUTH; | ||
| 363 | session->auth_type = SMTPAUTH_LOGIN; | ||
| 364 | |||
| 365 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "AUTH LOGIN"); | ||
| 366 | log_print("ESMTP> AUTH LOGIN\n"); | ||
| 367 | |||
| 368 | return SM_OK; | ||
| 369 | } | ||
| 370 | |||
| 371 | static gint smtp_helo(SMTPSession *session) | ||
| 372 | { | ||
| 373 | gchar buf[SMTPBUFSIZE]; | ||
| 374 | |||
| 375 | session->state = SMTP_HELO; | ||
| 376 | |||
| 377 | g_snprintf(buf, sizeof(buf), "HELO %s", | ||
| 378 | session->hostname ? session->hostname : get_domain_name()); | ||
| 379 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 380 | log_print("SMTP> %s\n", buf); | ||
| 381 | |||
| 382 | return SM_OK; | ||
| 383 | } | ||
| 384 | |||
| 385 | static gint smtp_rcpt(SMTPSession *session) | ||
| 386 | { | ||
| 387 | gchar buf[SMTPBUFSIZE]; | ||
| 388 | gchar *to; | ||
| 389 | |||
| 390 | g_return_val_if_fail(session->cur_to != NULL, SM_ERROR); | ||
| 391 | |||
| 392 | session->state = SMTP_RCPT; | ||
| 393 | |||
| 394 | to = (gchar *)session->cur_to->data; | ||
| 395 | |||
| 396 | if (strchr(to, '<')) | ||
| 397 | g_snprintf(buf, sizeof(buf), "RCPT TO:%s", to); | ||
| 398 | else | ||
| 399 | g_snprintf(buf, sizeof(buf), "RCPT TO:<%s>", to); | ||
| 400 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, buf); | ||
| 401 | log_print("SMTP> %s\n", buf); | ||
| 402 | |||
| 403 | session->cur_to = session->cur_to->next; | ||
| 404 | |||
| 405 | return SM_OK; | ||
| 406 | } | ||
| 407 | |||
| 408 | static gint smtp_data(SMTPSession *session) | ||
| 409 | { | ||
| 410 | session->state = SMTP_DATA; | ||
| 411 | |||
| 412 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "DATA"); | ||
| 413 | log_print("SMTP> DATA\n"); | ||
| 414 | |||
| 415 | return SM_OK; | ||
| 416 | } | ||
| 417 | |||
| 418 | static gint smtp_send_data(SMTPSession *session) | ||
| 419 | { | ||
| 420 | session->state = SMTP_SEND_DATA; | ||
| 421 | |||
| 422 | session_send_data(SESSION(session), session->send_data_fp, | ||
| 423 | session->send_data_len); | ||
| 424 | |||
| 425 | return SM_OK; | ||
| 426 | } | ||
| 427 | |||
| 428 | #if 0 | ||
| 429 | static gint smtp_rset(SMTPSession *session) | ||
| 430 | { | ||
| 431 | session->state = SMTP_RSET; | ||
| 432 | |||
| 433 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "RSET"); | ||
| 434 | log_print("SMTP> RSET\n"); | ||
| 435 | |||
| 436 | return SM_OK; | ||
| 437 | } | ||
| 438 | #endif | ||
| 439 | |||
| 440 | static gint smtp_quit(SMTPSession *session) | ||
| 441 | { | ||
| 442 | session->state = SMTP_QUIT; | ||
| 443 | |||
| 444 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "QUIT"); | ||
| 445 | log_print("SMTP> QUIT\n"); | ||
| 446 | |||
| 447 | return SM_OK; | ||
| 448 | } | ||
| 449 | |||
| 450 | static gint smtp_eom(SMTPSession *session) | ||
| 451 | { | ||
| 452 | session->state = SMTP_EOM; | ||
| 453 | |||
| 454 | session_send_msg(SESSION(session), SESSION_MSG_NORMAL, "."); | ||
| 455 | log_print("SMTP> . (EOM)\n"); | ||
| 456 | |||
| 457 | return SM_OK; | ||
| 458 | } | ||
| 459 | |||
| 460 | static gint smtp_session_recv_msg(Session *session, const gchar *msg) | ||
| 461 | { | ||
| 462 | SMTPSession *smtp_session = SMTP_SESSION(session); | ||
| 463 | gboolean cont = FALSE; | ||
| 464 | |||
| 465 | if (strlen(msg) < 4) { | ||
| 466 | log_warning(_("bad SMTP response\n")); | ||
| 467 | return -1; | ||
| 468 | } | ||
| 469 | |||
| 470 | switch (smtp_session->state) { | ||
| 471 | case SMTP_EHLO: | ||
| 472 | case SMTP_STARTTLS: | ||
| 473 | case SMTP_AUTH: | ||
| 474 | case SMTP_AUTH_PLAIN: | ||
| 475 | case SMTP_AUTH_LOGIN_USER: | ||
| 476 | case SMTP_AUTH_LOGIN_PASS: | ||
| 477 | case SMTP_AUTH_CRAM_MD5: | ||
| 478 | log_print("ESMTP< %s\n", msg); | ||
| 479 | break; | ||
| 480 | default: | ||
| 481 | log_print("SMTP< %s\n", msg); | ||
| 482 | break; | ||
| 483 | } | ||
| 484 | |||
| 485 | if (msg[0] == '5' && msg[1] == '0' && | ||
| 486 | (msg[2] == '4' || msg[2] == '3' || msg[2] == '1')) { | ||
| 487 | log_warning(_("error occurred on SMTP session\n")); | ||
| 488 | smtp_session->state = SMTP_ERROR; | ||
| 489 | smtp_session->error_val = SM_ERROR; | ||
| 490 | g_free(smtp_session->error_msg); | ||
| 491 | smtp_session->error_msg = g_strdup(msg); | ||
| 492 | return -1; | ||
| 493 | } | ||
| 494 | |||
| 495 | if (!strncmp(msg, "535", 3)) { | ||
| 496 | log_warning(_("error occurred on authentication\n")); | ||
| 497 | smtp_session->state = SMTP_ERROR; | ||
| 498 | smtp_session->error_val = SM_AUTHFAIL; | ||
| 499 | g_free(smtp_session->error_msg); | ||
| 500 | smtp_session->error_msg = g_strdup(msg); | ||
| 501 | return -1; | ||
| 502 | } | ||
| 503 | |||
| 504 | if (msg[0] != '1' && msg[0] != '2' && msg[0] != '3') { | ||
| 505 | log_warning(_("error occurred on SMTP session\n")); | ||
| 506 | smtp_session->state = SMTP_ERROR; | ||
| 507 | smtp_session->error_val = SM_ERROR; | ||
| 508 | g_free(smtp_session->error_msg); | ||
| 509 | smtp_session->error_msg = g_strdup(msg); | ||
| 510 | return -1; | ||
| 511 | } | ||
| 512 | |||
| 513 | if (msg[3] == '-') | ||
| 514 | cont = TRUE; | ||
| 515 | else if (msg[3] != ' ' && msg[3] != '\0') { | ||
| 516 | log_warning(_("bad SMTP response\n")); | ||
| 517 | smtp_session->state = SMTP_ERROR; | ||
| 518 | smtp_session->error_val = SM_UNRECOVERABLE; | ||
| 519 | return -1; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* ignore all multiline responses except for EHLO */ | ||
| 523 | if (cont && smtp_session->state != SMTP_EHLO) | ||
| 524 | return session_recv_msg(session); | ||
| 525 | |||
| 526 | switch (smtp_session->state) { | ||
| 527 | case SMTP_READY: | ||
| 528 | case SMTP_CONNECTED: | ||
| 529 | #if USE_SSL | ||
| 530 | if (smtp_session->user || session->ssl_type != SSL_NONE) | ||
| 531 | #else | ||
| 532 | if (smtp_session->user) | ||
| 533 | #endif | ||
| 534 | smtp_ehlo(smtp_session); | ||
| 535 | else | ||
| 536 | smtp_helo(smtp_session); | ||
| 537 | break; | ||
| 538 | case SMTP_HELO: | ||
| 539 | smtp_from(smtp_session); | ||
| 540 | break; | ||
| 541 | case SMTP_EHLO: | ||
| 542 | smtp_ehlo_recv(smtp_session, msg); | ||
| 543 | if (cont == TRUE) | ||
| 544 | break; | ||
| 545 | #if USE_SSL | ||
| 546 | if (session->ssl_type == SSL_STARTTLS && | ||
| 547 | smtp_session->tls_init_done == FALSE) { | ||
| 548 | smtp_starttls(smtp_session); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | #endif | ||
| 552 | if (smtp_session->user) { | ||
| 553 | if (smtp_auth(smtp_session) != SM_OK) | ||
| 554 | smtp_from(smtp_session); | ||
| 555 | } else | ||
| 556 | smtp_from(smtp_session); | ||
| 557 | break; | ||
| 558 | case SMTP_STARTTLS: | ||
| 559 | #if USE_SSL | ||
| 560 | if (session_start_tls(session) < 0) { | ||
| 561 | log_warning(_("can't start TLS session\n")); | ||
| 562 | smtp_session->state = SMTP_ERROR; | ||
| 563 | smtp_session->error_val = SM_ERROR; | ||
| 564 | return -1; | ||
| 565 | } | ||
| 566 | smtp_session->tls_init_done = TRUE; | ||
| 567 | smtp_ehlo(smtp_session); | ||
| 568 | #endif | ||
| 569 | break; | ||
| 570 | case SMTP_AUTH: | ||
| 571 | smtp_auth_recv(smtp_session, msg); | ||
| 572 | break; | ||
| 573 | case SMTP_AUTH_LOGIN_USER: | ||
| 574 | smtp_auth_login_user_recv(smtp_session, msg); | ||
| 575 | break; | ||
| 576 | case SMTP_AUTH_PLAIN: | ||
| 577 | case SMTP_AUTH_LOGIN_PASS: | ||
| 578 | case SMTP_AUTH_CRAM_MD5: | ||
| 579 | smtp_from(smtp_session); | ||
| 580 | break; | ||
| 581 | case SMTP_FROM: | ||
| 582 | if (smtp_session->cur_to) | ||
| 583 | smtp_rcpt(smtp_session); | ||
| 584 | break; | ||
| 585 | case SMTP_RCPT: | ||
| 586 | if (smtp_session->cur_to) | ||
| 587 | smtp_rcpt(smtp_session); | ||
| 588 | else | ||
| 589 | smtp_data(smtp_session); | ||
| 590 | break; | ||
| 591 | case SMTP_DATA: | ||
| 592 | smtp_send_data(smtp_session); | ||
| 593 | break; | ||
| 594 | case SMTP_EOM: | ||
| 595 | smtp_quit(smtp_session); | ||
| 596 | break; | ||
| 597 | case SMTP_QUIT: | ||
| 598 | session_disconnect(session); | ||
| 599 | break; | ||
| 600 | case SMTP_ERROR: | ||
| 601 | default: | ||
| 602 | log_warning(_("error occurred on SMTP session\n")); | ||
| 603 | smtp_session->error_val = SM_ERROR; | ||
| 604 | return -1; | ||
| 605 | } | ||
| 606 | |||
| 607 | if (cont) | ||
| 608 | return session_recv_msg(session); | ||
| 609 | |||
| 610 | return 0; | ||
| 611 | } | ||
| 612 | |||
| 613 | static gint smtp_session_send_data_finished(Session *session, guint len) | ||
| 614 | { | ||
| 615 | smtp_eom(SMTP_SESSION(session)); | ||
| 616 | return 0; | ||
| 617 | } | ||
diff --git a/libsylph/smtp.h b/libsylph/smtp.h new file mode 100644 index 0000000..6bf3ce5 --- /dev/null +++ b/libsylph/smtp.h | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2008 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SMTP_H__ | ||
| 21 | #define __SMTP_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | |||
| 30 | #include "session.h" | ||
| 31 | |||
| 32 | typedef struct _SMTPSession SMTPSession; | ||
| 33 | |||
| 34 | #define SMTP_SESSION(obj) ((SMTPSession *)obj) | ||
| 35 | |||
| 36 | #define SMTPBUFSIZE 8192 | ||
| 37 | |||
| 38 | typedef enum | ||
| 39 | { | ||
| 40 | SM_OK = 0, | ||
| 41 | SM_ERROR = 128, | ||
| 42 | SM_UNRECOVERABLE = 129, | ||
| 43 | SM_AUTHFAIL = 130 | ||
| 44 | } SMTPErrorValue; | ||
| 45 | |||
| 46 | typedef enum | ||
| 47 | { | ||
| 48 | ESMTP_8BITMIME = 1 << 0, | ||
| 49 | ESMTP_SIZE = 1 << 1, | ||
| 50 | ESMTP_ETRN = 1 << 2 | ||
| 51 | } ESMTPFlag; | ||
| 52 | |||
| 53 | typedef enum | ||
| 54 | { | ||
| 55 | SMTPAUTH_LOGIN = 1 << 0, | ||
| 56 | SMTPAUTH_CRAM_MD5 = 1 << 1, | ||
| 57 | SMTPAUTH_DIGEST_MD5 = 1 << 2, | ||
| 58 | SMTPAUTH_PLAIN = 1 << 3 | ||
| 59 | } SMTPAuthType; | ||
| 60 | |||
| 61 | typedef enum | ||
| 62 | { | ||
| 63 | SMTP_READY, | ||
| 64 | SMTP_CONNECTED, | ||
| 65 | SMTP_HELO, | ||
| 66 | SMTP_EHLO, | ||
| 67 | SMTP_STARTTLS, | ||
| 68 | SMTP_FROM, | ||
| 69 | SMTP_AUTH, | ||
| 70 | SMTP_AUTH_PLAIN, | ||
| 71 | SMTP_AUTH_LOGIN_USER, | ||
| 72 | SMTP_AUTH_LOGIN_PASS, | ||
| 73 | SMTP_AUTH_CRAM_MD5, | ||
| 74 | SMTP_RCPT, | ||
| 75 | SMTP_DATA, | ||
| 76 | SMTP_SEND_DATA, | ||
| 77 | SMTP_EOM, | ||
| 78 | SMTP_RSET, | ||
| 79 | SMTP_QUIT, | ||
| 80 | SMTP_ERROR, | ||
| 81 | SMTP_DISCONNECTED, | ||
| 82 | |||
| 83 | N_SMTP_PHASE | ||
| 84 | } SMTPState; | ||
| 85 | |||
| 86 | struct _SMTPSession | ||
| 87 | { | ||
| 88 | Session session; | ||
| 89 | |||
| 90 | SMTPState state; | ||
| 91 | |||
| 92 | gboolean tls_init_done; | ||
| 93 | |||
| 94 | gchar *hostname; | ||
| 95 | |||
| 96 | gchar *user; | ||
| 97 | gchar *pass; | ||
| 98 | |||
| 99 | gchar *from; | ||
| 100 | GSList *to_list; | ||
| 101 | GSList *cur_to; | ||
| 102 | |||
| 103 | FILE *send_data_fp; | ||
| 104 | gint send_data_len; | ||
| 105 | |||
| 106 | SMTPAuthType avail_auth_type; | ||
| 107 | SMTPAuthType forced_auth_type; | ||
| 108 | SMTPAuthType auth_type; | ||
| 109 | |||
| 110 | SMTPErrorValue error_val; | ||
| 111 | gchar *error_msg; | ||
| 112 | }; | ||
| 113 | |||
| 114 | Session *smtp_session_new (void); | ||
| 115 | |||
| 116 | #endif /* __SMTP_H__ */ | ||
diff --git a/libsylph/socket.c b/libsylph/socket.c new file mode 100644 index 0000000..228c8db --- /dev/null +++ b/libsylph/socket.c | |||
| @@ -0,0 +1,2179 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <sys/time.h> | ||
| 26 | #include <sys/types.h> | ||
| 27 | #ifdef G_OS_WIN32 | ||
| 28 | # include <winsock2.h> | ||
| 29 | # include <ws2tcpip.h> | ||
| 30 | #else | ||
| 31 | # if HAVE_SYS_WAIT_H | ||
| 32 | # include <sys/wait.h> | ||
| 33 | # endif | ||
| 34 | # include <sys/socket.h> | ||
| 35 | # include <sys/un.h> | ||
| 36 | # include <netinet/in.h> | ||
| 37 | # include <arpa/inet.h> | ||
| 38 | # include <resolv.h> | ||
| 39 | # include <netdb.h> | ||
| 40 | # include <sys/stat.h> | ||
| 41 | #endif /* G_OS_WIN32 */ | ||
| 42 | #include <unistd.h> | ||
| 43 | #include <stdio.h> | ||
| 44 | #include <string.h> | ||
| 45 | #include <stdarg.h> | ||
| 46 | #include <fcntl.h> | ||
| 47 | #include <errno.h> | ||
| 48 | #include <signal.h> | ||
| 49 | #include <setjmp.h> | ||
| 50 | #if HAVE_SYS_SELECT_H | ||
| 51 | # include <sys/select.h> | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #include "socket.h" | ||
| 55 | #if USE_SSL | ||
| 56 | # include "ssl.h" | ||
| 57 | #endif | ||
| 58 | |||
| 59 | #include "utils.h" | ||
| 60 | |||
| 61 | #define BUFFSIZE 8192 | ||
| 62 | |||
| 63 | #ifdef G_OS_WIN32 | ||
| 64 | #define SockDesc SOCKET | ||
| 65 | #define SOCKET_IS_VALID(s) ((s) != INVALID_SOCKET) | ||
| 66 | #else | ||
| 67 | #define SockDesc gint | ||
| 68 | #define SOCKET_IS_VALID(s) ((s) >= 0) | ||
| 69 | #define INVALID_SOCKET (-1) | ||
| 70 | #endif | ||
| 71 | |||
| 72 | typedef gint (*SockAddrFunc) (GList *addr_list, | ||
| 73 | gpointer data); | ||
| 74 | |||
| 75 | typedef struct _SockConnectData SockConnectData; | ||
| 76 | typedef struct _SockLookupData SockLookupData; | ||
| 77 | typedef struct _SockAddrData SockAddrData; | ||
| 78 | typedef struct _SockSource SockSource; | ||
| 79 | |||
| 80 | struct _SockConnectData { | ||
| 81 | gint id; | ||
| 82 | gchar *hostname; | ||
| 83 | gushort port; | ||
| 84 | #ifdef G_OS_UNIX | ||
| 85 | GList *addr_list; | ||
| 86 | GList *cur_addr; | ||
| 87 | SockLookupData *lookup_data; | ||
| 88 | GIOChannel *channel; | ||
| 89 | guint io_tag; | ||
| 90 | #endif /* G_OS_UNIX */ | ||
| 91 | #if USE_THREADS | ||
| 92 | gint flag; | ||
| 93 | GThread *thread; | ||
| 94 | #endif /* G_OS_UNIX */ | ||
| 95 | SockInfo *sock; | ||
| 96 | SockConnectFunc func; | ||
| 97 | gpointer data; | ||
| 98 | }; | ||
| 99 | |||
| 100 | struct _SockLookupData { | ||
| 101 | gchar *hostname; | ||
| 102 | pid_t child_pid; | ||
| 103 | GIOChannel *channel; | ||
| 104 | guint io_tag; | ||
| 105 | SockAddrFunc func; | ||
| 106 | gpointer data; | ||
| 107 | }; | ||
| 108 | |||
| 109 | struct _SockAddrData { | ||
| 110 | gint family; | ||
| 111 | gint socktype; | ||
| 112 | gint protocol; | ||
| 113 | gint addr_len; | ||
| 114 | struct sockaddr *addr; | ||
| 115 | }; | ||
| 116 | |||
| 117 | struct _SockSource { | ||
| 118 | GSource parent; | ||
| 119 | SockInfo *sock; | ||
| 120 | }; | ||
| 121 | |||
| 122 | static guint io_timeout = 60; | ||
| 123 | |||
| 124 | static GList *sock_connect_data_list = NULL; | ||
| 125 | static GList *sock_list = NULL; | ||
| 126 | |||
| 127 | static gboolean sock_prepare (GSource *source, | ||
| 128 | gint *timeout); | ||
| 129 | static gboolean sock_check (GSource *source); | ||
| 130 | static gboolean sock_dispatch (GSource *source, | ||
| 131 | GSourceFunc callback, | ||
| 132 | gpointer user_data); | ||
| 133 | |||
| 134 | GSourceFuncs sock_watch_funcs = { | ||
| 135 | sock_prepare, | ||
| 136 | sock_check, | ||
| 137 | sock_dispatch, | ||
| 138 | NULL | ||
| 139 | }; | ||
| 140 | |||
| 141 | static SockInfo *sock_find_from_fd (gint fd); | ||
| 142 | |||
| 143 | static gint sock_connect_with_timeout (gint sock, | ||
| 144 | const struct sockaddr *serv_addr, | ||
| 145 | gint addrlen, | ||
| 146 | guint timeout_secs); | ||
| 147 | |||
| 148 | #ifndef INET6 | ||
| 149 | static gint sock_info_connect_by_hostname | ||
| 150 | (SockInfo *sock); | ||
| 151 | #else | ||
| 152 | #ifdef G_OS_WIN32 | ||
| 153 | typedef int (*GetAddrInfoFunc) (const char *node, | ||
| 154 | const char *service, | ||
| 155 | const struct addrinfo *hints, | ||
| 156 | struct addrinfo **res); | ||
| 157 | typedef void (*FreeAddrInfoFunc) (struct addrinfo *res); | ||
| 158 | |||
| 159 | static GetAddrInfoFunc getaddrinfo_func = NULL; | ||
| 160 | static FreeAddrInfoFunc freeaddrinfo_func = NULL; | ||
| 161 | |||
| 162 | #undef getaddrinfo | ||
| 163 | #define getaddrinfo my_getaddrinfo | ||
| 164 | #undef freeaddrinfo | ||
| 165 | #define freeaddrinfo my_freeaddrinfo | ||
| 166 | #endif | ||
| 167 | |||
| 168 | static SockDesc sock_info_connect_by_getaddrinfo(SockInfo *sock); | ||
| 169 | #endif | ||
| 170 | |||
| 171 | #ifdef G_OS_UNIX | ||
| 172 | static void sock_address_list_free (GList *addr_list); | ||
| 173 | |||
| 174 | static gboolean sock_connect_async_cb (GIOChannel *source, | ||
| 175 | GIOCondition condition, | ||
| 176 | gpointer data); | ||
| 177 | static gint sock_connect_async_get_address_info_cb | ||
| 178 | (GList *addr_list, | ||
| 179 | gpointer data); | ||
| 180 | |||
| 181 | static gint sock_connect_address_list_async (SockConnectData *conn_data); | ||
| 182 | |||
| 183 | static gboolean sock_get_address_info_async_cb (GIOChannel *source, | ||
| 184 | GIOCondition condition, | ||
| 185 | gpointer data); | ||
| 186 | static SockLookupData *sock_get_address_info_async | ||
| 187 | (const gchar *hostname, | ||
| 188 | gushort port, | ||
| 189 | SockAddrFunc func, | ||
| 190 | gpointer data); | ||
| 191 | static gint sock_get_address_info_async_cancel (SockLookupData *lookup_data); | ||
| 192 | #endif /* G_OS_UNIX */ | ||
| 193 | |||
| 194 | |||
| 195 | gint sock_init(void) | ||
| 196 | { | ||
| 197 | #ifdef G_OS_WIN32 | ||
| 198 | WSADATA wsadata; | ||
| 199 | gint result; | ||
| 200 | |||
| 201 | result = WSAStartup(MAKEWORD(2, 2), &wsadata); | ||
| 202 | if (result != NO_ERROR) { | ||
| 203 | g_warning("WSAStartup() failed\n"); | ||
| 204 | return -1; | ||
| 205 | } | ||
| 206 | #endif | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | gint sock_cleanup(void) | ||
| 211 | { | ||
| 212 | #ifdef G_OS_WIN32 | ||
| 213 | WSACleanup(); | ||
| 214 | #endif | ||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | gint sock_set_io_timeout(guint sec) | ||
| 219 | { | ||
| 220 | io_timeout = sec; | ||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | gint fd_connect_inet(gushort port) | ||
| 225 | { | ||
| 226 | SockDesc sock; | ||
| 227 | struct sockaddr_in addr; | ||
| 228 | |||
| 229 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 230 | if (!SOCKET_IS_VALID(sock)) { | ||
| 231 | #ifdef G_OS_WIN32 | ||
| 232 | g_warning("fd_connect_inet(): socket() failed: %ld\n", | ||
| 233 | WSAGetLastError()); | ||
| 234 | #else | ||
| 235 | perror("fd_connect_inet(): socket"); | ||
| 236 | #endif | ||
| 237 | return -1; | ||
| 238 | } | ||
| 239 | |||
| 240 | memset(&addr, 0, sizeof(addr)); | ||
| 241 | addr.sin_family = AF_INET; | ||
| 242 | addr.sin_port = htons(port); | ||
| 243 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||
| 244 | |||
| 245 | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||
| 246 | fd_close(sock); | ||
| 247 | return -1; | ||
| 248 | } | ||
| 249 | |||
| 250 | return sock; | ||
| 251 | } | ||
| 252 | |||
| 253 | gint fd_open_inet(gushort port) | ||
| 254 | { | ||
| 255 | SockDesc sock; | ||
| 256 | struct sockaddr_in addr; | ||
| 257 | gint val; | ||
| 258 | |||
| 259 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 260 | if (!SOCKET_IS_VALID(sock)) { | ||
| 261 | #ifdef G_OS_WIN32 | ||
| 262 | g_warning("fd_open_inet(): socket() failed: %ld\n", | ||
| 263 | WSAGetLastError()); | ||
| 264 | #else | ||
| 265 | perror("fd_open_inet(): socket"); | ||
| 266 | #endif | ||
| 267 | return -1; | ||
| 268 | } | ||
| 269 | |||
| 270 | val = 1; | ||
| 271 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, | ||
| 272 | sizeof(val)) < 0) { | ||
| 273 | perror("setsockopt"); | ||
| 274 | fd_close(sock); | ||
| 275 | return -1; | ||
| 276 | } | ||
| 277 | |||
| 278 | memset(&addr, 0, sizeof(addr)); | ||
| 279 | addr.sin_family = AF_INET; | ||
| 280 | addr.sin_port = htons(port); | ||
| 281 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | ||
| 282 | |||
| 283 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||
| 284 | perror("bind"); | ||
| 285 | fd_close(sock); | ||
| 286 | return -1; | ||
| 287 | } | ||
| 288 | |||
| 289 | if (listen(sock, 1) < 0) { | ||
| 290 | perror("listen"); | ||
| 291 | fd_close(sock); | ||
| 292 | return -1; | ||
| 293 | } | ||
| 294 | |||
| 295 | return sock; | ||
| 296 | } | ||
| 297 | |||
| 298 | gint fd_connect_unix(const gchar *path) | ||
| 299 | { | ||
| 300 | #ifdef G_OS_UNIX | ||
| 301 | gint sock; | ||
| 302 | struct sockaddr_un addr; | ||
| 303 | |||
| 304 | sock = socket(PF_UNIX, SOCK_STREAM, 0); | ||
| 305 | if (sock < 0) { | ||
| 306 | perror("fd_connect_unix(): socket"); | ||
| 307 | return -1; | ||
| 308 | } | ||
| 309 | |||
| 310 | memset(&addr, 0, sizeof(addr)); | ||
| 311 | addr.sun_family = AF_UNIX; | ||
| 312 | strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); | ||
| 313 | |||
| 314 | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||
| 315 | fd_close(sock); | ||
| 316 | return -1; | ||
| 317 | } | ||
| 318 | |||
| 319 | return sock; | ||
| 320 | #else | ||
| 321 | return -1; | ||
| 322 | #endif | ||
| 323 | } | ||
| 324 | |||
| 325 | gint fd_open_unix(const gchar *path) | ||
| 326 | { | ||
| 327 | #ifdef G_OS_UNIX | ||
| 328 | gint sock; | ||
| 329 | struct sockaddr_un addr; | ||
| 330 | gint val; | ||
| 331 | |||
| 332 | sock = socket(PF_UNIX, SOCK_STREAM, 0); | ||
| 333 | |||
| 334 | if (sock < 0) { | ||
| 335 | perror("sock_open_unix(): socket"); | ||
| 336 | return -1; | ||
| 337 | } | ||
| 338 | |||
| 339 | val = 1; | ||
| 340 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { | ||
| 341 | perror("setsockopt"); | ||
| 342 | fd_close(sock); | ||
| 343 | return -1; | ||
| 344 | } | ||
| 345 | |||
| 346 | memset(&addr, 0, sizeof(addr)); | ||
| 347 | addr.sun_family = AF_UNIX; | ||
| 348 | strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); | ||
| 349 | |||
| 350 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | ||
| 351 | perror("bind"); | ||
| 352 | fd_close(sock); | ||
| 353 | return -1; | ||
| 354 | } | ||
| 355 | |||
| 356 | if (listen(sock, 1) < 0) { | ||
| 357 | perror("listen"); | ||
| 358 | fd_close(sock); | ||
| 359 | return -1; | ||
| 360 | } | ||
| 361 | |||
| 362 | return sock; | ||
| 363 | #else | ||
| 364 | return -1; | ||
| 365 | #endif | ||
| 366 | } | ||
| 367 | |||
| 368 | gint fd_accept(gint sock) | ||
| 369 | { | ||
| 370 | struct sockaddr_in caddr; | ||
| 371 | guint caddr_len; | ||
| 372 | |||
| 373 | caddr_len = sizeof(caddr); | ||
| 374 | return accept(sock, (struct sockaddr *)&caddr, &caddr_len); | ||
| 375 | } | ||
| 376 | |||
| 377 | |||
| 378 | SockInfo *sock_new(const gchar *hostname, gushort port) | ||
| 379 | { | ||
| 380 | SockInfo *sockinfo; | ||
| 381 | |||
| 382 | sockinfo = g_new0(SockInfo, 1); | ||
| 383 | sockinfo->sock = INVALID_SOCKET; | ||
| 384 | sockinfo->sock_ch = NULL; | ||
| 385 | sockinfo->hostname = g_strdup(hostname); | ||
| 386 | sockinfo->port = port; | ||
| 387 | sockinfo->state = CONN_READY; | ||
| 388 | sockinfo->flags = 0; | ||
| 389 | sockinfo->data = NULL; | ||
| 390 | |||
| 391 | return sockinfo; | ||
| 392 | } | ||
| 393 | |||
| 394 | static SockInfo *sock_find_from_fd(gint fd) | ||
| 395 | { | ||
| 396 | GList *cur; | ||
| 397 | |||
| 398 | for (cur = sock_list; cur != NULL; cur = cur->next) { | ||
| 399 | if (((SockInfo *)cur->data)->sock == fd) | ||
| 400 | return (SockInfo *)cur->data; | ||
| 401 | } | ||
| 402 | |||
| 403 | return NULL; | ||
| 404 | } | ||
| 405 | |||
| 406 | static gint set_nonblocking_mode(gint fd, gboolean nonblock) | ||
| 407 | { | ||
| 408 | #ifdef G_OS_WIN32 | ||
| 409 | gulong val = nonblock ? 1 : 0; | ||
| 410 | SockInfo *sock; | ||
| 411 | |||
| 412 | if (!nonblock) | ||
| 413 | WSAEventSelect(fd, NULL, 0); | ||
| 414 | if (ioctlsocket(fd, FIONBIO, &val) == SOCKET_ERROR) { | ||
| 415 | g_warning("set_nonblocking_mode(): ioctlsocket() failed: %ld\n", | ||
| 416 | WSAGetLastError()); | ||
| 417 | return -1; | ||
| 418 | } | ||
| 419 | |||
| 420 | sock = sock_find_from_fd(fd); | ||
| 421 | if (sock) { | ||
| 422 | if (nonblock) { | ||
| 423 | SOCK_SET_FLAGS(sock->flags, SYL_SOCK_NONBLOCK); | ||
| 424 | } else { | ||
| 425 | SOCK_UNSET_FLAGS(sock->flags, SYL_SOCK_NONBLOCK); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | debug_print("set nonblocking mode to %d\n", nonblock); | ||
| 429 | |||
| 430 | return 0; | ||
| 431 | #else | ||
| 432 | gint flags; | ||
| 433 | |||
| 434 | flags = fcntl(fd, F_GETFL, 0); | ||
| 435 | if (flags < 0) { | ||
| 436 | perror("fcntl"); | ||
| 437 | return -1; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (nonblock) | ||
| 441 | flags |= O_NONBLOCK; | ||
| 442 | else | ||
| 443 | flags &= ~O_NONBLOCK; | ||
| 444 | |||
| 445 | return fcntl(fd, F_SETFL, flags); | ||
| 446 | #endif | ||
| 447 | } | ||
| 448 | |||
| 449 | gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock) | ||
| 450 | { | ||
| 451 | gint ret; | ||
| 452 | |||
| 453 | g_return_val_if_fail(sock != NULL, -1); | ||
| 454 | |||
| 455 | ret = set_nonblocking_mode(sock->sock, nonblock); | ||
| 456 | if (ret == 0) { | ||
| 457 | if (nonblock) { | ||
| 458 | SOCK_SET_FLAGS(sock->flags, SYL_SOCK_NONBLOCK); | ||
| 459 | } else { | ||
| 460 | SOCK_UNSET_FLAGS(sock->flags, SYL_SOCK_NONBLOCK); | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | return ret; | ||
| 465 | } | ||
| 466 | |||
| 467 | static gboolean is_nonblocking_mode(gint fd) | ||
| 468 | { | ||
| 469 | #ifdef G_OS_WIN32 | ||
| 470 | SockInfo *sock; | ||
| 471 | |||
| 472 | sock = sock_find_from_fd(fd); | ||
| 473 | if (sock) { | ||
| 474 | return SOCK_IS_NONBLOCK(sock->flags); | ||
| 475 | } | ||
| 476 | |||
| 477 | return FALSE; | ||
| 478 | #else | ||
| 479 | gint flags; | ||
| 480 | |||
| 481 | flags = fcntl(fd, F_GETFL, 0); | ||
| 482 | if (flags < 0) { | ||
| 483 | perror("fcntl"); | ||
| 484 | return FALSE; | ||
| 485 | } | ||
| 486 | |||
| 487 | return ((flags & O_NONBLOCK) != 0); | ||
| 488 | #endif | ||
| 489 | } | ||
| 490 | |||
| 491 | gboolean sock_is_nonblocking_mode(SockInfo *sock) | ||
| 492 | { | ||
| 493 | g_return_val_if_fail(sock != NULL, FALSE); | ||
| 494 | |||
| 495 | #ifdef G_OS_WIN32 | ||
| 496 | return SOCK_IS_NONBLOCK(sock->flags); | ||
| 497 | #else | ||
| 498 | return is_nonblocking_mode(sock->sock); | ||
| 499 | #endif | ||
| 500 | } | ||
| 501 | |||
| 502 | gboolean sock_has_read_data(SockInfo *sock) | ||
| 503 | { | ||
| 504 | #ifdef G_OS_WIN32 | ||
| 505 | gulong val; | ||
| 506 | |||
| 507 | #if USE_SSL | ||
| 508 | if (sock->ssl) | ||
| 509 | return TRUE; | ||
| 510 | #endif | ||
| 511 | if (ioctlsocket(sock->sock, FIONREAD, &val) < 0) { | ||
| 512 | g_warning("sock_has_read_data(): ioctlsocket() failed: %ld\n", | ||
| 513 | WSAGetLastError()); | ||
| 514 | return TRUE; | ||
| 515 | } | ||
| 516 | |||
| 517 | if (val == 0) | ||
| 518 | return FALSE; | ||
| 519 | else | ||
| 520 | return TRUE; | ||
| 521 | #else | ||
| 522 | return TRUE; | ||
| 523 | #endif | ||
| 524 | } | ||
| 525 | |||
| 526 | |||
| 527 | static gboolean sock_prepare(GSource *source, gint *timeout) | ||
| 528 | { | ||
| 529 | *timeout = 1; | ||
| 530 | return FALSE; | ||
| 531 | } | ||
| 532 | |||
| 533 | static gboolean sock_check(GSource *source) | ||
| 534 | { | ||
| 535 | SockInfo *sock = ((SockSource *)source)->sock; | ||
| 536 | struct timeval timeout = {0, 0}; | ||
| 537 | fd_set fds; | ||
| 538 | GIOCondition condition = sock->condition; | ||
| 539 | |||
| 540 | #if USE_SSL | ||
| 541 | if (sock->ssl) { | ||
| 542 | if (condition & G_IO_IN) { | ||
| 543 | if (SSL_pending(sock->ssl) > 0) | ||
| 544 | return TRUE; | ||
| 545 | if (SSL_want_write(sock->ssl)) | ||
| 546 | condition |= G_IO_OUT; | ||
| 547 | } | ||
| 548 | |||
| 549 | if (condition & G_IO_OUT) { | ||
| 550 | if (SSL_want_read(sock->ssl)) | ||
| 551 | condition |= G_IO_IN; | ||
| 552 | } | ||
| 553 | } | ||
| 554 | #endif | ||
| 555 | |||
| 556 | FD_ZERO(&fds); | ||
| 557 | FD_SET(sock->sock, &fds); | ||
| 558 | |||
| 559 | select(sock->sock + 1, | ||
| 560 | (condition & G_IO_IN) ? &fds : NULL, | ||
| 561 | (condition & G_IO_OUT) ? &fds : NULL, | ||
| 562 | NULL, &timeout); | ||
| 563 | |||
| 564 | return FD_ISSET(sock->sock, &fds) != 0; | ||
| 565 | } | ||
| 566 | |||
| 567 | static gboolean sock_dispatch(GSource *source, GSourceFunc callback, | ||
| 568 | gpointer user_data) | ||
| 569 | { | ||
| 570 | SockInfo *sock = ((SockSource *)source)->sock; | ||
| 571 | |||
| 572 | return sock->callback(sock, sock->condition, sock->data); | ||
| 573 | } | ||
| 574 | |||
| 575 | static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition, | ||
| 576 | gpointer data) | ||
| 577 | { | ||
| 578 | SockInfo *sock = (SockInfo *)data; | ||
| 579 | |||
| 580 | if ((condition & sock->condition) == 0) | ||
| 581 | return TRUE; | ||
| 582 | |||
| 583 | return sock->callback(sock, sock->condition, sock->data); | ||
| 584 | } | ||
| 585 | |||
| 586 | guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func, | ||
| 587 | gpointer data) | ||
| 588 | { | ||
| 589 | sock->callback = func; | ||
| 590 | sock->condition = condition; | ||
| 591 | sock->data = data; | ||
| 592 | |||
| 593 | #if USE_SSL | ||
| 594 | if (sock->ssl) { | ||
| 595 | GSource *source; | ||
| 596 | |||
| 597 | source = g_source_new(&sock_watch_funcs, sizeof(SockSource)); | ||
| 598 | ((SockSource *)source)->sock = sock; | ||
| 599 | g_source_set_priority(source, G_PRIORITY_DEFAULT); | ||
| 600 | g_source_set_can_recurse(source, FALSE); | ||
| 601 | return g_source_attach(source, NULL); | ||
| 602 | } | ||
| 603 | #endif | ||
| 604 | |||
| 605 | return g_io_add_watch(sock->sock_ch, condition, sock_watch_cb, sock); | ||
| 606 | } | ||
| 607 | |||
| 608 | guint sock_add_watch_poll(SockInfo *sock, GIOCondition condition, SockFunc func, | ||
| 609 | gpointer data) | ||
| 610 | { | ||
| 611 | GSource *source; | ||
| 612 | |||
| 613 | sock->callback = func; | ||
| 614 | sock->condition = condition; | ||
| 615 | sock->data = data; | ||
| 616 | |||
| 617 | source = g_source_new(&sock_watch_funcs, sizeof(SockSource)); | ||
| 618 | ((SockSource *)source)->sock = sock; | ||
| 619 | g_source_set_priority(source, G_PRIORITY_DEFAULT); | ||
| 620 | g_source_set_can_recurse(source, FALSE); | ||
| 621 | |||
| 622 | return g_source_attach(source, NULL); | ||
| 623 | } | ||
| 624 | |||
| 625 | static gint fd_check_io(gint fd, GIOCondition cond) | ||
| 626 | { | ||
| 627 | struct timeval timeout; | ||
| 628 | fd_set fds; | ||
| 629 | SockInfo *sock; | ||
| 630 | |||
| 631 | sock = sock_find_from_fd(fd); | ||
| 632 | if (sock && !SOCK_IS_CHECK_IO(sock->flags)) | ||
| 633 | return 0; | ||
| 634 | |||
| 635 | timeout.tv_sec = io_timeout; | ||
| 636 | timeout.tv_usec = 0; | ||
| 637 | |||
| 638 | FD_ZERO(&fds); | ||
| 639 | FD_SET(fd, &fds); | ||
| 640 | |||
| 641 | if (cond == G_IO_IN) { | ||
| 642 | select(fd + 1, &fds, NULL, NULL, | ||
| 643 | io_timeout > 0 ? &timeout : NULL); | ||
| 644 | } else { | ||
| 645 | select(fd + 1, NULL, &fds, NULL, | ||
| 646 | io_timeout > 0 ? &timeout : NULL); | ||
| 647 | } | ||
| 648 | |||
| 649 | if (FD_ISSET(fd, &fds)) { | ||
| 650 | return 0; | ||
| 651 | } else { | ||
| 652 | g_warning("Socket IO timeout\n"); | ||
| 653 | return -1; | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | #if defined(G_OS_UNIX) && !defined(USE_THREADS) | ||
| 658 | static sigjmp_buf jmpenv; | ||
| 659 | |||
| 660 | static void timeout_handler(gint sig) | ||
| 661 | { | ||
| 662 | siglongjmp(jmpenv, 1); | ||
| 663 | } | ||
| 664 | #endif | ||
| 665 | |||
| 666 | static gint sock_connect_with_timeout(gint sock, | ||
| 667 | const struct sockaddr *serv_addr, | ||
| 668 | gint addrlen, | ||
| 669 | guint timeout_secs) | ||
| 670 | { | ||
| 671 | gint ret; | ||
| 672 | |||
| 673 | #ifdef G_OS_WIN32 | ||
| 674 | WSAEVENT hevent; | ||
| 675 | gint err; | ||
| 676 | DWORD dwret; | ||
| 677 | WSANETWORKEVENTS events; | ||
| 678 | |||
| 679 | errno = 0; | ||
| 680 | |||
| 681 | hevent = WSACreateEvent(); | ||
| 682 | if (hevent == WSA_INVALID_EVENT) | ||
| 683 | return -1; | ||
| 684 | |||
| 685 | ret = WSAEventSelect(sock, hevent, FD_CONNECT); | ||
| 686 | if (ret == SOCKET_ERROR) { | ||
| 687 | g_warning("sock_connect_with_timeout: WSAEventSelect"); | ||
| 688 | WSACloseEvent(hevent); | ||
| 689 | return -1; | ||
| 690 | } | ||
| 691 | |||
| 692 | ret = connect(sock, serv_addr, addrlen); | ||
| 693 | |||
| 694 | if (ret == SOCKET_ERROR) { | ||
| 695 | err = WSAGetLastError(); | ||
| 696 | if (err != WSAEWOULDBLOCK) { | ||
| 697 | g_warning("sock_connect_with_timeout: connect (%d)", err); | ||
| 698 | ret = -1; | ||
| 699 | goto end; | ||
| 700 | } | ||
| 701 | } | ||
| 702 | |||
| 703 | dwret = WSAWaitForMultipleEvents(1, &hevent, FALSE, timeout_secs * 1000, FALSE); | ||
| 704 | if (dwret == WSA_WAIT_TIMEOUT) { | ||
| 705 | g_warning("sock_connect_with_timeout: timeout"); | ||
| 706 | errno = WSAETIMEDOUT; | ||
| 707 | ret = -1; | ||
| 708 | goto end; | ||
| 709 | } else if (dwret != WSA_WAIT_EVENT_0) { | ||
| 710 | g_warning("sock_connect_with_timeout: WSAWaitForMultipleEvents (%d)", dwret); | ||
| 711 | ret = -1; | ||
| 712 | goto end; | ||
| 713 | } | ||
| 714 | |||
| 715 | ret = WSAEnumNetworkEvents(sock, hevent, &events); | ||
| 716 | if (ret == SOCKET_ERROR) { | ||
| 717 | g_warning("sock_connect_with_timeout: WSAEnumNetworkEvents (%d)", ret); | ||
| 718 | ret = -1; | ||
| 719 | goto end; | ||
| 720 | } | ||
| 721 | |||
| 722 | if ((events.lNetworkEvents & FD_CONNECT) && | ||
| 723 | events.iErrorCode[FD_CONNECT_BIT] == 0) { | ||
| 724 | ret = 0; | ||
| 725 | errno = 0; | ||
| 726 | } else | ||
| 727 | ret = -1; | ||
| 728 | |||
| 729 | end: | ||
| 730 | WSAEventSelect(sock, NULL, 0); | ||
| 731 | WSACloseEvent(hevent); | ||
| 732 | |||
| 733 | set_nonblocking_mode(sock, FALSE); | ||
| 734 | #else | ||
| 735 | set_nonblocking_mode(sock, TRUE); | ||
| 736 | |||
| 737 | ret = connect(sock, serv_addr, addrlen); | ||
| 738 | |||
| 739 | if (ret < 0) { | ||
| 740 | if (EINPROGRESS == errno) { | ||
| 741 | fd_set fds; | ||
| 742 | struct timeval tv; | ||
| 743 | |||
| 744 | tv.tv_sec = timeout_secs; | ||
| 745 | tv.tv_usec = 0; | ||
| 746 | FD_ZERO(&fds); | ||
| 747 | FD_SET(sock, &fds); | ||
| 748 | do { | ||
| 749 | ret = select(sock + 1, NULL, &fds, NULL, &tv); | ||
| 750 | } while (ret < 0 && EINTR == errno); | ||
| 751 | if (ret < 0) { | ||
| 752 | perror("sock_connect_with_timeout: select"); | ||
| 753 | return -1; | ||
| 754 | } else if (ret == 0) { | ||
| 755 | debug_print("sock_connect_with_timeout: timeout\n"); | ||
| 756 | errno = ETIMEDOUT; | ||
| 757 | return -1; | ||
| 758 | } else { | ||
| 759 | gint val; | ||
| 760 | guint len; | ||
| 761 | |||
| 762 | if (FD_ISSET(sock, &fds)) { | ||
| 763 | ret = 0; | ||
| 764 | } else { | ||
| 765 | debug_print("sock_connect_with_timeout: fd not set\n"); | ||
| 766 | return -1; | ||
| 767 | } | ||
| 768 | |||
| 769 | len = sizeof(val); | ||
| 770 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len) < 0) { | ||
| 771 | perror("sock_connect_with_timeout: getsockopt"); | ||
| 772 | return -1; | ||
| 773 | } | ||
| 774 | if (val != 0) { | ||
| 775 | debug_print("sock_connect_with_timeout: getsockopt(SOL_SOCKET, SO_ERROR) returned error: %s\n", g_strerror(val)); | ||
| 776 | return -1; | ||
| 777 | } | ||
| 778 | } | ||
| 779 | } else { | ||
| 780 | perror("sock_connect_with_timeout: connect"); | ||
| 781 | return -1; | ||
| 782 | } | ||
| 783 | } | ||
| 784 | |||
| 785 | set_nonblocking_mode(sock, FALSE); | ||
| 786 | #endif | ||
| 787 | |||
| 788 | return ret; | ||
| 789 | } | ||
| 790 | |||
| 791 | static void resolver_init(void) | ||
| 792 | { | ||
| 793 | #ifdef G_OS_UNIX | ||
| 794 | static time_t resolv_conf_mtime = 0; | ||
| 795 | GStatBuf s; | ||
| 796 | |||
| 797 | if (g_stat("/etc/resolv.conf", &s) == 0 && | ||
| 798 | s.st_mtime != resolv_conf_mtime) { | ||
| 799 | debug_print("Reloading /etc/resolv.conf\n"); | ||
| 800 | resolv_conf_mtime = s.st_mtime; | ||
| 801 | res_init(); | ||
| 802 | } | ||
| 803 | #endif | ||
| 804 | } | ||
| 805 | |||
| 806 | struct hostent *my_gethostbyname(const gchar *hostname) | ||
| 807 | { | ||
| 808 | struct hostent *hp; | ||
| 809 | #if defined(G_OS_UNIX) && !defined(USE_THREADS) | ||
| 810 | void (*prev_handler)(gint); | ||
| 811 | |||
| 812 | alarm(0); | ||
| 813 | prev_handler = signal(SIGALRM, timeout_handler); | ||
| 814 | if (sigsetjmp(jmpenv, 1)) { | ||
| 815 | alarm(0); | ||
| 816 | signal(SIGALRM, prev_handler); | ||
| 817 | fprintf(stderr, "%s: host lookup timed out.\n", hostname); | ||
| 818 | errno = 0; | ||
| 819 | return NULL; | ||
| 820 | } | ||
| 821 | alarm(io_timeout); | ||
| 822 | #endif | ||
| 823 | |||
| 824 | if ((hp = gethostbyname(hostname)) == NULL) { | ||
| 825 | #if defined(G_OS_UNIX) && !defined(USE_THREADS) | ||
| 826 | alarm(0); | ||
| 827 | signal(SIGALRM, prev_handler); | ||
| 828 | #endif | ||
| 829 | fprintf(stderr, "%s: unknown host.\n", hostname); | ||
| 830 | errno = 0; | ||
| 831 | return NULL; | ||
| 832 | } | ||
| 833 | |||
| 834 | #if defined(G_OS_UNIX) && !defined(USE_THREADS) | ||
| 835 | alarm(0); | ||
| 836 | signal(SIGALRM, prev_handler); | ||
| 837 | #endif | ||
| 838 | |||
| 839 | return hp; | ||
| 840 | } | ||
| 841 | |||
| 842 | static void sock_set_buffer_size(gint sock) | ||
| 843 | { | ||
| 844 | #ifdef G_OS_WIN32 | ||
| 845 | gint val; | ||
| 846 | guint len = sizeof(val); | ||
| 847 | |||
| 848 | #define SOCK_BUFFSIZE 32768 | ||
| 849 | |||
| 850 | getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&val, &len); | ||
| 851 | if (val < SOCK_BUFFSIZE) { | ||
| 852 | val = SOCK_BUFFSIZE; | ||
| 853 | setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&val, len); | ||
| 854 | } | ||
| 855 | getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&val, &len); | ||
| 856 | if (val < SOCK_BUFFSIZE) { | ||
| 857 | val = SOCK_BUFFSIZE; | ||
| 858 | setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&val, len); | ||
| 859 | } | ||
| 860 | getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&val, &len); | ||
| 861 | debug_print("SO_SNDBUF = %d\n", val); | ||
| 862 | getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&val, &len); | ||
| 863 | debug_print("SO_RCVBUF = %d\n", val); | ||
| 864 | |||
| 865 | #undef SOCK_BUFFSIZE | ||
| 866 | #endif | ||
| 867 | } | ||
| 868 | |||
| 869 | #if !defined(INET6) || defined(G_OS_WIN32) | ||
| 870 | static gint my_inet_aton(const gchar *hostname, struct in_addr *inp) | ||
| 871 | { | ||
| 872 | #if HAVE_INET_ATON | ||
| 873 | return inet_aton(hostname, inp); | ||
| 874 | #else | ||
| 875 | #if HAVE_INET_ADDR | ||
| 876 | guint32 inaddr; | ||
| 877 | |||
| 878 | inaddr = inet_addr(hostname); | ||
| 879 | if (inaddr != -1) { | ||
| 880 | memcpy(inp, &inaddr, sizeof(inaddr)); | ||
| 881 | return 1; | ||
| 882 | } else | ||
| 883 | return 0; | ||
| 884 | #else | ||
| 885 | return 0; | ||
| 886 | #endif | ||
| 887 | #endif /* HAVE_INET_ATON */ | ||
| 888 | } | ||
| 889 | #endif /* !defined(INET6) || defined(G_OS_WIN32) */ | ||
| 890 | |||
| 891 | #ifndef INET6 | ||
| 892 | static gint sock_info_connect_by_hostname(SockInfo *sock) | ||
| 893 | { | ||
| 894 | struct hostent *hp; | ||
| 895 | struct sockaddr_in ad; | ||
| 896 | gint ret; | ||
| 897 | |||
| 898 | g_return_val_if_fail(sock != NULL, -1); | ||
| 899 | g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1); | ||
| 900 | |||
| 901 | resolver_init(); | ||
| 902 | |||
| 903 | memset(&ad, 0, sizeof(ad)); | ||
| 904 | ad.sin_family = AF_INET; | ||
| 905 | ad.sin_port = htons(sock->port); | ||
| 906 | |||
| 907 | if (!my_inet_aton(sock->hostname, &ad.sin_addr)) { | ||
| 908 | if ((hp = my_gethostbyname(sock->hostname)) == NULL) { | ||
| 909 | fprintf(stderr, "%s: unknown host.\n", sock->hostname); | ||
| 910 | errno = 0; | ||
| 911 | sock->state = CONN_LOOKUPFAILED; | ||
| 912 | return -1; | ||
| 913 | } | ||
| 914 | |||
| 915 | if (hp->h_length != 4 && hp->h_length != 8) { | ||
| 916 | fprintf(stderr, "illegal address length received for host %s\n", sock->hostname); | ||
| 917 | errno = 0; | ||
| 918 | sock->state = CONN_LOOKUPFAILED; | ||
| 919 | return -1; | ||
| 920 | } | ||
| 921 | |||
| 922 | memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); | ||
| 923 | } | ||
| 924 | |||
| 925 | sock->state = CONN_LOOKUPSUCCESS; | ||
| 926 | |||
| 927 | ret = sock_connect_with_timeout(sock->sock, (struct sockaddr *)&ad, | ||
| 928 | sizeof(ad), io_timeout); | ||
| 929 | if (ret < 0) | ||
| 930 | sock->state = CONN_FAILED; | ||
| 931 | else | ||
| 932 | sock->state = CONN_ESTABLISHED; | ||
| 933 | |||
| 934 | return ret; | ||
| 935 | } | ||
| 936 | |||
| 937 | #else /* INET6 */ | ||
| 938 | |||
| 939 | #ifdef G_OS_WIN32 | ||
| 940 | static gboolean win32_ipv6_supported(void) | ||
| 941 | { | ||
| 942 | static gboolean ipv6_checked = FALSE; | ||
| 943 | HMODULE hmodule; | ||
| 944 | |||
| 945 | if (ipv6_checked) | ||
| 946 | return getaddrinfo_func != NULL; | ||
| 947 | |||
| 948 | hmodule = GetModuleHandleA("ws2_32"); | ||
| 949 | if (hmodule) { | ||
| 950 | getaddrinfo_func = | ||
| 951 | (GetAddrInfoFunc)GetProcAddress(hmodule, "getaddrinfo"); | ||
| 952 | freeaddrinfo_func = | ||
| 953 | (FreeAddrInfoFunc)GetProcAddress(hmodule, "freeaddrinfo"); | ||
| 954 | if (!getaddrinfo_func || !freeaddrinfo_func) { | ||
| 955 | getaddrinfo_func = NULL; | ||
| 956 | freeaddrinfo_func = NULL; | ||
| 957 | } | ||
| 958 | } | ||
| 959 | |||
| 960 | if (getaddrinfo_func) | ||
| 961 | debug_print("ws2_32 has IPv6 functions.\n"); | ||
| 962 | else | ||
| 963 | debug_print("ws2_32 does not have IPv6 functions.\n"); | ||
| 964 | |||
| 965 | ipv6_checked = TRUE; | ||
| 966 | return getaddrinfo_func != NULL; | ||
| 967 | } | ||
| 968 | |||
| 969 | /* subset of getaddrinfo() */ | ||
| 970 | static int my_getaddrinfo(const char *node, const char *service, | ||
| 971 | const struct addrinfo *hintp, | ||
| 972 | struct addrinfo **res) | ||
| 973 | { | ||
| 974 | struct addrinfo *ai; | ||
| 975 | struct sockaddr_in addr, *paddr; | ||
| 976 | struct addrinfo hints; | ||
| 977 | gint port = 0; | ||
| 978 | |||
| 979 | if (win32_ipv6_supported()) | ||
| 980 | return getaddrinfo_func(node, service, hintp, res); | ||
| 981 | |||
| 982 | if (!hintp) { | ||
| 983 | memset(&hints, 0, sizeof(hints)); | ||
| 984 | hints.ai_family = AF_INET; | ||
| 985 | hints.ai_socktype = SOCK_STREAM; | ||
| 986 | } else | ||
| 987 | memcpy(&hints, hintp, sizeof(hints)); | ||
| 988 | |||
| 989 | if (hints.ai_family != AF_UNSPEC && hints.ai_family != AF_INET) | ||
| 990 | return EAI_FAMILY; | ||
| 991 | if (hints.ai_socktype == 0) | ||
| 992 | hints.ai_socktype = SOCK_STREAM; | ||
| 993 | if (hints.ai_protocol == 0) | ||
| 994 | hints.ai_protocol = IPPROTO_TCP; | ||
| 995 | if (hints.ai_socktype != SOCK_STREAM) | ||
| 996 | return EAI_SOCKTYPE; | ||
| 997 | if (hints.ai_protocol != IPPROTO_TCP) | ||
| 998 | return EAI_SOCKTYPE; | ||
| 999 | #if 0 | ||
| 1000 | if (!node && !service) | ||
| 1001 | return EAI_NONAME; | ||
| 1002 | #endif | ||
| 1003 | if (!node || !service) | ||
| 1004 | return EAI_NONAME; | ||
| 1005 | |||
| 1006 | port = atoi(service); | ||
| 1007 | |||
| 1008 | memset(&addr, 0, sizeof(addr)); | ||
| 1009 | addr.sin_family = AF_INET; | ||
| 1010 | addr.sin_port = htons(port); | ||
| 1011 | |||
| 1012 | if (!my_inet_aton(node, &addr.sin_addr)) { | ||
| 1013 | struct hostent *hp; | ||
| 1014 | |||
| 1015 | if ((hp = my_gethostbyname(node)) == NULL) { | ||
| 1016 | fprintf(stderr, "%s: unknown host.\n", node); | ||
| 1017 | errno = 0; | ||
| 1018 | return EAI_NONAME; | ||
| 1019 | } | ||
| 1020 | if (hp->h_length != 4 && hp->h_length != 8) { | ||
| 1021 | fprintf(stderr, "illegal address length received for host %s\n", node); | ||
| 1022 | errno = 0; | ||
| 1023 | return EAI_FAIL; | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | ai = g_malloc0(sizeof(struct addrinfo)); | ||
| 1030 | paddr = g_malloc(sizeof(struct sockaddr_in)); | ||
| 1031 | memcpy(paddr, &addr, sizeof(struct sockaddr_in)); | ||
| 1032 | |||
| 1033 | ai->ai_flags = 0; | ||
| 1034 | ai->ai_family = AF_INET; | ||
| 1035 | ai->ai_socktype = hints.ai_socktype; | ||
| 1036 | ai->ai_protocol = hints.ai_protocol; | ||
| 1037 | ai->ai_addrlen = sizeof(struct sockaddr_in); | ||
| 1038 | ai->ai_addr = (struct sockaddr *)paddr; | ||
| 1039 | ai->ai_canonname = NULL; | ||
| 1040 | ai->ai_next = NULL; | ||
| 1041 | |||
| 1042 | *res = ai; | ||
| 1043 | |||
| 1044 | return 0; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | static void my_freeaddrinfo(struct addrinfo *res) | ||
| 1048 | { | ||
| 1049 | if (win32_ipv6_supported()) { | ||
| 1050 | freeaddrinfo_func(res); | ||
| 1051 | return; | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | if (res) { | ||
| 1055 | g_free(res->ai_addr); | ||
| 1056 | g_free(res); | ||
| 1057 | } | ||
| 1058 | } | ||
| 1059 | #endif | ||
| 1060 | |||
| 1061 | static SockDesc sock_info_connect_by_getaddrinfo(SockInfo *sockinfo) | ||
| 1062 | { | ||
| 1063 | SockDesc sock = INVALID_SOCKET; | ||
| 1064 | gint gai_error; | ||
| 1065 | struct addrinfo hints, *res, *ai; | ||
| 1066 | gchar port_str[6]; | ||
| 1067 | |||
| 1068 | g_return_val_if_fail(sockinfo != NULL, INVALID_SOCKET); | ||
| 1069 | g_return_val_if_fail(sockinfo->hostname != NULL && sockinfo->port > 0, INVALID_SOCKET); | ||
| 1070 | |||
| 1071 | resolver_init(); | ||
| 1072 | |||
| 1073 | memset(&hints, 0, sizeof(hints)); | ||
| 1074 | /* hints.ai_flags = AI_CANONNAME; */ | ||
| 1075 | hints.ai_family = AF_UNSPEC; | ||
| 1076 | hints.ai_socktype = SOCK_STREAM; | ||
| 1077 | hints.ai_protocol = IPPROTO_TCP; | ||
| 1078 | |||
| 1079 | /* convert port from integer to string. */ | ||
| 1080 | g_snprintf(port_str, sizeof(port_str), "%d", sockinfo->port); | ||
| 1081 | |||
| 1082 | if ((gai_error = getaddrinfo(sockinfo->hostname, port_str, &hints, &res)) != 0) { | ||
| 1083 | #ifdef G_OS_WIN32 | ||
| 1084 | fprintf(stderr, "getaddrinfo for %s:%s failed: errno: %d\n", | ||
| 1085 | sockinfo->hostname, port_str, gai_error); | ||
| 1086 | #else | ||
| 1087 | fprintf(stderr, "getaddrinfo for %s:%s failed: %s\n", | ||
| 1088 | sockinfo->hostname, port_str, gai_strerror(gai_error)); | ||
| 1089 | #endif | ||
| 1090 | debug_print("getaddrinfo failed\n"); | ||
| 1091 | sockinfo->state = CONN_LOOKUPFAILED; | ||
| 1092 | return INVALID_SOCKET; | ||
| 1093 | } | ||
| 1094 | |||
| 1095 | for (ai = res; ai != NULL; ai = ai->ai_next) { | ||
| 1096 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | ||
| 1097 | if (!SOCKET_IS_VALID(sock)) | ||
| 1098 | continue; | ||
| 1099 | sock_set_buffer_size(sock); | ||
| 1100 | |||
| 1101 | if (sock_connect_with_timeout | ||
| 1102 | (sock, ai->ai_addr, ai->ai_addrlen, io_timeout) == 0) | ||
| 1103 | break; | ||
| 1104 | |||
| 1105 | fd_close(sock); | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | if (res != NULL) | ||
| 1109 | freeaddrinfo(res); | ||
| 1110 | |||
| 1111 | if (ai == NULL) { | ||
| 1112 | sockinfo->state = CONN_FAILED; | ||
| 1113 | return INVALID_SOCKET; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | sockinfo->state = CONN_ESTABLISHED; | ||
| 1117 | return sock; | ||
| 1118 | } | ||
| 1119 | #endif /* !INET6 */ | ||
| 1120 | |||
| 1121 | SockInfo *sock_connect(const gchar *hostname, gushort port) | ||
| 1122 | { | ||
| 1123 | SockInfo *sockinfo; | ||
| 1124 | |||
| 1125 | sockinfo = sock_new(hostname, port); | ||
| 1126 | if (sock_info_connect(sockinfo) < 0) { | ||
| 1127 | sock_close(sockinfo); | ||
| 1128 | return NULL; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | return sockinfo; | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | gint sock_info_connect(SockInfo *sockinfo) | ||
| 1135 | { | ||
| 1136 | SockDesc sock; | ||
| 1137 | #ifndef INET6 | ||
| 1138 | gint ret; | ||
| 1139 | #endif | ||
| 1140 | |||
| 1141 | g_return_val_if_fail(sockinfo != NULL, -1); | ||
| 1142 | g_return_val_if_fail(sockinfo->hostname != NULL && sockinfo->port > 0, | ||
| 1143 | -1); | ||
| 1144 | |||
| 1145 | #ifdef INET6 | ||
| 1146 | sock = sock_info_connect_by_getaddrinfo(sockinfo); | ||
| 1147 | if (!SOCKET_IS_VALID(sock)) { | ||
| 1148 | return -1; | ||
| 1149 | } | ||
| 1150 | #else | ||
| 1151 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
| 1152 | if (!SOCKET_IS_VALID(sock)) { | ||
| 1153 | #ifdef G_OS_WIN32 | ||
| 1154 | g_warning("socket() failed: %ld\n", WSAGetLastError()); | ||
| 1155 | #else | ||
| 1156 | perror("socket"); | ||
| 1157 | #endif /* G_OS_WIN32 */ | ||
| 1158 | sockinfo->state = CONN_FAILED; | ||
| 1159 | return -1; | ||
| 1160 | } | ||
| 1161 | sock_set_buffer_size(sock); | ||
| 1162 | |||
| 1163 | sockinfo->sock = sock; | ||
| 1164 | if ((ret = sock_info_connect_by_hostname(sockinfo)) < 0) { | ||
| 1165 | if (errno != 0) perror("connect"); | ||
| 1166 | fd_close(sock); | ||
| 1167 | sockinfo->sock = INVALID_SOCKET; | ||
| 1168 | return ret; | ||
| 1169 | } | ||
| 1170 | #endif /* INET6 */ | ||
| 1171 | |||
| 1172 | sockinfo->sock = sock; | ||
| 1173 | sockinfo->sock_ch = g_io_channel_unix_new(sock); | ||
| 1174 | sockinfo->flags = SYL_SOCK_CHECK_IO; | ||
| 1175 | |||
| 1176 | sock_list = g_list_prepend(sock_list, sockinfo); | ||
| 1177 | |||
| 1178 | g_usleep(100000); | ||
| 1179 | |||
| 1180 | return 0; | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | #ifdef G_OS_UNIX | ||
| 1184 | static void sock_address_list_free(GList *addr_list) | ||
| 1185 | { | ||
| 1186 | GList *cur; | ||
| 1187 | |||
| 1188 | for (cur = addr_list; cur != NULL; cur = cur->next) { | ||
| 1189 | SockAddrData *addr_data = (SockAddrData *)cur->data; | ||
| 1190 | g_free(addr_data->addr); | ||
| 1191 | g_free(addr_data); | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | g_list_free(addr_list); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /* asynchronous TCP connection */ | ||
| 1198 | |||
| 1199 | static gboolean sock_connect_async_cb(GIOChannel *source, | ||
| 1200 | GIOCondition condition, gpointer data) | ||
| 1201 | { | ||
| 1202 | SockConnectData *conn_data = (SockConnectData *)data; | ||
| 1203 | gint fd; | ||
| 1204 | gint val; | ||
| 1205 | guint len; | ||
| 1206 | SockInfo *sockinfo; | ||
| 1207 | |||
| 1208 | fd = g_io_channel_unix_get_fd(source); | ||
| 1209 | |||
| 1210 | conn_data->io_tag = 0; | ||
| 1211 | conn_data->channel = NULL; | ||
| 1212 | g_io_channel_unref(source); | ||
| 1213 | |||
| 1214 | if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { | ||
| 1215 | debug_print("sock_connect_async_cb: condition = %d\n", | ||
| 1216 | condition); | ||
| 1217 | fd_close(fd); | ||
| 1218 | sock_connect_address_list_async(conn_data); | ||
| 1219 | return FALSE; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | len = sizeof(val); | ||
| 1223 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) { | ||
| 1224 | perror("getsockopt"); | ||
| 1225 | fd_close(fd); | ||
| 1226 | sock_connect_address_list_async(conn_data); | ||
| 1227 | return FALSE; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | if (val != 0) { | ||
| 1231 | debug_print("getsockopt(SOL_SOCKET, SO_ERROR) returned error\n"); | ||
| 1232 | fd_close(fd); | ||
| 1233 | sock_connect_address_list_async(conn_data); | ||
| 1234 | return FALSE; | ||
| 1235 | } | ||
| 1236 | |||
| 1237 | sockinfo = conn_data->sock; | ||
| 1238 | sockinfo->sock = fd; | ||
| 1239 | sockinfo->sock_ch = g_io_channel_unix_new(fd); | ||
| 1240 | sockinfo->state = CONN_ESTABLISHED; | ||
| 1241 | sockinfo->flags = SYL_SOCK_NONBLOCK; | ||
| 1242 | |||
| 1243 | sock_list = g_list_prepend(sock_list, sockinfo); | ||
| 1244 | |||
| 1245 | conn_data->func(sockinfo, conn_data->data); | ||
| 1246 | |||
| 1247 | conn_data->sock = NULL; | ||
| 1248 | sock_connect_async_cancel(conn_data->id); | ||
| 1249 | |||
| 1250 | return FALSE; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | static gint sock_connect_async_get_address_info_cb(GList *addr_list, | ||
| 1254 | gpointer data) | ||
| 1255 | { | ||
| 1256 | SockConnectData *conn_data = (SockConnectData *)data; | ||
| 1257 | |||
| 1258 | conn_data->addr_list = addr_list; | ||
| 1259 | conn_data->cur_addr = addr_list; | ||
| 1260 | conn_data->lookup_data = NULL; | ||
| 1261 | |||
| 1262 | return sock_connect_address_list_async(conn_data); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | gint sock_connect_async(const gchar *hostname, gushort port, | ||
| 1266 | SockConnectFunc func, gpointer data) | ||
| 1267 | { | ||
| 1268 | SockInfo *sock; | ||
| 1269 | gint ret; | ||
| 1270 | |||
| 1271 | sock = sock_new(hostname, port); | ||
| 1272 | ret = sock_info_connect_async(sock, func, data); | ||
| 1273 | if (ret < 0) | ||
| 1274 | sock_close(sock); | ||
| 1275 | |||
| 1276 | return ret; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | gint sock_info_connect_async(SockInfo *sock, SockConnectFunc func, | ||
| 1280 | gpointer data) | ||
| 1281 | { | ||
| 1282 | static gint id = 1; | ||
| 1283 | SockConnectData *conn_data; | ||
| 1284 | |||
| 1285 | g_return_val_if_fail(sock != NULL, -1); | ||
| 1286 | g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1); | ||
| 1287 | |||
| 1288 | conn_data = g_new0(SockConnectData, 1); | ||
| 1289 | conn_data->id = id++; | ||
| 1290 | conn_data->hostname = g_strdup(sock->hostname); | ||
| 1291 | conn_data->port = sock->port; | ||
| 1292 | conn_data->addr_list = NULL; | ||
| 1293 | conn_data->cur_addr = NULL; | ||
| 1294 | conn_data->io_tag = 0; | ||
| 1295 | conn_data->sock = sock; | ||
| 1296 | conn_data->func = func; | ||
| 1297 | conn_data->data = data; | ||
| 1298 | |||
| 1299 | conn_data->lookup_data = sock_get_address_info_async | ||
| 1300 | (sock->hostname, sock->port, | ||
| 1301 | sock_connect_async_get_address_info_cb, conn_data); | ||
| 1302 | |||
| 1303 | if (conn_data->lookup_data == NULL) { | ||
| 1304 | g_free(conn_data->hostname); | ||
| 1305 | g_free(conn_data); | ||
| 1306 | return -1; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | sock_connect_data_list = g_list_append(sock_connect_data_list, | ||
| 1310 | conn_data); | ||
| 1311 | |||
| 1312 | return conn_data->id; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | gint sock_connect_async_cancel(gint id) | ||
| 1316 | { | ||
| 1317 | SockConnectData *conn_data = NULL; | ||
| 1318 | GList *cur; | ||
| 1319 | |||
| 1320 | for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) { | ||
| 1321 | if (((SockConnectData *)cur->data)->id == id) { | ||
| 1322 | conn_data = (SockConnectData *)cur->data; | ||
| 1323 | break; | ||
| 1324 | } | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | if (conn_data) { | ||
| 1328 | sock_connect_data_list = g_list_remove(sock_connect_data_list, | ||
| 1329 | conn_data); | ||
| 1330 | |||
| 1331 | if (conn_data->lookup_data) | ||
| 1332 | sock_get_address_info_async_cancel | ||
| 1333 | (conn_data->lookup_data); | ||
| 1334 | |||
| 1335 | if (conn_data->io_tag > 0) | ||
| 1336 | g_source_remove(conn_data->io_tag); | ||
| 1337 | if (conn_data->channel) { | ||
| 1338 | g_io_channel_shutdown(conn_data->channel, FALSE, NULL); | ||
| 1339 | g_io_channel_unref(conn_data->channel); | ||
| 1340 | } | ||
| 1341 | if (conn_data->sock) | ||
| 1342 | sock_close(conn_data->sock); | ||
| 1343 | |||
| 1344 | sock_address_list_free(conn_data->addr_list); | ||
| 1345 | g_free(conn_data->hostname); | ||
| 1346 | g_free(conn_data); | ||
| 1347 | } else { | ||
| 1348 | g_warning("sock_connect_async_cancel: id %d not found.\n", id); | ||
| 1349 | return -1; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | return 0; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | static gint sock_connect_address_list_async(SockConnectData *conn_data) | ||
| 1356 | { | ||
| 1357 | SockAddrData *addr_data; | ||
| 1358 | gint sock = -1; | ||
| 1359 | |||
| 1360 | if (conn_data->addr_list == NULL) { | ||
| 1361 | g_warning("sock_connect_address_list_async: " | ||
| 1362 | "DNS lookup for %s failed", conn_data->hostname); | ||
| 1363 | conn_data->sock->state = CONN_LOOKUPFAILED; | ||
| 1364 | conn_data->func(conn_data->sock, conn_data->data); | ||
| 1365 | sock_connect_async_cancel(conn_data->id); | ||
| 1366 | return -1; | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | for (; conn_data->cur_addr != NULL; | ||
| 1370 | conn_data->cur_addr = conn_data->cur_addr->next) { | ||
| 1371 | addr_data = (SockAddrData *)conn_data->cur_addr->data; | ||
| 1372 | |||
| 1373 | if ((sock = socket(addr_data->family, addr_data->socktype, | ||
| 1374 | addr_data->protocol)) < 0) { | ||
| 1375 | perror("socket"); | ||
| 1376 | continue; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | sock_set_buffer_size(sock); | ||
| 1380 | set_nonblocking_mode(sock, TRUE); | ||
| 1381 | |||
| 1382 | if (connect(sock, addr_data->addr, addr_data->addr_len) < 0) { | ||
| 1383 | if (EINPROGRESS == errno) { | ||
| 1384 | break; | ||
| 1385 | } else { | ||
| 1386 | perror("connect"); | ||
| 1387 | fd_close(sock); | ||
| 1388 | } | ||
| 1389 | } else | ||
| 1390 | break; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | if (conn_data->cur_addr == NULL) { | ||
| 1394 | g_warning("sock_connect_address_list_async: " | ||
| 1395 | "connection to %s:%d failed", | ||
| 1396 | conn_data->hostname, conn_data->port); | ||
| 1397 | conn_data->sock->state = CONN_FAILED; | ||
| 1398 | conn_data->func(conn_data->sock, conn_data->data); | ||
| 1399 | sock_connect_async_cancel(conn_data->id); | ||
| 1400 | return -1; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | debug_print("sock_connect_address_list_async: waiting for connect\n"); | ||
| 1404 | |||
| 1405 | conn_data->cur_addr = conn_data->cur_addr->next; | ||
| 1406 | |||
| 1407 | conn_data->channel = g_io_channel_unix_new(sock); | ||
| 1408 | conn_data->io_tag = g_io_add_watch | ||
| 1409 | (conn_data->channel, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, | ||
| 1410 | sock_connect_async_cb, conn_data); | ||
| 1411 | |||
| 1412 | return 0; | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | static gint sock_kill_process(pid_t pid) | ||
| 1416 | { | ||
| 1417 | pid_t ret = (pid_t)-1; | ||
| 1418 | |||
| 1419 | kill(pid, SIGKILL); | ||
| 1420 | |||
| 1421 | while (ret == (pid_t)-1) { | ||
| 1422 | if ((ret = waitpid(pid, NULL, 0)) != pid) { | ||
| 1423 | if (ret == (pid_t)-1 && errno != EINTR) { | ||
| 1424 | perror("sock_kill_process(): waitpid"); | ||
| 1425 | break; | ||
| 1426 | } | ||
| 1427 | } | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | return (gint)pid; | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | /* asynchronous DNS lookup */ | ||
| 1434 | |||
| 1435 | static gboolean sock_get_address_info_async_cb(GIOChannel *source, | ||
| 1436 | GIOCondition condition, | ||
| 1437 | gpointer data) | ||
| 1438 | { | ||
| 1439 | SockLookupData *lookup_data = (SockLookupData *)data; | ||
| 1440 | GList *addr_list = NULL; | ||
| 1441 | SockAddrData *addr_data; | ||
| 1442 | gsize bytes_read; | ||
| 1443 | gint ai_member[4]; | ||
| 1444 | struct sockaddr *addr; | ||
| 1445 | |||
| 1446 | for (;;) { | ||
| 1447 | if (g_io_channel_read(source, (gchar *)ai_member, | ||
| 1448 | sizeof(ai_member), &bytes_read) | ||
| 1449 | != G_IO_ERROR_NONE) { | ||
| 1450 | g_warning("sock_get_address_info_async_cb: " | ||
| 1451 | "address length read error\n"); | ||
| 1452 | break; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | if (bytes_read == 0 || bytes_read != sizeof(ai_member)) | ||
| 1456 | break; | ||
| 1457 | |||
| 1458 | if (ai_member[0] == AF_UNSPEC) { | ||
| 1459 | g_warning("DNS lookup failed"); | ||
| 1460 | break; | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | addr = g_malloc(ai_member[3]); | ||
| 1464 | if (g_io_channel_read(source, (gchar *)addr, ai_member[3], | ||
| 1465 | &bytes_read) | ||
| 1466 | != G_IO_ERROR_NONE) { | ||
| 1467 | g_warning("sock_get_address_info_async_cb: " | ||
| 1468 | "address data read error\n"); | ||
| 1469 | g_free(addr); | ||
| 1470 | break; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | if (bytes_read != ai_member[3]) { | ||
| 1474 | g_warning("sock_get_address_info_async_cb: " | ||
| 1475 | "incomplete address data\n"); | ||
| 1476 | g_free(addr); | ||
| 1477 | break; | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | addr_data = g_new0(SockAddrData, 1); | ||
| 1481 | addr_data->family = ai_member[0]; | ||
| 1482 | addr_data->socktype = ai_member[1]; | ||
| 1483 | addr_data->protocol = ai_member[2]; | ||
| 1484 | addr_data->addr_len = ai_member[3]; | ||
| 1485 | addr_data->addr = addr; | ||
| 1486 | |||
| 1487 | addr_list = g_list_append(addr_list, addr_data); | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | g_io_channel_shutdown(source, FALSE, NULL); | ||
| 1491 | g_io_channel_unref(source); | ||
| 1492 | |||
| 1493 | sock_kill_process(lookup_data->child_pid); | ||
| 1494 | |||
| 1495 | lookup_data->func(addr_list, lookup_data->data); | ||
| 1496 | |||
| 1497 | g_free(lookup_data->hostname); | ||
| 1498 | g_free(lookup_data); | ||
| 1499 | |||
| 1500 | return FALSE; | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | static SockLookupData *sock_get_address_info_async(const gchar *hostname, | ||
| 1504 | gushort port, | ||
| 1505 | SockAddrFunc func, | ||
| 1506 | gpointer data) | ||
| 1507 | { | ||
| 1508 | SockLookupData *lookup_data = NULL; | ||
| 1509 | gint pipe_fds[2]; | ||
| 1510 | pid_t pid; | ||
| 1511 | |||
| 1512 | resolver_init(); | ||
| 1513 | |||
| 1514 | if (pipe(pipe_fds) < 0) { | ||
| 1515 | perror("pipe"); | ||
| 1516 | func(NULL, data); | ||
| 1517 | return NULL; | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | if ((pid = fork()) < 0) { | ||
| 1521 | perror("fork"); | ||
| 1522 | func(NULL, data); | ||
| 1523 | return NULL; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | /* child process */ | ||
| 1527 | if (pid == 0) { | ||
| 1528 | #ifdef INET6 | ||
| 1529 | gint gai_err; | ||
| 1530 | struct addrinfo hints, *res, *ai; | ||
| 1531 | gchar port_str[6]; | ||
| 1532 | #else /* !INET6 */ | ||
| 1533 | struct hostent *hp; | ||
| 1534 | gchar **addr_list_p; | ||
| 1535 | struct sockaddr_in ad; | ||
| 1536 | #endif /* INET6 */ | ||
| 1537 | gint ai_member[4] = {AF_UNSPEC, 0, 0, 0}; | ||
| 1538 | |||
| 1539 | close(pipe_fds[0]); | ||
| 1540 | |||
| 1541 | #ifdef INET6 | ||
| 1542 | memset(&hints, 0, sizeof(hints)); | ||
| 1543 | /* hints.ai_flags = AI_CANONNAME; */ | ||
| 1544 | hints.ai_family = AF_UNSPEC; | ||
| 1545 | hints.ai_socktype = SOCK_STREAM; | ||
| 1546 | hints.ai_protocol = IPPROTO_TCP; | ||
| 1547 | |||
| 1548 | g_snprintf(port_str, sizeof(port_str), "%d", port); | ||
| 1549 | |||
| 1550 | gai_err = getaddrinfo(hostname, port_str, &hints, &res); | ||
| 1551 | if (gai_err != 0) { | ||
| 1552 | g_warning("getaddrinfo for %s:%s failed: %s", | ||
| 1553 | hostname, port_str, gai_strerror(gai_err)); | ||
| 1554 | fd_write_all(pipe_fds[1], (gchar *)ai_member, | ||
| 1555 | sizeof(ai_member)); | ||
| 1556 | close(pipe_fds[1]); | ||
| 1557 | _exit(1); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | for (ai = res; ai != NULL; ai = ai->ai_next) { | ||
| 1561 | ai_member[0] = ai->ai_family; | ||
| 1562 | ai_member[1] = ai->ai_socktype; | ||
| 1563 | ai_member[2] = ai->ai_protocol; | ||
| 1564 | ai_member[3] = ai->ai_addrlen; | ||
| 1565 | |||
| 1566 | fd_write_all(pipe_fds[1], (gchar *)ai_member, | ||
| 1567 | sizeof(ai_member)); | ||
| 1568 | fd_write_all(pipe_fds[1], (gchar *)ai->ai_addr, | ||
| 1569 | ai->ai_addrlen); | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | if (res != NULL) | ||
| 1573 | freeaddrinfo(res); | ||
| 1574 | #else /* !INET6 */ | ||
| 1575 | hp = my_gethostbyname(hostname); | ||
| 1576 | if (hp == NULL || hp->h_addrtype != AF_INET) { | ||
| 1577 | fd_write_all(pipe_fds[1], (gchar *)ai_member, | ||
| 1578 | sizeof(ai_member)); | ||
| 1579 | close(pipe_fds[1]); | ||
| 1580 | _exit(1); | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | ai_member[0] = AF_INET; | ||
| 1584 | ai_member[1] = SOCK_STREAM; | ||
| 1585 | ai_member[2] = IPPROTO_TCP; | ||
| 1586 | ai_member[3] = sizeof(ad); | ||
| 1587 | |||
| 1588 | memset(&ad, 0, sizeof(ad)); | ||
| 1589 | ad.sin_family = AF_INET; | ||
| 1590 | ad.sin_port = htons(port); | ||
| 1591 | |||
| 1592 | for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL; | ||
| 1593 | addr_list_p++) { | ||
| 1594 | memcpy(&ad.sin_addr, *addr_list_p, hp->h_length); | ||
| 1595 | fd_write_all(pipe_fds[1], (gchar *)ai_member, | ||
| 1596 | sizeof(ai_member)); | ||
| 1597 | fd_write_all(pipe_fds[1], (gchar *)&ad, sizeof(ad)); | ||
| 1598 | } | ||
| 1599 | #endif /* INET6 */ | ||
| 1600 | |||
| 1601 | close(pipe_fds[1]); | ||
| 1602 | |||
| 1603 | _exit(0); | ||
| 1604 | } else { | ||
| 1605 | close(pipe_fds[1]); | ||
| 1606 | |||
| 1607 | lookup_data = g_new0(SockLookupData, 1); | ||
| 1608 | lookup_data->hostname = g_strdup(hostname); | ||
| 1609 | lookup_data->child_pid = pid; | ||
| 1610 | lookup_data->func = func; | ||
| 1611 | lookup_data->data = data; | ||
| 1612 | |||
| 1613 | lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]); | ||
| 1614 | lookup_data->io_tag = g_io_add_watch | ||
| 1615 | (lookup_data->channel, G_IO_IN, | ||
| 1616 | sock_get_address_info_async_cb, lookup_data); | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | return lookup_data; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data) | ||
| 1623 | { | ||
| 1624 | if (lookup_data->io_tag > 0) | ||
| 1625 | g_source_remove(lookup_data->io_tag); | ||
| 1626 | if (lookup_data->channel) { | ||
| 1627 | g_io_channel_shutdown(lookup_data->channel, FALSE, NULL); | ||
| 1628 | g_io_channel_unref(lookup_data->channel); | ||
| 1629 | } | ||
| 1630 | |||
| 1631 | if (lookup_data->child_pid > 0) | ||
| 1632 | sock_kill_process(lookup_data->child_pid); | ||
| 1633 | |||
| 1634 | g_free(lookup_data->hostname); | ||
| 1635 | g_free(lookup_data); | ||
| 1636 | |||
| 1637 | return 0; | ||
| 1638 | } | ||
| 1639 | #endif /* G_OS_UNIX */ | ||
| 1640 | |||
| 1641 | #if USE_THREADS | ||
| 1642 | static gpointer sock_connect_async_func(gpointer data) | ||
| 1643 | { | ||
| 1644 | SockConnectData *conn_data = (SockConnectData *)data; | ||
| 1645 | gint ret; | ||
| 1646 | |||
| 1647 | ret = sock_info_connect(conn_data->sock); | ||
| 1648 | |||
| 1649 | if (ret == 0) { | ||
| 1650 | debug_print("sock_connect_async_func: connected\n"); | ||
| 1651 | } else { | ||
| 1652 | if (conn_data->sock->state == CONN_LOOKUPFAILED) | ||
| 1653 | debug_print("sock_connect_async_func: DNS lookup failed\n"); | ||
| 1654 | else | ||
| 1655 | debug_print("sock_connect_async_func: connection failed\n"); | ||
| 1656 | } | ||
| 1657 | |||
| 1658 | g_atomic_int_set(&conn_data->flag, 1); | ||
| 1659 | g_main_context_wakeup(NULL); | ||
| 1660 | |||
| 1661 | debug_print("sock_connect_async_func: exit\n"); | ||
| 1662 | return GINT_TO_POINTER(ret); | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | gint sock_connect_async_thread(const gchar *hostname, gushort port) | ||
| 1666 | { | ||
| 1667 | SockInfo *sock; | ||
| 1668 | gint ret; | ||
| 1669 | |||
| 1670 | sock = sock_new(hostname, port); | ||
| 1671 | ret = sock_info_connect_async_thread(sock); | ||
| 1672 | if (ret < 0) | ||
| 1673 | sock_close(sock); | ||
| 1674 | |||
| 1675 | return ret; | ||
| 1676 | } | ||
| 1677 | |||
| 1678 | gint sock_info_connect_async_thread(SockInfo *sock) | ||
| 1679 | { | ||
| 1680 | static gint id = 1; | ||
| 1681 | SockConnectData *data; | ||
| 1682 | |||
| 1683 | g_return_val_if_fail(sock != NULL, -1); | ||
| 1684 | g_return_val_if_fail(sock->hostname != NULL && sock->port > 0, -1); | ||
| 1685 | |||
| 1686 | data = g_new0(SockConnectData, 1); | ||
| 1687 | data->id = id++; | ||
| 1688 | data->hostname = g_strdup(sock->hostname); | ||
| 1689 | data->port = sock->port; | ||
| 1690 | data->flag = 0; | ||
| 1691 | data->sock = sock; | ||
| 1692 | |||
| 1693 | data->thread = g_thread_create(sock_connect_async_func, data, TRUE, | ||
| 1694 | NULL); | ||
| 1695 | if (!data->thread) { | ||
| 1696 | g_free(data->hostname); | ||
| 1697 | g_free(data); | ||
| 1698 | return -1; | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | sock_connect_data_list = g_list_append(sock_connect_data_list, data); | ||
| 1702 | |||
| 1703 | return data->id; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | gint sock_connect_async_thread_wait(gint id, SockInfo **sock) | ||
| 1707 | { | ||
| 1708 | gint ret; | ||
| 1709 | |||
| 1710 | *sock = NULL; | ||
| 1711 | ret = sock_info_connect_async_thread_wait(id, sock); | ||
| 1712 | if (ret < 0) { | ||
| 1713 | if (*sock) { | ||
| 1714 | sock_close(*sock); | ||
| 1715 | *sock = NULL; | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | return ret; | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | gint sock_info_connect_async_thread_wait(gint id, SockInfo **sock) | ||
| 1723 | { | ||
| 1724 | SockConnectData *conn_data = NULL; | ||
| 1725 | GList *cur; | ||
| 1726 | gint ret; | ||
| 1727 | |||
| 1728 | for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) { | ||
| 1729 | if (((SockConnectData *)cur->data)->id == id) { | ||
| 1730 | conn_data = (SockConnectData *)cur->data; | ||
| 1731 | break; | ||
| 1732 | } | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | if (!conn_data) { | ||
| 1736 | g_warning("sock_info_connect_async_thread_wait: id %d not found.", id); | ||
| 1737 | return -1; | ||
| 1738 | } | ||
| 1739 | |||
| 1740 | debug_print("sock_connect_async_thread_wait: waiting thread\n"); | ||
| 1741 | while (g_atomic_int_get(&conn_data->flag) == 0) | ||
| 1742 | event_loop_iterate(); | ||
| 1743 | |||
| 1744 | ret = GPOINTER_TO_INT(g_thread_join(conn_data->thread)); | ||
| 1745 | debug_print("sock_info_connect_async_thread_wait: thread exited with status %d\n", ret); | ||
| 1746 | |||
| 1747 | if (sock) | ||
| 1748 | *sock = conn_data->sock; | ||
| 1749 | |||
| 1750 | sock_connect_data_list = g_list_remove(sock_connect_data_list, | ||
| 1751 | conn_data); | ||
| 1752 | g_free(conn_data->hostname); | ||
| 1753 | g_free(conn_data); | ||
| 1754 | |||
| 1755 | return ret; | ||
| 1756 | } | ||
| 1757 | #endif /* USE_THREADS */ | ||
| 1758 | |||
| 1759 | gint sock_printf(SockInfo *sock, const gchar *format, ...) | ||
| 1760 | { | ||
| 1761 | va_list args; | ||
| 1762 | gchar buf[BUFFSIZE]; | ||
| 1763 | |||
| 1764 | va_start(args, format); | ||
| 1765 | g_vsnprintf(buf, sizeof(buf), format, args); | ||
| 1766 | va_end(args); | ||
| 1767 | |||
| 1768 | return sock_write_all(sock, buf, strlen(buf)); | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | #ifdef G_OS_WIN32 | ||
| 1772 | static void sock_set_errno_from_last_error(gint error) | ||
| 1773 | { | ||
| 1774 | switch (error) { | ||
| 1775 | case WSAEWOULDBLOCK: | ||
| 1776 | errno = EAGAIN; | ||
| 1777 | break; | ||
| 1778 | default: | ||
| 1779 | debug_print("last error = %d\n", error); | ||
| 1780 | errno = 0; | ||
| 1781 | break; | ||
| 1782 | } | ||
| 1783 | } | ||
| 1784 | #endif | ||
| 1785 | |||
| 1786 | gint sock_read(SockInfo *sock, gchar *buf, gint len) | ||
| 1787 | { | ||
| 1788 | g_return_val_if_fail(sock != NULL, -1); | ||
| 1789 | |||
| 1790 | #if USE_SSL | ||
| 1791 | if (sock->ssl) | ||
| 1792 | return ssl_read(sock->ssl, buf, len); | ||
| 1793 | #endif | ||
| 1794 | return fd_read(sock->sock, buf, len); | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | gint fd_read(gint fd, gchar *buf, gint len) | ||
| 1798 | { | ||
| 1799 | #ifdef G_OS_WIN32 | ||
| 1800 | return fd_recv(fd, buf, len, 0); | ||
| 1801 | #else | ||
| 1802 | if (fd_check_io(fd, G_IO_IN) < 0) | ||
| 1803 | return -1; | ||
| 1804 | |||
| 1805 | return read(fd, buf, len); | ||
| 1806 | #endif | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | #if USE_SSL | ||
| 1810 | gint ssl_read(SSL *ssl, gchar *buf, gint len) | ||
| 1811 | { | ||
| 1812 | gint err, ret; | ||
| 1813 | |||
| 1814 | errno = 0; | ||
| 1815 | |||
| 1816 | if (SSL_pending(ssl) == 0) { | ||
| 1817 | if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) | ||
| 1818 | return -1; | ||
| 1819 | } | ||
| 1820 | |||
| 1821 | ret = SSL_read(ssl, buf, len); | ||
| 1822 | |||
| 1823 | switch ((err = SSL_get_error(ssl, ret))) { | ||
| 1824 | case SSL_ERROR_NONE: | ||
| 1825 | return ret; | ||
| 1826 | case SSL_ERROR_WANT_READ: | ||
| 1827 | case SSL_ERROR_WANT_WRITE: | ||
| 1828 | errno = EAGAIN; | ||
| 1829 | return -1; | ||
| 1830 | case SSL_ERROR_ZERO_RETURN: | ||
| 1831 | return 0; | ||
| 1832 | default: | ||
| 1833 | g_warning("SSL_read() returned error %d, ret = %d\n", err, ret); | ||
| 1834 | if (ret == 0) | ||
| 1835 | return 0; | ||
| 1836 | #ifdef G_OS_WIN32 | ||
| 1837 | errno = EIO; | ||
| 1838 | #endif | ||
| 1839 | return -1; | ||
| 1840 | } | ||
| 1841 | } | ||
| 1842 | #endif | ||
| 1843 | |||
| 1844 | gint sock_write(SockInfo *sock, const gchar *buf, gint len) | ||
| 1845 | { | ||
| 1846 | g_return_val_if_fail(sock != NULL, -1); | ||
| 1847 | |||
| 1848 | #if USE_SSL | ||
| 1849 | if (sock->ssl) | ||
| 1850 | return ssl_write(sock->ssl, buf, len); | ||
| 1851 | #endif | ||
| 1852 | return fd_write(sock->sock, buf, len); | ||
| 1853 | } | ||
| 1854 | |||
| 1855 | gint fd_write(gint fd, const gchar *buf, gint len) | ||
| 1856 | { | ||
| 1857 | #ifdef G_OS_WIN32 | ||
| 1858 | gint ret; | ||
| 1859 | #endif | ||
| 1860 | if (fd_check_io(fd, G_IO_OUT) < 0) | ||
| 1861 | return -1; | ||
| 1862 | |||
| 1863 | #ifdef G_OS_WIN32 | ||
| 1864 | ret = send(fd, buf, len, 0); | ||
| 1865 | if (ret == SOCKET_ERROR) { | ||
| 1866 | gint err; | ||
| 1867 | err = WSAGetLastError(); | ||
| 1868 | sock_set_errno_from_last_error(err); | ||
| 1869 | if (err != WSAEWOULDBLOCK) | ||
| 1870 | g_warning("fd_write() failed with %d (errno = %d)\n", | ||
| 1871 | err, errno); | ||
| 1872 | } | ||
| 1873 | return ret; | ||
| 1874 | #else | ||
| 1875 | return write(fd, buf, len); | ||
| 1876 | #endif | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | #if USE_SSL | ||
| 1880 | gint ssl_write(SSL *ssl, const gchar *buf, gint len) | ||
| 1881 | { | ||
| 1882 | gint ret; | ||
| 1883 | |||
| 1884 | ret = SSL_write(ssl, buf, len); | ||
| 1885 | |||
| 1886 | switch (SSL_get_error(ssl, ret)) { | ||
| 1887 | case SSL_ERROR_NONE: | ||
| 1888 | return ret; | ||
| 1889 | case SSL_ERROR_WANT_READ: | ||
| 1890 | case SSL_ERROR_WANT_WRITE: | ||
| 1891 | errno = EAGAIN; | ||
| 1892 | return -1; | ||
| 1893 | default: | ||
| 1894 | return -1; | ||
| 1895 | } | ||
| 1896 | } | ||
| 1897 | #endif | ||
| 1898 | |||
| 1899 | gint sock_write_all(SockInfo *sock, const gchar *buf, gint len) | ||
| 1900 | { | ||
| 1901 | g_return_val_if_fail(sock != NULL, -1); | ||
| 1902 | |||
| 1903 | #if USE_SSL | ||
| 1904 | if (sock->ssl) | ||
| 1905 | return ssl_write_all(sock->ssl, buf, len); | ||
| 1906 | #endif | ||
| 1907 | return fd_write_all(sock->sock, buf, len); | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | gint fd_write_all(gint fd, const gchar *buf, gint len) | ||
| 1911 | { | ||
| 1912 | gint n, wrlen = 0; | ||
| 1913 | |||
| 1914 | while (len) { | ||
| 1915 | n = fd_write(fd, buf, len); | ||
| 1916 | if (n <= 0) | ||
| 1917 | return -1; | ||
| 1918 | len -= n; | ||
| 1919 | wrlen += n; | ||
| 1920 | buf += n; | ||
| 1921 | } | ||
| 1922 | |||
| 1923 | return wrlen; | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | #if USE_SSL | ||
| 1927 | gint ssl_write_all(SSL *ssl, const gchar *buf, gint len) | ||
| 1928 | { | ||
| 1929 | gint n, wrlen = 0; | ||
| 1930 | |||
| 1931 | while (len) { | ||
| 1932 | n = ssl_write(ssl, buf, len); | ||
| 1933 | if (n <= 0) | ||
| 1934 | return -1; | ||
| 1935 | len -= n; | ||
| 1936 | wrlen += n; | ||
| 1937 | buf += n; | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | return wrlen; | ||
| 1941 | } | ||
| 1942 | #endif | ||
| 1943 | |||
| 1944 | gint fd_recv(gint fd, gchar *buf, gint len, gint flags) | ||
| 1945 | { | ||
| 1946 | #ifdef G_OS_WIN32 | ||
| 1947 | gint ret; | ||
| 1948 | #endif | ||
| 1949 | if (fd_check_io(fd, G_IO_IN) < 0) | ||
| 1950 | return -1; | ||
| 1951 | |||
| 1952 | #ifdef G_OS_WIN32 | ||
| 1953 | ret = recv(fd, buf, len, flags); | ||
| 1954 | if (ret == SOCKET_ERROR) { | ||
| 1955 | gint err; | ||
| 1956 | err = WSAGetLastError(); | ||
| 1957 | sock_set_errno_from_last_error(err); | ||
| 1958 | if (err != WSAEWOULDBLOCK) | ||
| 1959 | g_warning("fd_recv(): failed with %d (errno = %d)\n", | ||
| 1960 | err, errno); | ||
| 1961 | } | ||
| 1962 | return ret; | ||
| 1963 | #else | ||
| 1964 | return recv(fd, buf, len, flags); | ||
| 1965 | #endif | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | gint fd_gets(gint fd, gchar *buf, gint len) | ||
| 1969 | { | ||
| 1970 | gchar *newline, *bp = buf; | ||
| 1971 | gint n; | ||
| 1972 | |||
| 1973 | if (--len < 1) | ||
| 1974 | return -1; | ||
| 1975 | do { | ||
| 1976 | if ((n = fd_recv(fd, bp, len, MSG_PEEK)) <= 0) | ||
| 1977 | return -1; | ||
| 1978 | if ((newline = memchr(bp, '\n', n)) != NULL) | ||
| 1979 | n = newline - bp + 1; | ||
| 1980 | if ((n = fd_read(fd, bp, n)) < 0) | ||
| 1981 | return -1; | ||
| 1982 | bp += n; | ||
| 1983 | len -= n; | ||
| 1984 | } while (!newline && len); | ||
| 1985 | |||
| 1986 | *bp = '\0'; | ||
| 1987 | return bp - buf; | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | #if USE_SSL | ||
| 1991 | gint ssl_gets(SSL *ssl, gchar *buf, gint len) | ||
| 1992 | { | ||
| 1993 | gchar *newline, *bp = buf; | ||
| 1994 | gint n; | ||
| 1995 | |||
| 1996 | if (--len < 1) | ||
| 1997 | return -1; | ||
| 1998 | do { | ||
| 1999 | if ((n = ssl_peek(ssl, bp, len)) <= 0) | ||
| 2000 | return -1; | ||
| 2001 | if ((newline = memchr(bp, '\n', n)) != NULL) | ||
| 2002 | n = newline - bp + 1; | ||
| 2003 | if ((n = ssl_read(ssl, bp, n)) < 0) | ||
| 2004 | return -1; | ||
| 2005 | bp += n; | ||
| 2006 | len -= n; | ||
| 2007 | } while (!newline && len); | ||
| 2008 | |||
| 2009 | *bp = '\0'; | ||
| 2010 | return bp - buf; | ||
| 2011 | } | ||
| 2012 | #endif | ||
| 2013 | |||
| 2014 | gint sock_gets(SockInfo *sock, gchar *buf, gint len) | ||
| 2015 | { | ||
| 2016 | g_return_val_if_fail(sock != NULL, -1); | ||
| 2017 | |||
| 2018 | #if USE_SSL | ||
| 2019 | if (sock->ssl) | ||
| 2020 | return ssl_gets(sock->ssl, buf, len); | ||
| 2021 | #endif | ||
| 2022 | return fd_gets(sock->sock, buf, len); | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | gint fd_getline(gint fd, gchar **line) | ||
| 2026 | { | ||
| 2027 | gchar buf[BUFFSIZE]; | ||
| 2028 | gchar *str = NULL; | ||
| 2029 | gint len; | ||
| 2030 | gulong size = 0; | ||
| 2031 | gulong cur_offset = 0; | ||
| 2032 | |||
| 2033 | while ((len = fd_gets(fd, buf, sizeof(buf))) > 0) { | ||
| 2034 | size += len; | ||
| 2035 | str = g_realloc(str, size + 1); | ||
| 2036 | memcpy(str + cur_offset, buf, len + 1); | ||
| 2037 | cur_offset += len; | ||
| 2038 | if (buf[len - 1] == '\n') | ||
| 2039 | break; | ||
| 2040 | } | ||
| 2041 | |||
| 2042 | *line = str; | ||
| 2043 | |||
| 2044 | if (!str) | ||
| 2045 | return -1; | ||
| 2046 | else | ||
| 2047 | return (gint)size; | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | #if USE_SSL | ||
| 2051 | gint ssl_getline(SSL *ssl, gchar **line) | ||
| 2052 | { | ||
| 2053 | gchar buf[BUFFSIZE]; | ||
| 2054 | gchar *str = NULL; | ||
| 2055 | gint len; | ||
| 2056 | gulong size = 0; | ||
| 2057 | gulong cur_offset = 0; | ||
| 2058 | |||
| 2059 | while ((len = ssl_gets(ssl, buf, sizeof(buf))) > 0) { | ||
| 2060 | size += len; | ||
| 2061 | str = g_realloc(str, size + 1); | ||
| 2062 | memcpy(str + cur_offset, buf, len + 1); | ||
| 2063 | cur_offset += len; | ||
| 2064 | if (buf[len - 1] == '\n') | ||
| 2065 | break; | ||
| 2066 | } | ||
| 2067 | |||
| 2068 | *line = str; | ||
| 2069 | |||
| 2070 | if (!str) | ||
| 2071 | return -1; | ||
| 2072 | else | ||
| 2073 | return (gint)size; | ||
| 2074 | } | ||
| 2075 | #endif | ||
| 2076 | |||
| 2077 | gint sock_getline(SockInfo *sock, gchar **line) | ||
| 2078 | { | ||
| 2079 | g_return_val_if_fail(sock != NULL, -1); | ||
| 2080 | g_return_val_if_fail(line != NULL, -1); | ||
| 2081 | |||
| 2082 | #if USE_SSL | ||
| 2083 | if (sock->ssl) | ||
| 2084 | return ssl_getline(sock->ssl, line); | ||
| 2085 | #endif | ||
| 2086 | return fd_getline(sock->sock, line); | ||
| 2087 | } | ||
| 2088 | |||
| 2089 | gint sock_puts(SockInfo *sock, const gchar *buf) | ||
| 2090 | { | ||
| 2091 | gint ret; | ||
| 2092 | |||
| 2093 | if ((ret = sock_write_all(sock, buf, strlen(buf))) < 0) | ||
| 2094 | return ret; | ||
| 2095 | return sock_write_all(sock, "\r\n", 2); | ||
| 2096 | } | ||
| 2097 | |||
| 2098 | /* peek at the socket data without actually reading it */ | ||
| 2099 | #if USE_SSL | ||
| 2100 | gint ssl_peek(SSL *ssl, gchar *buf, gint len) | ||
| 2101 | { | ||
| 2102 | gint err, ret; | ||
| 2103 | |||
| 2104 | if (SSL_pending(ssl) == 0) { | ||
| 2105 | if (fd_check_io(SSL_get_rfd(ssl), G_IO_IN) < 0) | ||
| 2106 | return -1; | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | ret = SSL_peek(ssl, buf, len); | ||
| 2110 | |||
| 2111 | switch ((err = SSL_get_error(ssl, ret))) { | ||
| 2112 | case SSL_ERROR_NONE: | ||
| 2113 | return ret; | ||
| 2114 | case SSL_ERROR_WANT_READ: | ||
| 2115 | case SSL_ERROR_WANT_WRITE: | ||
| 2116 | errno = EAGAIN; | ||
| 2117 | return -1; | ||
| 2118 | case SSL_ERROR_ZERO_RETURN: | ||
| 2119 | return 0; | ||
| 2120 | default: | ||
| 2121 | g_warning("SSL_peek() returned error %d, ret = %d\n", err, ret); | ||
| 2122 | if (ret == 0) | ||
| 2123 | return 0; | ||
| 2124 | return -1; | ||
| 2125 | } | ||
| 2126 | } | ||
| 2127 | #endif | ||
| 2128 | |||
| 2129 | gint sock_peek(SockInfo *sock, gchar *buf, gint len) | ||
| 2130 | { | ||
| 2131 | g_return_val_if_fail(sock != NULL, -1); | ||
| 2132 | |||
| 2133 | #if USE_SSL | ||
| 2134 | if (sock->ssl) | ||
| 2135 | return ssl_peek(sock->ssl, buf, len); | ||
| 2136 | #endif | ||
| 2137 | return fd_recv(sock->sock, buf, len, MSG_PEEK); | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | gint sock_close(SockInfo *sock) | ||
| 2141 | { | ||
| 2142 | GList *cur; | ||
| 2143 | |||
| 2144 | if (!sock) | ||
| 2145 | return 0; | ||
| 2146 | |||
| 2147 | debug_print("sock_close: %s:%u (%p)\n", sock->hostname ? sock->hostname : "(none)", sock->port, sock); | ||
| 2148 | |||
| 2149 | #if USE_SSL | ||
| 2150 | if (sock->ssl) | ||
| 2151 | ssl_done_socket(sock); | ||
| 2152 | #endif | ||
| 2153 | |||
| 2154 | if (sock->sock_ch) { | ||
| 2155 | g_io_channel_shutdown(sock->sock_ch, FALSE, NULL); | ||
| 2156 | g_io_channel_unref(sock->sock_ch); | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | for (cur = sock_list; cur != NULL; cur = cur->next) { | ||
| 2160 | if ((SockInfo *)cur->data == sock) { | ||
| 2161 | sock_list = g_list_remove(sock_list, sock); | ||
| 2162 | break; | ||
| 2163 | } | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | g_free(sock->hostname); | ||
| 2167 | g_free(sock); | ||
| 2168 | |||
| 2169 | return 0; | ||
| 2170 | } | ||
| 2171 | |||
| 2172 | gint fd_close(gint fd) | ||
| 2173 | { | ||
| 2174 | #ifdef G_OS_WIN32 | ||
| 2175 | return closesocket(fd); | ||
| 2176 | #else | ||
| 2177 | return close(fd); | ||
| 2178 | #endif | ||
| 2179 | } | ||
diff --git a/libsylph/socket.h b/libsylph/socket.h new file mode 100644 index 0000000..15bcef7 --- /dev/null +++ b/libsylph/socket.h | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2012 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SOCKET_H__ | ||
| 21 | #define __SOCKET_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #if HAVE_NETDB_H | ||
| 29 | # include <netdb.h> | ||
| 30 | #endif | ||
| 31 | #ifdef G_OS_WIN32 | ||
| 32 | # include <winsock2.h> | ||
| 33 | #endif | ||
| 34 | |||
| 35 | typedef struct _SockInfo SockInfo; | ||
| 36 | |||
| 37 | #if USE_SSL | ||
| 38 | # include "ssl.h" | ||
| 39 | #endif | ||
| 40 | |||
| 41 | typedef enum | ||
| 42 | { | ||
| 43 | CONN_READY, | ||
| 44 | CONN_LOOKUPSUCCESS, | ||
| 45 | CONN_ESTABLISHED, | ||
| 46 | CONN_LOOKUPFAILED, | ||
| 47 | CONN_FAILED | ||
| 48 | } ConnectionState; | ||
| 49 | |||
| 50 | typedef enum | ||
| 51 | { | ||
| 52 | SYL_SOCK_NONBLOCK = 1 << 0, | ||
| 53 | SYL_SOCK_CHECK_IO = 1 << 1 | ||
| 54 | } SockFlags; | ||
| 55 | |||
| 56 | #define SOCK_SET_FLAGS(flags, set) { (flags) |= (set); } | ||
| 57 | #define SOCK_UNSET_FLAGS(flags, set) { (flags) &= ~(set); } | ||
| 58 | #define SOCK_IS_NONBLOCK(flags) ((flags & SYL_SOCK_NONBLOCK) != 0) | ||
| 59 | #define SOCK_IS_CHECK_IO(flags) ((flags & SYL_SOCK_CHECK_IO) != 0) | ||
| 60 | |||
| 61 | typedef gint (*SockConnectFunc) (SockInfo *sock, | ||
| 62 | gpointer data); | ||
| 63 | typedef gboolean (*SockFunc) (SockInfo *sock, | ||
| 64 | GIOCondition condition, | ||
| 65 | gpointer data); | ||
| 66 | |||
| 67 | struct _SockInfo | ||
| 68 | { | ||
| 69 | gint sock; | ||
| 70 | #if USE_SSL | ||
| 71 | SSL *ssl; | ||
| 72 | #else | ||
| 73 | gpointer ssl; | ||
| 74 | #endif | ||
| 75 | GIOChannel *sock_ch; | ||
| 76 | |||
| 77 | gchar *hostname; | ||
| 78 | gushort port; | ||
| 79 | ConnectionState state; | ||
| 80 | SockFlags flags; | ||
| 81 | gpointer data; | ||
| 82 | |||
| 83 | SockFunc callback; | ||
| 84 | GIOCondition condition; | ||
| 85 | }; | ||
| 86 | |||
| 87 | gint sock_init (void); | ||
| 88 | gint sock_cleanup (void); | ||
| 89 | |||
| 90 | gint sock_set_io_timeout (guint sec); | ||
| 91 | |||
| 92 | SockInfo *sock_new (const gchar *hostname, gushort port); | ||
| 93 | |||
| 94 | gint sock_set_nonblocking_mode (SockInfo *sock, gboolean nonblock); | ||
| 95 | gboolean sock_is_nonblocking_mode (SockInfo *sock); | ||
| 96 | |||
| 97 | gboolean sock_has_read_data (SockInfo *sock); | ||
| 98 | |||
| 99 | guint sock_add_watch (SockInfo *sock, GIOCondition condition, | ||
| 100 | SockFunc func, gpointer data); | ||
| 101 | guint sock_add_watch_poll (SockInfo *sock, GIOCondition condition, | ||
| 102 | SockFunc func, gpointer data); | ||
| 103 | |||
| 104 | struct hostent *my_gethostbyname (const gchar *hostname); | ||
| 105 | |||
| 106 | SockInfo *sock_connect (const gchar *hostname, gushort port); | ||
| 107 | #ifdef G_OS_UNIX | ||
| 108 | gint sock_connect_async (const gchar *hostname, gushort port, | ||
| 109 | SockConnectFunc func, gpointer data); | ||
| 110 | gint sock_connect_async_cancel (gint id); | ||
| 111 | #endif | ||
| 112 | #if USE_THREADS | ||
| 113 | gint sock_connect_async_thread (const gchar *hostname, gushort port); | ||
| 114 | gint sock_connect_async_thread_wait (gint id, SockInfo **sock); | ||
| 115 | #endif | ||
| 116 | |||
| 117 | gint sock_info_connect (SockInfo *sock); | ||
| 118 | #ifdef G_OS_UNIX | ||
| 119 | gint sock_info_connect_async (SockInfo *sock, | ||
| 120 | SockConnectFunc func, gpointer data); | ||
| 121 | #endif | ||
| 122 | #if USE_THREADS | ||
| 123 | gint sock_info_connect_async_thread (SockInfo *sock); | ||
| 124 | gint sock_info_connect_async_thread_wait(gint id, SockInfo **sock); | ||
| 125 | #endif | ||
| 126 | |||
| 127 | /* Basic I/O functions */ | ||
| 128 | gint sock_printf (SockInfo *sock, const gchar *format, ...) | ||
| 129 | G_GNUC_PRINTF(2, 3); | ||
| 130 | gint sock_read (SockInfo *sock, gchar *buf, gint len); | ||
| 131 | gint sock_write (SockInfo *sock, const gchar *buf, gint len); | ||
| 132 | gint sock_write_all (SockInfo *sock, const gchar *buf, gint len); | ||
| 133 | gint sock_gets (SockInfo *sock, gchar *buf, gint len); | ||
| 134 | gint sock_getline (SockInfo *sock, gchar **line); | ||
| 135 | gint sock_puts (SockInfo *sock, const gchar *buf); | ||
| 136 | gint sock_peek (SockInfo *sock, gchar *buf, gint len); | ||
| 137 | gint sock_close (SockInfo *sock); | ||
| 138 | |||
| 139 | /* Functions to directly work on FD. They are needed for pipes */ | ||
| 140 | gint fd_connect_inet (gushort port); | ||
| 141 | gint fd_open_inet (gushort port); | ||
| 142 | gint fd_connect_unix (const gchar *path); | ||
| 143 | gint fd_open_unix (const gchar *path); | ||
| 144 | gint fd_accept (gint sock); | ||
| 145 | |||
| 146 | gint fd_read (gint sock, gchar *buf, gint len); | ||
| 147 | gint fd_write (gint sock, const gchar *buf, gint len); | ||
| 148 | gint fd_write_all (gint sock, const gchar *buf, gint len); | ||
| 149 | gint fd_gets (gint sock, gchar *buf, gint len); | ||
| 150 | gint fd_getline (gint sock, gchar **line); | ||
| 151 | gint fd_close (gint sock); | ||
| 152 | |||
| 153 | /* Functions for SSL */ | ||
| 154 | #if USE_SSL | ||
| 155 | gint ssl_read (SSL *ssl, gchar *buf, gint len); | ||
| 156 | gint ssl_write (SSL *ssl, const gchar *buf, gint len); | ||
| 157 | gint ssl_write_all (SSL *ssl, const gchar *buf, gint len); | ||
| 158 | gint ssl_gets (SSL *ssl, gchar *buf, gint len); | ||
| 159 | gint ssl_getline (SSL *ssl, gchar **line); | ||
| 160 | gint ssl_peek (SSL *ssl, gchar *buf, gint len); | ||
| 161 | #endif | ||
| 162 | |||
| 163 | #endif /* __SOCKET_H__ */ | ||
diff --git a/libsylph/socks.c b/libsylph/socks.c new file mode 100644 index 0000000..b725ba7 --- /dev/null +++ b/libsylph/socks.c | |||
| @@ -0,0 +1,267 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | |||
| 26 | #ifdef G_OS_WIN32 | ||
| 27 | # include <winsock2.h> | ||
| 28 | # include <ws2tcpip.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #include "socket.h" | ||
| 32 | #include "socks.h" | ||
| 33 | #include "utils.h" | ||
| 34 | |||
| 35 | |||
| 36 | SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host, | ||
| 37 | gushort proxy_port, const gchar *proxy_name, | ||
| 38 | const gchar *proxy_pass) | ||
| 39 | { | ||
| 40 | SocksInfo *socks_info; | ||
| 41 | |||
| 42 | socks_info = g_new0(SocksInfo, 1); | ||
| 43 | socks_info->type = type; | ||
| 44 | socks_info->proxy_host = g_strdup(proxy_host); | ||
| 45 | socks_info->proxy_port = proxy_port; | ||
| 46 | socks_info->proxy_name = g_strdup(proxy_name); | ||
| 47 | socks_info->proxy_pass = g_strdup(proxy_pass); | ||
| 48 | |||
| 49 | return socks_info; | ||
| 50 | } | ||
| 51 | |||
| 52 | void socks_info_free(SocksInfo *socks_info) | ||
| 53 | { | ||
| 54 | if (!socks_info) | ||
| 55 | return; | ||
| 56 | g_free(socks_info->proxy_host); | ||
| 57 | g_free(socks_info->proxy_name); | ||
| 58 | g_free(socks_info->proxy_pass); | ||
| 59 | g_free(socks_info); | ||
| 60 | } | ||
| 61 | |||
| 62 | gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 63 | SocksInfo *socks_info) | ||
| 64 | { | ||
| 65 | g_return_val_if_fail(sock != NULL, -1); | ||
| 66 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 67 | g_return_val_if_fail(socks_info != NULL, -1); | ||
| 68 | |||
| 69 | debug_print("socks_connect: connect to %s:%u via %s:%u\n", | ||
| 70 | hostname, port, | ||
| 71 | socks_info->proxy_host, socks_info->proxy_port); | ||
| 72 | |||
| 73 | if (socks_info->type == SOCKS_SOCKS5) | ||
| 74 | return socks5_connect(sock, hostname, port, | ||
| 75 | socks_info->proxy_name, | ||
| 76 | socks_info->proxy_pass); | ||
| 77 | else if (socks_info->type == SOCKS_SOCKS4) | ||
| 78 | return socks4_connect(sock, hostname, port); | ||
| 79 | else | ||
| 80 | g_warning("socks_connect: unknown SOCKS type: %d\n", | ||
| 81 | socks_info->type); | ||
| 82 | |||
| 83 | return -1; | ||
| 84 | } | ||
| 85 | |||
| 86 | gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port) | ||
| 87 | { | ||
| 88 | guchar socks_req[1024]; | ||
| 89 | struct hostent *hp; | ||
| 90 | |||
| 91 | g_return_val_if_fail(sock != NULL, -1); | ||
| 92 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 93 | |||
| 94 | debug_print("socks4_connect: connect to %s:%u\n", hostname, port); | ||
| 95 | |||
| 96 | socks_req[0] = 4; | ||
| 97 | socks_req[1] = 1; | ||
| 98 | *((gushort *)(socks_req + 2)) = htons(port); | ||
| 99 | |||
| 100 | /* lookup */ | ||
| 101 | if ((hp = my_gethostbyname(hostname)) == NULL) { | ||
| 102 | g_warning("socks4_connect: cannot lookup host: %s", hostname); | ||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | if (hp->h_length != 4) { | ||
| 106 | g_warning("socks4_connect: invalid address length for host: %s", hostname); | ||
| 107 | return -1; | ||
| 108 | } | ||
| 109 | memcpy(socks_req + 4, (guchar *)hp->h_addr, 4); | ||
| 110 | |||
| 111 | /* userid (empty) */ | ||
| 112 | socks_req[8] = 0; | ||
| 113 | |||
| 114 | if (sock_write_all(sock, (gchar *)socks_req, 9) != 9) { | ||
| 115 | g_warning("socks4_connect: SOCKS4 initial request write failed"); | ||
| 116 | return -1; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (sock_read(sock, (gchar *)socks_req, 8) != 8) { | ||
| 120 | g_warning("socks4_connect: SOCKS4 response read failed"); | ||
| 121 | return -1; | ||
| 122 | } | ||
| 123 | if (socks_req[0] != 0) { | ||
| 124 | g_warning("socks4_connect: SOCKS4 response has invalid version"); | ||
| 125 | return -1; | ||
| 126 | } | ||
| 127 | if (socks_req[1] != 90) { | ||
| 128 | g_warning("socks4_connect: SOCKS4 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 2)), socks_req[1]); | ||
| 129 | return -1; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* replace sock->hostname with endpoint */ | ||
| 133 | if (sock->hostname != hostname) { | ||
| 134 | g_free(sock->hostname); | ||
| 135 | sock->hostname = g_strdup(hostname); | ||
| 136 | sock->port = port; | ||
| 137 | } | ||
| 138 | |||
| 139 | debug_print("socks4_connect: SOCKS4 connection to %s:%u successful.\n", hostname, port); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 145 | const gchar *proxy_name, const gchar *proxy_pass) | ||
| 146 | { | ||
| 147 | guchar socks_req[1024]; | ||
| 148 | size_t len; | ||
| 149 | size_t size; | ||
| 150 | |||
| 151 | g_return_val_if_fail(sock != NULL, -1); | ||
| 152 | g_return_val_if_fail(hostname != NULL, -1); | ||
| 153 | |||
| 154 | debug_print("socks5_connect: connect to %s:%u\n", hostname, port); | ||
| 155 | |||
| 156 | len = strlen(hostname); | ||
| 157 | if (len > 255) { | ||
| 158 | g_warning("socks5_connect: hostname too long"); | ||
| 159 | return -1; | ||
| 160 | } | ||
| 161 | |||
| 162 | socks_req[0] = 5; | ||
| 163 | socks_req[1] = proxy_name ? 2 : 1; | ||
| 164 | socks_req[2] = 0; | ||
| 165 | socks_req[3] = 2; | ||
| 166 | |||
| 167 | if (sock_write_all(sock, (gchar *)socks_req, 2 + socks_req[1]) != 2 + socks_req[1]) { | ||
| 168 | g_warning("socks5_connect: SOCKS5 initial request write failed"); | ||
| 169 | return -1; | ||
| 170 | } | ||
| 171 | |||
| 172 | if (sock_read(sock, (gchar *)socks_req, 2) != 2) { | ||
| 173 | g_warning("socks5_connect: SOCKS5 response read failed"); | ||
| 174 | return -1; | ||
| 175 | } | ||
| 176 | if (socks_req[0] != 5) { | ||
| 177 | g_warning("socks5_connect: SOCKS5 response has invalid version"); | ||
| 178 | return -1; | ||
| 179 | } | ||
| 180 | if (socks_req[1] == 2) { | ||
| 181 | /* auth */ | ||
| 182 | size_t userlen, passlen; | ||
| 183 | gint reqlen; | ||
| 184 | |||
| 185 | if (proxy_name && proxy_pass) { | ||
| 186 | userlen = strlen(proxy_name); | ||
| 187 | passlen = strlen(proxy_pass); | ||
| 188 | } else | ||
| 189 | userlen = passlen = 0; | ||
| 190 | |||
| 191 | socks_req[0] = 1; | ||
| 192 | socks_req[1] = (guchar)userlen; | ||
| 193 | if (proxy_name && userlen > 0) | ||
| 194 | memcpy(socks_req + 2, proxy_name, userlen); | ||
| 195 | socks_req[2 + userlen] = (guchar)passlen; | ||
| 196 | if (proxy_pass && passlen > 0) | ||
| 197 | memcpy(socks_req + 2 + userlen + 1, proxy_pass, passlen); | ||
| 198 | |||
| 199 | reqlen = 2 + userlen + 1 + passlen; | ||
| 200 | if (sock_write_all(sock, (gchar *)socks_req, reqlen) != reqlen) { | ||
| 201 | g_warning("socks5_connect: SOCKS5 auth write failed"); | ||
| 202 | return -1; | ||
| 203 | } | ||
| 204 | if (sock_read(sock, (gchar *)socks_req, 2) != 2) { | ||
| 205 | g_warning("socks5_connect: SOCKS5 auth response read failed"); | ||
| 206 | return -1; | ||
| 207 | } | ||
| 208 | if (socks_req[1] != 0) { | ||
| 209 | g_warning("socks5_connect: SOCKS5 authentication failed: user: %s (%u %u)", proxy_name ? proxy_name : "(none)", socks_req[0], socks_req[1]); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | } else if (socks_req[1] != 0) { | ||
| 213 | g_warning("socks5_connect: SOCKS5 reply (%u) error", socks_req[1]); | ||
| 214 | return -1; | ||
| 215 | } | ||
| 216 | |||
| 217 | socks_req[0] = 5; | ||
| 218 | socks_req[1] = 1; | ||
| 219 | socks_req[2] = 0; | ||
| 220 | |||
| 221 | socks_req[3] = 3; | ||
| 222 | socks_req[4] = (guchar)len; | ||
| 223 | memcpy(socks_req + 5, hostname, len); | ||
| 224 | *((gushort *)(socks_req + 5 + len)) = htons(port); | ||
| 225 | |||
| 226 | if (sock_write_all(sock, (gchar *)socks_req, 5 + len + 2) != 5 + len + 2) { | ||
| 227 | g_warning("socks5_connect: SOCKS5 connect request write failed"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (sock_read(sock, (gchar *)socks_req, 10) != 10) { | ||
| 232 | g_warning("socks5_connect: SOCKS5 connect request response read failed"); | ||
| 233 | return -1; | ||
| 234 | } | ||
| 235 | if (socks_req[0] != 5) { | ||
| 236 | g_warning("socks5_connect: SOCKS5 response has invalid version"); | ||
| 237 | return -1; | ||
| 238 | } | ||
| 239 | if (socks_req[1] != 0) { | ||
| 240 | g_warning("socks5_connect: SOCKS5 connection to %u.%u.%u.%u:%u failed. (%u)", socks_req[4], socks_req[5], socks_req[6], socks_req[7], ntohs(*(gushort *)(socks_req + 8)), socks_req[1]); | ||
| 241 | return -1; | ||
| 242 | } | ||
| 243 | |||
| 244 | size = 10; | ||
| 245 | if (socks_req[3] == 3) | ||
| 246 | size = 5 + socks_req[4] + 2; | ||
| 247 | else if (socks_req[3] == 4) | ||
| 248 | size = 4 + 16 + 2; | ||
| 249 | if (size > 10) { | ||
| 250 | size -= 10; | ||
| 251 | if (sock_read(sock, (gchar *)socks_req + 10, size) != size) { | ||
| 252 | g_warning("socks5_connect: SOCKS5 connect request response read failed"); | ||
| 253 | return -1; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | /* replace sock->hostname with endpoint */ | ||
| 258 | if (sock->hostname != hostname) { | ||
| 259 | g_free(sock->hostname); | ||
| 260 | sock->hostname = g_strdup(hostname); | ||
| 261 | sock->port = port; | ||
| 262 | } | ||
| 263 | |||
| 264 | debug_print("socks5_connect: SOCKS5 connection to %s:%u successful.\n", hostname, port); | ||
| 265 | |||
| 266 | return 0; | ||
| 267 | } | ||
diff --git a/libsylph/socks.h b/libsylph/socks.h new file mode 100644 index 0000000..afa34c5 --- /dev/null +++ b/libsylph/socks.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2010 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SOCKS_H__ | ||
| 21 | #define __SOCKS_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | |||
| 29 | #include "socket.h" | ||
| 30 | |||
| 31 | typedef struct _SocksInfo SocksInfo; | ||
| 32 | |||
| 33 | typedef enum { | ||
| 34 | SOCKS_SOCKS4, | ||
| 35 | SOCKS_SOCKS5 | ||
| 36 | } SocksType; | ||
| 37 | |||
| 38 | struct _SocksInfo | ||
| 39 | { | ||
| 40 | SocksType type; | ||
| 41 | gchar *proxy_host; | ||
| 42 | gushort proxy_port; | ||
| 43 | gchar *proxy_name; | ||
| 44 | gchar *proxy_pass; | ||
| 45 | }; | ||
| 46 | |||
| 47 | SocksInfo *socks_info_new(SocksType type, const gchar *proxy_host, | ||
| 48 | gushort proxy_port, const gchar *proxy_name, | ||
| 49 | const gchar *proxy_pass); | ||
| 50 | void socks_info_free(SocksInfo *socks_info); | ||
| 51 | |||
| 52 | gint socks_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 53 | SocksInfo *socks_info); | ||
| 54 | |||
| 55 | gint socks4_connect(SockInfo *sock, const gchar *hostname, gushort port); | ||
| 56 | gint socks5_connect(SockInfo *sock, const gchar *hostname, gushort port, | ||
| 57 | const gchar *proxy_name, const gchar *proxy_pass); | ||
| 58 | |||
| 59 | #endif /* __SOCKS_H__ */ | ||
diff --git a/libsylph/ssl.c b/libsylph/ssl.c new file mode 100644 index 0000000..8413925 --- /dev/null +++ b/libsylph/ssl.c | |||
| @@ -0,0 +1,405 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2014 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #if USE_SSL | ||
| 25 | |||
| 26 | #include "defs.h" | ||
| 27 | |||
| 28 | #include <glib.h> | ||
| 29 | #include <glib/gi18n.h> | ||
| 30 | |||
| 31 | #include "utils.h" | ||
| 32 | #include "ssl.h" | ||
| 33 | #include "ssl_hostname_validation.h" | ||
| 34 | |||
| 35 | static SSL_CTX *ssl_ctx_SSLv23 = NULL; | ||
| 36 | static SSL_CTX *ssl_ctx_TLSv1 = NULL; | ||
| 37 | |||
| 38 | static GSList *trust_list = NULL; | ||
| 39 | static GSList *tmp_trust_list = NULL; | ||
| 40 | static GSList *reject_list = NULL; | ||
| 41 | |||
| 42 | static SSLVerifyFunc verify_ui_func = NULL; | ||
| 43 | |||
| 44 | static gchar *find_certs_file(const gchar *certs_dir) | ||
| 45 | { | ||
| 46 | gchar *certs_file; | ||
| 47 | |||
| 48 | #define LOOK_FOR(crt) \ | ||
| 49 | { \ | ||
| 50 | certs_file = g_strconcat(certs_dir, G_DIR_SEPARATOR_S, crt, NULL); \ | ||
| 51 | debug_print("looking for %s\n", certs_file); \ | ||
| 52 | if (is_file_exist(certs_file)) \ | ||
| 53 | return certs_file; \ | ||
| 54 | g_free(certs_file); \ | ||
| 55 | } | ||
| 56 | |||
| 57 | if (certs_dir) { | ||
| 58 | LOOK_FOR("ca-certificates.crt"); | ||
| 59 | LOOK_FOR("ca-bundle.crt"); | ||
| 60 | LOOK_FOR("ca-root.crt"); | ||
| 61 | LOOK_FOR("certs.crt"); | ||
| 62 | LOOK_FOR("cert.pem"); | ||
| 63 | } | ||
| 64 | |||
| 65 | #undef LOOK_FOR | ||
| 66 | |||
| 67 | return NULL; | ||
| 68 | } | ||
| 69 | |||
| 70 | void ssl_init(void) | ||
| 71 | { | ||
| 72 | gchar *certs_file, *certs_dir; | ||
| 73 | FILE *fp; | ||
| 74 | |||
| 75 | SSL_library_init(); | ||
| 76 | SSL_load_error_strings(); | ||
| 77 | |||
| 78 | certs_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "certs", NULL); | ||
| 79 | if (!is_dir_exist(certs_dir)) { | ||
| 80 | debug_print("ssl_init(): %s doesn't exist, or not a directory.\n", | ||
| 81 | certs_dir); | ||
| 82 | g_free(certs_dir); | ||
| 83 | #ifdef G_OS_WIN32 | ||
| 84 | certs_dir = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S | ||
| 85 | "etc" G_DIR_SEPARATOR_S | ||
| 86 | "ssl" G_DIR_SEPARATOR_S "certs", NULL); | ||
| 87 | #else | ||
| 88 | certs_dir = g_strdup("/etc/ssl/certs"); | ||
| 89 | #endif | ||
| 90 | if (!is_dir_exist(certs_dir)) { | ||
| 91 | debug_print("ssl_init(): %s doesn't exist, or not a directory.\n", | ||
| 92 | certs_dir); | ||
| 93 | g_free(certs_dir); | ||
| 94 | certs_dir = NULL; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | if (certs_dir) | ||
| 98 | debug_print("ssl_init(): certs dir %s found.\n", certs_dir); | ||
| 99 | |||
| 100 | certs_file = find_certs_file(get_rc_dir()); | ||
| 101 | |||
| 102 | if (certs_dir && !certs_file) | ||
| 103 | certs_file = find_certs_file(certs_dir); | ||
| 104 | |||
| 105 | if (!certs_file) { | ||
| 106 | #ifdef G_OS_WIN32 | ||
| 107 | certs_dir = g_strconcat(get_startup_dir(), | ||
| 108 | G_DIR_SEPARATOR_S "etc" | ||
| 109 | G_DIR_SEPARATOR_S "ssl", NULL); | ||
| 110 | certs_file = find_certs_file(certs_dir); | ||
| 111 | g_free(certs_dir); | ||
| 112 | certs_dir = NULL; | ||
| 113 | if (!certs_file) { | ||
| 114 | certs_dir = g_strconcat(get_startup_dir(), | ||
| 115 | G_DIR_SEPARATOR_S "etc", NULL); | ||
| 116 | certs_file = find_certs_file(certs_dir); | ||
| 117 | g_free(certs_dir); | ||
| 118 | certs_dir = NULL; | ||
| 119 | } | ||
| 120 | #else | ||
| 121 | certs_file = find_certs_file("/etc/ssl"); | ||
| 122 | if (!certs_file) | ||
| 123 | certs_file = find_certs_file("/etc"); | ||
| 124 | #endif | ||
| 125 | } | ||
| 126 | |||
| 127 | if (certs_file) | ||
| 128 | debug_print("ssl_init(): certs file %s found.\n", certs_file); | ||
| 129 | |||
| 130 | ssl_ctx_SSLv23 = SSL_CTX_new(SSLv23_client_method()); | ||
| 131 | if (ssl_ctx_SSLv23 == NULL) { | ||
| 132 | debug_print(_("SSLv23 not available\n")); | ||
| 133 | } else { | ||
| 134 | debug_print(_("SSLv23 available\n")); | ||
| 135 | if ((certs_file || certs_dir) && | ||
| 136 | !SSL_CTX_load_verify_locations(ssl_ctx_SSLv23, certs_file, | ||
| 137 | certs_dir)) | ||
| 138 | g_warning("SSLv23 SSL_CTX_load_verify_locations failed.\n"); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* ssl_ctx_TLSv1 = SSL_CTX_new(TLSv1_client_method()); */ | ||
| 142 | ssl_ctx_TLSv1 = SSL_CTX_new(SSLv23_client_method()); | ||
| 143 | if (ssl_ctx_TLSv1 == NULL) { | ||
| 144 | debug_print(_("TLSv1 not available\n")); | ||
| 145 | } else { | ||
| 146 | debug_print(_("TLSv1 available\n")); | ||
| 147 | /* disable SSLv2/SSLv3 */ | ||
| 148 | SSL_CTX_set_options(ssl_ctx_TLSv1, | ||
| 149 | SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); | ||
| 150 | if ((certs_file || certs_dir) && | ||
| 151 | !SSL_CTX_load_verify_locations(ssl_ctx_TLSv1, certs_file, | ||
| 152 | certs_dir)) | ||
| 153 | g_warning("TLSv1 SSL_CTX_load_verify_locations failed.\n"); | ||
| 154 | } | ||
| 155 | |||
| 156 | g_free(certs_dir); | ||
| 157 | g_free(certs_file); | ||
| 158 | |||
| 159 | certs_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "trust.crt", | ||
| 160 | NULL); | ||
| 161 | if ((fp = g_fopen(certs_file, "rb")) != NULL) { | ||
| 162 | X509 *cert; | ||
| 163 | |||
| 164 | debug_print("ssl_init(): reading trust.crt\n"); | ||
| 165 | |||
| 166 | while ((cert = PEM_read_X509(fp, NULL, NULL, NULL)) != NULL) | ||
| 167 | trust_list = g_slist_append(trust_list, cert); | ||
| 168 | fclose(fp); | ||
| 169 | } | ||
| 170 | g_free(certs_file); | ||
| 171 | } | ||
| 172 | |||
| 173 | void ssl_done(void) | ||
| 174 | { | ||
| 175 | gchar *trust_file; | ||
| 176 | GSList *cur; | ||
| 177 | FILE *fp; | ||
| 178 | |||
| 179 | if (trust_list) { | ||
| 180 | trust_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 181 | "trust.crt", NULL); | ||
| 182 | if ((fp = g_fopen(trust_file, "wb")) == NULL) { | ||
| 183 | FILE_OP_ERROR(trust_file, "fopen"); | ||
| 184 | } | ||
| 185 | for (cur = trust_list; cur != NULL; cur = cur->next) { | ||
| 186 | if (fp && !PEM_write_X509(fp, (X509 *)cur->data)) | ||
| 187 | g_warning("can't write X509 to PEM file: %s", | ||
| 188 | trust_file); | ||
| 189 | X509_free((X509 *)cur->data); | ||
| 190 | } | ||
| 191 | if (fp) | ||
| 192 | fclose(fp); | ||
| 193 | g_free(trust_file); | ||
| 194 | g_slist_free(trust_list); | ||
| 195 | trust_list = NULL; | ||
| 196 | } | ||
| 197 | for (cur = tmp_trust_list; cur != NULL; cur = cur->next) | ||
| 198 | X509_free((X509 *)cur->data); | ||
| 199 | g_slist_free(tmp_trust_list); | ||
| 200 | tmp_trust_list = NULL; | ||
| 201 | for (cur = reject_list; cur != NULL; cur = cur->next) | ||
| 202 | X509_free((X509 *)cur->data); | ||
| 203 | g_slist_free(reject_list); | ||
| 204 | reject_list = NULL; | ||
| 205 | |||
| 206 | if (ssl_ctx_SSLv23) { | ||
| 207 | SSL_CTX_free(ssl_ctx_SSLv23); | ||
| 208 | ssl_ctx_SSLv23 = NULL; | ||
| 209 | } | ||
| 210 | |||
| 211 | if (ssl_ctx_TLSv1) { | ||
| 212 | SSL_CTX_free(ssl_ctx_TLSv1); | ||
| 213 | ssl_ctx_TLSv1 = NULL; | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | gboolean ssl_init_socket(SockInfo *sockinfo) | ||
| 218 | { | ||
| 219 | return ssl_init_socket_with_method(sockinfo, SSL_METHOD_SSLv23); | ||
| 220 | } | ||
| 221 | |||
| 222 | static gint x509_cmp_func(gconstpointer a, gconstpointer b) | ||
| 223 | { | ||
| 224 | const X509 *xa = a; | ||
| 225 | const X509 *xb = b; | ||
| 226 | |||
| 227 | return X509_cmp(xa, xb); | ||
| 228 | } | ||
| 229 | |||
| 230 | gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method) | ||
| 231 | { | ||
| 232 | X509 *server_cert; | ||
| 233 | gint err, ret; | ||
| 234 | |||
| 235 | switch (method) { | ||
| 236 | case SSL_METHOD_SSLv23: | ||
| 237 | if (!ssl_ctx_SSLv23) { | ||
| 238 | g_warning(_("SSL method not available\n")); | ||
| 239 | return FALSE; | ||
| 240 | } | ||
| 241 | sockinfo->ssl = SSL_new(ssl_ctx_SSLv23); | ||
| 242 | break; | ||
| 243 | case SSL_METHOD_TLSv1: | ||
| 244 | if (!ssl_ctx_TLSv1) { | ||
| 245 | g_warning(_("SSL method not available\n")); | ||
| 246 | return FALSE; | ||
| 247 | } | ||
| 248 | sockinfo->ssl = SSL_new(ssl_ctx_TLSv1); | ||
| 249 | break; | ||
| 250 | default: | ||
| 251 | g_warning(_("Unknown SSL method *PROGRAM BUG*\n")); | ||
| 252 | return FALSE; | ||
| 253 | break; | ||
| 254 | } | ||
| 255 | |||
| 256 | if (sockinfo->ssl == NULL) { | ||
| 257 | g_warning(_("Error creating ssl context\n")); | ||
| 258 | return FALSE; | ||
| 259 | } | ||
| 260 | |||
| 261 | SSL_set_fd(sockinfo->ssl, sockinfo->sock); | ||
| 262 | while ((ret = SSL_connect(sockinfo->ssl)) != 1) { | ||
| 263 | err = SSL_get_error(sockinfo->ssl, ret); | ||
| 264 | if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { | ||
| 265 | g_usleep(100000); | ||
| 266 | g_warning("SSL_connect(): try again\n"); | ||
| 267 | continue; | ||
| 268 | } | ||
| 269 | g_warning("SSL_connect() failed with error %d, ret = %d (%s)\n", | ||
| 270 | err, ret, ERR_error_string(ERR_get_error(), NULL)); | ||
| 271 | return FALSE; | ||
| 272 | } | ||
| 273 | |||
| 274 | /* Get the cipher */ | ||
| 275 | |||
| 276 | debug_print(_("SSL connection using %s\n"), | ||
| 277 | SSL_get_cipher(sockinfo->ssl)); | ||
| 278 | debug_print("SSL protocol version: %s\n", | ||
| 279 | SSL_get_version(sockinfo->ssl)); | ||
| 280 | |||
| 281 | /* Get server's certificate (note: beware of dynamic allocation) */ | ||
| 282 | |||
| 283 | if ((server_cert = SSL_get_peer_certificate(sockinfo->ssl)) != NULL) { | ||
| 284 | glong verify_result; | ||
| 285 | gboolean expired = FALSE; | ||
| 286 | |||
| 287 | if (get_debug_mode()) { | ||
| 288 | gchar *str; | ||
| 289 | guchar keyid[EVP_MAX_MD_SIZE]; | ||
| 290 | gchar keyidstr[EVP_MAX_MD_SIZE * 3 + 1] = ""; | ||
| 291 | guint keyidlen = 0; | ||
| 292 | gint i; | ||
| 293 | |||
| 294 | debug_print(_("Server certificate:\n")); | ||
| 295 | |||
| 296 | if ((str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0)) != NULL) { | ||
| 297 | debug_print(_(" Subject: %s\n"), str); | ||
| 298 | OPENSSL_free(str); | ||
| 299 | } | ||
| 300 | |||
| 301 | if ((str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0)) != NULL) { | ||
| 302 | debug_print(_(" Issuer: %s\n"), str); | ||
| 303 | OPENSSL_free(str); | ||
| 304 | } | ||
| 305 | if (X509_digest(server_cert, EVP_sha1(), keyid, &keyidlen)) { | ||
| 306 | for (i = 0; i < keyidlen; i++) | ||
| 307 | g_snprintf(keyidstr + i * 3, 4, "%02x:", keyid[i]); | ||
| 308 | keyidstr[keyidlen * 3 - 1] = '\0'; | ||
| 309 | debug_print(" SHA1 fingerprint: %s\n", keyidstr); | ||
| 310 | } | ||
| 311 | if (X509_digest(server_cert, EVP_md5(), keyid, &keyidlen)) { | ||
| 312 | for (i = 0; i < keyidlen; i++) | ||
| 313 | g_snprintf(keyidstr + i * 3, 4, "%02x:", keyid[i]); | ||
| 314 | keyidstr[keyidlen * 3 - 1] = '\0'; | ||
| 315 | debug_print(" MD5 fingerprint: %s\n", keyidstr); | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | verify_result = SSL_get_verify_result(sockinfo->ssl); | ||
| 320 | if (verify_result == X509_V_OK) { | ||
| 321 | debug_print("SSL certificate verify OK\n"); | ||
| 322 | if (ssl_validate_hostname(sockinfo->hostname, server_cert) == SSL_HOSTNAME_MATCH_FOUND) { | ||
| 323 | debug_print("SSL certificate hostname validation OK\n"); | ||
| 324 | X509_free(server_cert); | ||
| 325 | return TRUE; | ||
| 326 | } else { | ||
| 327 | verify_result = X509_V_ERR_APPLICATION_VERIFICATION; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) { | ||
| 332 | log_message("SSL certificate of %s has expired\n", sockinfo->hostname); | ||
| 333 | expired = TRUE; | ||
| 334 | } else if (g_slist_find_custom(trust_list, server_cert, | ||
| 335 | x509_cmp_func) || | ||
| 336 | g_slist_find_custom(tmp_trust_list, server_cert, | ||
| 337 | x509_cmp_func)) { | ||
| 338 | log_message("SSL certificate of %s previously accepted\n", sockinfo->hostname); | ||
| 339 | X509_free(server_cert); | ||
| 340 | return TRUE; | ||
| 341 | } else if (g_slist_find_custom(reject_list, server_cert, | ||
| 342 | x509_cmp_func)) { | ||
| 343 | log_message("SSL certificate of %s previously rejected\n", sockinfo->hostname); | ||
| 344 | X509_free(server_cert); | ||
| 345 | return FALSE; | ||
| 346 | } | ||
| 347 | |||
| 348 | if (verify_result == X509_V_ERR_APPLICATION_VERIFICATION) { | ||
| 349 | g_warning("%s: SSL hostname validation failed\n", | ||
| 350 | sockinfo->hostname); | ||
| 351 | } else { | ||
| 352 | g_warning("%s: SSL certificate verify failed (%ld: %s)\n", | ||
| 353 | sockinfo->hostname, verify_result, | ||
| 354 | X509_verify_cert_error_string(verify_result)); | ||
| 355 | } | ||
| 356 | |||
| 357 | if (verify_ui_func) { | ||
| 358 | gint res; | ||
| 359 | |||
| 360 | res = verify_ui_func(sockinfo, sockinfo->hostname, | ||
| 361 | server_cert, verify_result); | ||
| 362 | /* 0: accept 1: temporarily accept -1: reject */ | ||
| 363 | if (res < 0) { | ||
| 364 | debug_print("SSL certificate of %s rejected\n", | ||
| 365 | sockinfo->hostname); | ||
| 366 | #if 0 | ||
| 367 | reject_list = g_slist_prepend | ||
| 368 | (reject_list, X509_dup(server_cert)); | ||
| 369 | #endif | ||
| 370 | X509_free(server_cert); | ||
| 371 | return FALSE; | ||
| 372 | } else if (res > 0) { | ||
| 373 | debug_print("Temporarily accept SSL certificate of %s\n", sockinfo->hostname); | ||
| 374 | if (!expired) | ||
| 375 | tmp_trust_list = g_slist_prepend(tmp_trust_list, X509_dup(server_cert)); | ||
| 376 | } else { | ||
| 377 | debug_print("Permanently accept SSL certificate of %s\n", sockinfo->hostname); | ||
| 378 | if (!expired) | ||
| 379 | trust_list = g_slist_prepend(trust_list, X509_dup(server_cert)); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | X509_free(server_cert); | ||
| 384 | } else { | ||
| 385 | g_warning("%s: couldn't get SSL certificate\n", | ||
| 386 | sockinfo->hostname); | ||
| 387 | return FALSE; | ||
| 388 | } | ||
| 389 | |||
| 390 | return TRUE; | ||
| 391 | } | ||
| 392 | |||
| 393 | void ssl_done_socket(SockInfo *sockinfo) | ||
| 394 | { | ||
| 395 | if (sockinfo->ssl) { | ||
| 396 | SSL_free(sockinfo->ssl); | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | void ssl_set_verify_func(SSLVerifyFunc func) | ||
| 401 | { | ||
| 402 | verify_ui_func = func; | ||
| 403 | } | ||
| 404 | |||
| 405 | #endif /* USE_SSL */ | ||
diff --git a/libsylph/ssl.h b/libsylph/ssl.h new file mode 100644 index 0000000..a9f690d --- /dev/null +++ b/libsylph/ssl.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2006 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SSL_H__ | ||
| 21 | #define __SSL_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #if USE_SSL | ||
| 28 | |||
| 29 | #include <glib.h> | ||
| 30 | #include <openssl/crypto.h> | ||
| 31 | #include <openssl/x509.h> | ||
| 32 | #include <openssl/pem.h> | ||
| 33 | #include <openssl/ssl.h> | ||
| 34 | #include <openssl/err.h> | ||
| 35 | |||
| 36 | #include "socket.h" | ||
| 37 | |||
| 38 | typedef enum { | ||
| 39 | SSL_METHOD_SSLv23, | ||
| 40 | SSL_METHOD_TLSv1 | ||
| 41 | } SSLMethod; | ||
| 42 | |||
| 43 | typedef enum { | ||
| 44 | SSL_NONE, | ||
| 45 | SSL_TUNNEL, | ||
| 46 | SSL_STARTTLS | ||
| 47 | } SSLType; | ||
| 48 | |||
| 49 | typedef gint (*SSLVerifyFunc) (SockInfo *sockinfo, | ||
| 50 | const gchar *hostname, | ||
| 51 | X509 *server_cert, | ||
| 52 | glong verify_result); | ||
| 53 | |||
| 54 | void ssl_init (void); | ||
| 55 | void ssl_done (void); | ||
| 56 | gboolean ssl_init_socket (SockInfo *sockinfo); | ||
| 57 | gboolean ssl_init_socket_with_method (SockInfo *sockinfo, | ||
| 58 | SSLMethod method); | ||
| 59 | void ssl_done_socket (SockInfo *sockinfo); | ||
| 60 | |||
| 61 | void ssl_set_verify_func (SSLVerifyFunc func); | ||
| 62 | |||
| 63 | #endif /* USE_SSL */ | ||
| 64 | |||
| 65 | #endif /* __SSL_H__ */ | ||
diff --git a/libsylph/ssl_hostname_validation.c b/libsylph/ssl_hostname_validation.c new file mode 100644 index 0000000..cb2cea9 --- /dev/null +++ b/libsylph/ssl_hostname_validation.c | |||
| @@ -0,0 +1,282 @@ | |||
| 1 | /* | ||
| 2 | * Helper functions to perform basic hostname validation using OpenSSL. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012, iSEC Partners. | ||
| 5 | * | ||
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 7 | * this software and associated documentation files (the "Software"), to deal in | ||
| 8 | * the Software without restriction, including without limitation the rights to | ||
| 9 | + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 10 | * of the Software, and to permit persons to whom the Software is furnished to do | ||
| 11 | + so, subject to the following conditions: | ||
| 12 | + | ||
| 13 | + The above copyright notice and this permission notice shall be included in all | ||
| 14 | * copies or substantial portions of the Software. | ||
| 15 | * | ||
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 22 | * SOFTWARE. | ||
| 23 | * | ||
| 24 | * Author: Alban Diquet | ||
| 25 | * | ||
| 26 | * https://github.com/iSECPartners/ssl-conservatory | ||
| 27 | * | ||
| 28 | * Modified naming convention to match LibSylph. | ||
| 29 | * | ||
| 30 | */ | ||
| 31 | |||
| 32 | #ifdef HAVE_CONFIG_H | ||
| 33 | # include "config.h" | ||
| 34 | #endif | ||
| 35 | |||
| 36 | #if USE_SSL | ||
| 37 | |||
| 38 | #include <glib.h> | ||
| 39 | |||
| 40 | #include <strings.h> | ||
| 41 | #include <openssl/x509v3.h> | ||
| 42 | #include <openssl/ssl.h> | ||
| 43 | |||
| 44 | #include "utils.h" | ||
| 45 | #include "ssl_hostname_validation.h" | ||
| 46 | |||
| 47 | |||
| 48 | #define HOSTNAME_MAX_SIZE 255 | ||
| 49 | |||
| 50 | |||
| 51 | /* The following host_match() function is based on cURL/libcurl code. */ | ||
| 52 | |||
| 53 | /*************************************************************************** | ||
| 54 | * _ _ ____ _ | ||
| 55 | * Project ___| | | | _ \| | | ||
| 56 | * / __| | | | |_) | | | ||
| 57 | * | (__| |_| | _ <| |___ | ||
| 58 | * \___|\___/|_| \_\_____| | ||
| 59 | * | ||
| 60 | * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. | ||
| 61 | * | ||
| 62 | * This software is licensed as described in the file COPYING, which | ||
| 63 | * you should have received as part of this distribution. The terms | ||
| 64 | * are also available at http://curl.haxx.se/docs/copyright.html. | ||
| 65 | * | ||
| 66 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell | ||
| 67 | * copies of the Software, and permit persons to whom the Software is | ||
| 68 | * furnished to do so, under the terms of the COPYING file. | ||
| 69 | * | ||
| 70 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
| 71 | * KIND, either express or implied. | ||
| 72 | * | ||
| 73 | ***************************************************************************/ | ||
| 74 | |||
| 75 | /*************************************************************************** | ||
| 76 | * | ||
| 77 | * COPYRIGHT AND PERMISSION NOTICE | ||
| 78 | * | ||
| 79 | * Copyright (c) 1996 - 2014, Daniel Stenberg, <daniel@haxx.se>. | ||
| 80 | * | ||
| 81 | * All rights reserved. | ||
| 82 | * | ||
| 83 | * Permission to use, copy, modify, and distribute this software for any purpose | ||
| 84 | * with or without fee is hereby granted, provided that the above copyright | ||
| 85 | * notice and this permission notice appear in all copies. | ||
| 86 | * | ||
| 87 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 88 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 89 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN | ||
| 90 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||
| 91 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
| 92 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE | ||
| 93 | * OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 94 | * | ||
| 95 | * Except as contained in this notice, the name of a copyright holder shall not | ||
| 96 | * be used in advertising or otherwise to promote the sale, use or other dealings | ||
| 97 | * in this Software without prior written authorization of the copyright holder. | ||
| 98 | * | ||
| 99 | ***************************************************************************/ | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Match a hostname against a wildcard pattern. | ||
| 103 | * E.g. | ||
| 104 | * "foo.host.com" matches "*.host.com". | ||
| 105 | * | ||
| 106 | * We use the matching rule described in RFC6125, section 6.4.3. | ||
| 107 | * http://tools.ietf.org/html/rfc6125#section-6.4.3 | ||
| 108 | */ | ||
| 109 | |||
| 110 | static int host_match(const char *hostname, const char *pattern) | ||
| 111 | { | ||
| 112 | const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; | ||
| 113 | size_t prefixlen, suffixlen; | ||
| 114 | |||
| 115 | if (!pattern || !*pattern || !hostname || !*hostname) | ||
| 116 | return SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 117 | |||
| 118 | /* trivial case */ | ||
| 119 | if (g_ascii_strcasecmp(pattern, hostname) == 0) | ||
| 120 | return SSL_HOSTNAME_MATCH_FOUND; | ||
| 121 | |||
| 122 | pattern_wildcard = strchr(pattern, '*'); | ||
| 123 | if (pattern_wildcard == NULL) { | ||
| 124 | return g_ascii_strcasecmp(pattern, hostname) == 0 ? | ||
| 125 | SSL_HOSTNAME_MATCH_FOUND : SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* We require at least 2 dots in pattern to avoid too wide wildcard | ||
| 129 | match. */ | ||
| 130 | pattern_label_end = strchr(pattern, '.'); | ||
| 131 | if (pattern_label_end == NULL || | ||
| 132 | strchr(pattern_label_end + 1, '.') == NULL || | ||
| 133 | pattern_wildcard > pattern_label_end || | ||
| 134 | g_ascii_strncasecmp(pattern, "xn--", 4) == 0) { | ||
| 135 | return g_ascii_strcasecmp(pattern, hostname) == 0 ? | ||
| 136 | SSL_HOSTNAME_MATCH_FOUND : SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 137 | } | ||
| 138 | |||
| 139 | hostname_label_end = strchr(hostname, '.'); | ||
| 140 | if (hostname_label_end == NULL || | ||
| 141 | g_ascii_strcasecmp(pattern_label_end, hostname_label_end) != 0) | ||
| 142 | return SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 143 | |||
| 144 | /* The wildcard must match at least one character, so the left-most | ||
| 145 | label of the hostname is at least as large as the left-most label | ||
| 146 | of the pattern. */ | ||
| 147 | if (hostname_label_end - hostname < pattern_label_end - pattern) | ||
| 148 | return SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 149 | |||
| 150 | prefixlen = pattern_wildcard - pattern; | ||
| 151 | suffixlen = pattern_label_end - (pattern_wildcard + 1); | ||
| 152 | return g_ascii_strncasecmp(pattern, hostname, prefixlen) == 0 && | ||
| 153 | g_ascii_strncasecmp(pattern_wildcard + 1, hostname_label_end - suffixlen, suffixlen) == 0 ? | ||
| 154 | SSL_HOSTNAME_MATCH_FOUND : SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 155 | } | ||
| 156 | |||
| 157 | |||
| 158 | /** | ||
| 159 | * Tries to find a match for hostname in the certificate's Common Name field. | ||
| 160 | * | ||
| 161 | * Returns MatchFound if a match was found. | ||
| 162 | * Returns MatchNotFound if no matches were found. | ||
| 163 | * Returns MalformedCertificate if the Common Name had a NUL character embedded in it. | ||
| 164 | * Returns Error if the Common Name could not be extracted. | ||
| 165 | */ | ||
| 166 | static SSLHostnameValidationResult matches_common_name(const char *hostname, const X509 *server_cert) { | ||
| 167 | int common_name_loc = -1; | ||
| 168 | X509_NAME_ENTRY *common_name_entry = NULL; | ||
| 169 | ASN1_STRING *common_name_asn1 = NULL; | ||
| 170 | char *common_name_str = NULL; | ||
| 171 | |||
| 172 | // Find the position of the CN field in the Subject field of the certificate | ||
| 173 | common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1); | ||
| 174 | if (common_name_loc < 0) { | ||
| 175 | return SSL_HOSTNAME_ERROR; | ||
| 176 | } | ||
| 177 | |||
| 178 | // Extract the CN field | ||
| 179 | common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc); | ||
| 180 | if (common_name_entry == NULL) { | ||
| 181 | return SSL_HOSTNAME_ERROR; | ||
| 182 | } | ||
| 183 | |||
| 184 | // Convert the CN field to a C string | ||
| 185 | common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); | ||
| 186 | if (common_name_asn1 == NULL) { | ||
| 187 | return SSL_HOSTNAME_ERROR; | ||
| 188 | } | ||
| 189 | common_name_str = (char *) ASN1_STRING_data(common_name_asn1); | ||
| 190 | |||
| 191 | debug_print("matches_common_name: %s\n", common_name_str); | ||
| 192 | |||
| 193 | // Make sure there isn't an embedded NUL character in the CN | ||
| 194 | if (ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { | ||
| 195 | return SSL_HOSTNAME_MALFORMED_CERTIFICATE; | ||
| 196 | } | ||
| 197 | |||
| 198 | // Compare expected hostname with the CN | ||
| 199 | return host_match(hostname, common_name_str); | ||
| 200 | } | ||
| 201 | |||
| 202 | |||
| 203 | /** | ||
| 204 | * Tries to find a match for hostname in the certificate's Subject Alternative Name extension. | ||
| 205 | * | ||
| 206 | * Returns MatchFound if a match was found. | ||
| 207 | * Returns MatchNotFound if no matches were found. | ||
| 208 | * Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. | ||
| 209 | * Returns NoSANPresent if the SAN extension was not present in the certificate. | ||
| 210 | */ | ||
| 211 | static SSLHostnameValidationResult matches_subject_alternative_name(const char *hostname, const X509 *server_cert) { | ||
| 212 | SSLHostnameValidationResult result = SSL_HOSTNAME_MATCH_NOT_FOUND; | ||
| 213 | int i; | ||
| 214 | int san_names_nb = -1; | ||
| 215 | STACK_OF(GENERAL_NAME) *san_names = NULL; | ||
| 216 | |||
| 217 | // Try to extract the names within the SAN extension from the certificate | ||
| 218 | san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL); | ||
| 219 | if (san_names == NULL) { | ||
| 220 | return SSL_HOSTNAME_NO_SAN_PRESENT; | ||
| 221 | } | ||
| 222 | san_names_nb = sk_GENERAL_NAME_num(san_names); | ||
| 223 | |||
| 224 | // Check each name within the extension | ||
| 225 | for (i=0; i<san_names_nb; i++) { | ||
| 226 | const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); | ||
| 227 | |||
| 228 | if (current_name->type == GEN_DNS) { | ||
| 229 | // Current name is a DNS name, let's check it | ||
| 230 | char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); | ||
| 231 | |||
| 232 | debug_print("matches_subject_alternative_name: %s\n", dns_name); | ||
| 233 | |||
| 234 | // Make sure there isn't an embedded NUL character in the DNS name | ||
| 235 | if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { | ||
| 236 | result = SSL_HOSTNAME_MALFORMED_CERTIFICATE; | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | else { // Compare expected hostname with the DNS name | ||
| 240 | if (host_match(hostname, dns_name) == SSL_HOSTNAME_MATCH_FOUND) { | ||
| 241 | result = SSL_HOSTNAME_MATCH_FOUND; | ||
| 242 | break; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); | ||
| 248 | |||
| 249 | return result; | ||
| 250 | } | ||
| 251 | |||
| 252 | |||
| 253 | /** | ||
| 254 | * Validates the server's identity by looking for the expected hostname in the | ||
| 255 | * server's certificate. As described in RFC 6125, it first tries to find a match | ||
| 256 | * in the Subject Alternative Name extension. If the extension is not present in | ||
| 257 | * the certificate, it checks the Common Name instead. | ||
| 258 | * | ||
| 259 | * Returns MatchFound if a match was found. | ||
| 260 | * Returns MatchNotFound if no matches were found. | ||
| 261 | * Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. | ||
| 262 | * Returns Error if there was an error. | ||
| 263 | */ | ||
| 264 | SSLHostnameValidationResult ssl_validate_hostname(const char *hostname, const X509 *server_cert) { | ||
| 265 | SSLHostnameValidationResult result; | ||
| 266 | |||
| 267 | debug_print("ssl_validate_hostname: validating hostname: %s\n", hostname); | ||
| 268 | |||
| 269 | if((hostname == NULL) || (server_cert == NULL)) | ||
| 270 | return SSL_HOSTNAME_ERROR; | ||
| 271 | |||
| 272 | // First try the Subject Alternative Names extension | ||
| 273 | result = matches_subject_alternative_name(hostname, server_cert); | ||
| 274 | if (result == SSL_HOSTNAME_NO_SAN_PRESENT) { | ||
| 275 | // Extension was not found: try the Common Name | ||
| 276 | result = matches_common_name(hostname, server_cert); | ||
| 277 | } | ||
| 278 | |||
| 279 | return result; | ||
| 280 | } | ||
| 281 | |||
| 282 | #endif /* USE_SSL */ | ||
diff --git a/libsylph/ssl_hostname_validation.h b/libsylph/ssl_hostname_validation.h new file mode 100644 index 0000000..4a24ae3 --- /dev/null +++ b/libsylph/ssl_hostname_validation.h | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | /* | ||
| 2 | * Helper functions to perform basic hostname validation using OpenSSL. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012, iSEC Partners. | ||
| 5 | * | ||
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| 7 | * this software and associated documentation files (the "Software"), to deal in | ||
| 8 | * the Software without restriction, including without limitation the rights to | ||
| 9 | + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
| 10 | * of the Software, and to permit persons to whom the Software is furnished to do | ||
| 11 | + so, subject to the following conditions: | ||
| 12 | + | ||
| 13 | + The above copyright notice and this permission notice shall be included in all | ||
| 14 | * copies or substantial portions of the Software. | ||
| 15 | * | ||
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 22 | * SOFTWARE. | ||
| 23 | * | ||
| 24 | * Author: Alban Diquet | ||
| 25 | * | ||
| 26 | * https://github.com/iSECPartners/ssl-conservatory | ||
| 27 | * | ||
| 28 | * Modified naming convention to match LibSylph. | ||
| 29 | * | ||
| 30 | */ | ||
| 31 | |||
| 32 | #ifndef __SSL_HOSTNAME_VALIDATION_H__ | ||
| 33 | #define __SSL_HOSTNAME_VALIDATION_H__ | ||
| 34 | |||
| 35 | #ifdef HAVE_CONFIG_H | ||
| 36 | # include "config.h" | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #if USE_SSL | ||
| 40 | |||
| 41 | #include <openssl/x509.h> | ||
| 42 | |||
| 43 | typedef enum { | ||
| 44 | SSL_HOSTNAME_MATCH_FOUND, | ||
| 45 | SSL_HOSTNAME_MATCH_NOT_FOUND, | ||
| 46 | SSL_HOSTNAME_NO_SAN_PRESENT, | ||
| 47 | SSL_HOSTNAME_MALFORMED_CERTIFICATE, | ||
| 48 | SSL_HOSTNAME_ERROR | ||
| 49 | } SSLHostnameValidationResult; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Validates the server's identity by looking for the expected hostname in the | ||
| 53 | * server's certificate. As described in RFC 6125, it first tries to find a match | ||
| 54 | * in the Subject Alternative Name extension. If the extension is not present in | ||
| 55 | * the certificate, it checks the Common Name instead. | ||
| 56 | * | ||
| 57 | * Returns MatchFound if a match was found. | ||
| 58 | * Returns MatchNotFound if no matches were found. | ||
| 59 | * Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. | ||
| 60 | * Returns Error if there was an error. | ||
| 61 | */ | ||
| 62 | SSLHostnameValidationResult ssl_validate_hostname(const char *hostname, const X509 *server_cert); | ||
| 63 | |||
| 64 | #endif /* USE_SSL */ | ||
| 65 | |||
| 66 | #endif /* __SSL_HOSTNAME_VALIDATION_H__ */ | ||
diff --git a/libsylph/stringtable.c b/libsylph/stringtable.c new file mode 100644 index 0000000..dbb1951 --- /dev/null +++ b/libsylph/stringtable.c | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <glib.h> | ||
| 21 | #include <string.h> | ||
| 22 | |||
| 23 | #include "stringtable.h" | ||
| 24 | #include "utils.h" | ||
| 25 | |||
| 26 | /* alfons - hashed string table (I wasn't content with GStringChunk; | ||
| 27 | * can't recall why :-) */ | ||
| 28 | |||
| 29 | #if 0 | ||
| 30 | #define XXX_DEBUG \ | ||
| 31 | debug_print | ||
| 32 | #else | ||
| 33 | #define XXX_DEBUG \ | ||
| 34 | if (0) debug_print | ||
| 35 | #endif | ||
| 36 | |||
| 37 | typedef struct StringEntry_ { | ||
| 38 | gint ref_count; | ||
| 39 | gchar *string; | ||
| 40 | } StringEntry; | ||
| 41 | |||
| 42 | static StringEntry *string_entry_new(const gchar *str) | ||
| 43 | { | ||
| 44 | StringEntry *entry; | ||
| 45 | |||
| 46 | entry = g_new0(StringEntry, 1); | ||
| 47 | entry->ref_count = 1; | ||
| 48 | entry->string = g_strdup(str); | ||
| 49 | return entry; | ||
| 50 | } | ||
| 51 | |||
| 52 | static void string_entry_free(StringEntry *entry) | ||
| 53 | { | ||
| 54 | g_return_if_fail(entry != NULL); | ||
| 55 | |||
| 56 | g_free(entry->string); | ||
| 57 | g_free(entry); | ||
| 58 | } | ||
| 59 | |||
| 60 | StringTable *string_table_new(void) | ||
| 61 | { | ||
| 62 | StringTable *strtable; | ||
| 63 | |||
| 64 | strtable = g_new0(StringTable, 1); | ||
| 65 | g_return_val_if_fail(strtable != NULL, NULL); | ||
| 66 | strtable->hash_table = g_hash_table_new(g_str_hash, g_str_equal); | ||
| 67 | g_return_val_if_fail(strtable->hash_table, NULL); | ||
| 68 | return strtable; | ||
| 69 | } | ||
| 70 | |||
| 71 | gchar *string_table_lookup_string(StringTable *table, const gchar *str) | ||
| 72 | { | ||
| 73 | StringEntry *entry; | ||
| 74 | |||
| 75 | entry = g_hash_table_lookup(table->hash_table, str); | ||
| 76 | |||
| 77 | if (entry) { | ||
| 78 | return entry->string; | ||
| 79 | } else { | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | gchar *string_table_insert_string(StringTable *table, const gchar *str) | ||
| 85 | { | ||
| 86 | StringEntry *entry; | ||
| 87 | |||
| 88 | entry = g_hash_table_lookup(table->hash_table, str); | ||
| 89 | |||
| 90 | if (entry) { | ||
| 91 | entry->ref_count++; | ||
| 92 | XXX_DEBUG ("ref++ for %s (%d)\n", entry->string, | ||
| 93 | entry->ref_count); | ||
| 94 | } else { | ||
| 95 | entry = string_entry_new(str); | ||
| 96 | XXX_DEBUG ("inserting %s\n", str); | ||
| 97 | /* insert entry->string instead of str, since it can be | ||
| 98 | * invalid pointer after this. */ | ||
| 99 | g_hash_table_insert(table->hash_table, entry->string, entry); | ||
| 100 | } | ||
| 101 | |||
| 102 | return entry->string; | ||
| 103 | } | ||
| 104 | |||
| 105 | void string_table_free_string(StringTable *table, const gchar *str) | ||
| 106 | { | ||
| 107 | StringEntry *entry; | ||
| 108 | |||
| 109 | entry = g_hash_table_lookup(table->hash_table, str); | ||
| 110 | |||
| 111 | if (entry) { | ||
| 112 | entry->ref_count--; | ||
| 113 | if (entry->ref_count <= 0) { | ||
| 114 | XXX_DEBUG ("refcount of string %s dropped to zero\n", | ||
| 115 | entry->string); | ||
| 116 | g_hash_table_remove(table->hash_table, str); | ||
| 117 | string_entry_free(entry); | ||
| 118 | } else { | ||
| 119 | XXX_DEBUG ("ref-- for %s (%d)\n", entry->string, | ||
| 120 | entry->ref_count); | ||
| 121 | } | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | static gboolean string_table_remove_for_each_fn(gchar *key, StringEntry *entry, | ||
| 126 | gpointer user_data) | ||
| 127 | { | ||
| 128 | g_return_val_if_fail(key != NULL, TRUE); | ||
| 129 | g_return_val_if_fail(entry != NULL, TRUE); | ||
| 130 | |||
| 131 | string_entry_free(entry); | ||
| 132 | |||
| 133 | return TRUE; | ||
| 134 | } | ||
| 135 | |||
| 136 | void string_table_free(StringTable *table) | ||
| 137 | { | ||
| 138 | g_return_if_fail(table != NULL); | ||
| 139 | g_return_if_fail(table->hash_table != NULL); | ||
| 140 | |||
| 141 | g_hash_table_foreach_remove(table->hash_table, | ||
| 142 | (GHRFunc)string_table_remove_for_each_fn, | ||
| 143 | NULL); | ||
| 144 | g_hash_table_destroy(table->hash_table); | ||
| 145 | g_free(table); | ||
| 146 | } | ||
| 147 | |||
| 148 | static void string_table_stats_for_each_fn(gchar *key, StringEntry *entry, | ||
| 149 | guint *totals) | ||
| 150 | { | ||
| 151 | if (entry->ref_count > 1) { | ||
| 152 | *totals += strlen(key) * (entry->ref_count - 1); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | void string_table_get_stats(StringTable *table) | ||
| 157 | { | ||
| 158 | guint totals = 0; | ||
| 159 | |||
| 160 | g_hash_table_foreach(table->hash_table, | ||
| 161 | (GHFunc)string_table_stats_for_each_fn, &totals); | ||
| 162 | XXX_DEBUG ("TOTAL UNSPILLED %d (%dK)\n", totals, totals / 1024); | ||
| 163 | } | ||
diff --git a/libsylph/stringtable.h b/libsylph/stringtable.h new file mode 100644 index 0000000..6f06d3c --- /dev/null +++ b/libsylph/stringtable.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef STRINGTABLE_H__ | ||
| 21 | #define STRINGTABLE_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | typedef struct { | ||
| 26 | GHashTable *hash_table; | ||
| 27 | } StringTable; | ||
| 28 | |||
| 29 | StringTable *string_table_new (void); | ||
| 30 | void string_table_free (StringTable *table); | ||
| 31 | |||
| 32 | gchar *string_table_lookup_string (StringTable *table, const gchar *str); | ||
| 33 | gchar *string_table_insert_string (StringTable *table, const gchar *str); | ||
| 34 | void string_table_free_string (StringTable *table, const gchar *str); | ||
| 35 | |||
| 36 | void string_table_get_stats (StringTable *table); | ||
| 37 | |||
| 38 | #endif /* STRINGTABLE_H__ */ | ||
diff --git a/libsylph/syl-marshal.c b/libsylph/syl-marshal.c new file mode 100644 index 0000000..3d17749 --- /dev/null +++ b/libsylph/syl-marshal.c | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | |||
| 2 | #include <glib-object.h> | ||
| 3 | |||
| 4 | |||
| 5 | #ifdef G_ENABLE_DEBUG | ||
| 6 | #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) | ||
| 7 | #define g_marshal_value_peek_char(v) g_value_get_char (v) | ||
| 8 | #define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) | ||
| 9 | #define g_marshal_value_peek_int(v) g_value_get_int (v) | ||
| 10 | #define g_marshal_value_peek_uint(v) g_value_get_uint (v) | ||
| 11 | #define g_marshal_value_peek_long(v) g_value_get_long (v) | ||
| 12 | #define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) | ||
| 13 | #define g_marshal_value_peek_int64(v) g_value_get_int64 (v) | ||
| 14 | #define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) | ||
| 15 | #define g_marshal_value_peek_enum(v) g_value_get_enum (v) | ||
| 16 | #define g_marshal_value_peek_flags(v) g_value_get_flags (v) | ||
| 17 | #define g_marshal_value_peek_float(v) g_value_get_float (v) | ||
| 18 | #define g_marshal_value_peek_double(v) g_value_get_double (v) | ||
| 19 | #define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) | ||
| 20 | #define g_marshal_value_peek_param(v) g_value_get_param (v) | ||
| 21 | #define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) | ||
| 22 | #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) | ||
| 23 | #define g_marshal_value_peek_object(v) g_value_get_object (v) | ||
| 24 | #else /* !G_ENABLE_DEBUG */ | ||
| 25 | /* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. | ||
| 26 | * Do not access GValues directly in your code. Instead, use the | ||
| 27 | * g_value_get_*() functions | ||
| 28 | */ | ||
| 29 | #define g_marshal_value_peek_boolean(v) (v)->data[0].v_int | ||
| 30 | #define g_marshal_value_peek_char(v) (v)->data[0].v_int | ||
| 31 | #define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint | ||
| 32 | #define g_marshal_value_peek_int(v) (v)->data[0].v_int | ||
| 33 | #define g_marshal_value_peek_uint(v) (v)->data[0].v_uint | ||
| 34 | #define g_marshal_value_peek_long(v) (v)->data[0].v_long | ||
| 35 | #define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong | ||
| 36 | #define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 | ||
| 37 | #define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 | ||
| 38 | #define g_marshal_value_peek_enum(v) (v)->data[0].v_long | ||
| 39 | #define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong | ||
| 40 | #define g_marshal_value_peek_float(v) (v)->data[0].v_float | ||
| 41 | #define g_marshal_value_peek_double(v) (v)->data[0].v_double | ||
| 42 | #define g_marshal_value_peek_string(v) (v)->data[0].v_pointer | ||
| 43 | #define g_marshal_value_peek_param(v) (v)->data[0].v_pointer | ||
| 44 | #define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer | ||
| 45 | #define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer | ||
| 46 | #define g_marshal_value_peek_object(v) (v)->data[0].v_pointer | ||
| 47 | #endif /* !G_ENABLE_DEBUG */ | ||
| 48 | |||
| 49 | |||
| 50 | /* VOID:VOID (syl-marshal.list:1) */ | ||
| 51 | |||
| 52 | /* VOID:POINTER,STRING,UINT (syl-marshal.list:2) */ | ||
| 53 | void | ||
| 54 | syl_marshal_VOID__POINTER_STRING_UINT (GClosure *closure, | ||
| 55 | GValue *return_value G_GNUC_UNUSED, | ||
| 56 | guint n_param_values, | ||
| 57 | const GValue *param_values, | ||
| 58 | gpointer invocation_hint G_GNUC_UNUSED, | ||
| 59 | gpointer marshal_data) | ||
| 60 | { | ||
| 61 | typedef void (*GMarshalFunc_VOID__POINTER_STRING_UINT) (gpointer data1, | ||
| 62 | gpointer arg_1, | ||
| 63 | gpointer arg_2, | ||
| 64 | guint arg_3, | ||
| 65 | gpointer data2); | ||
| 66 | register GMarshalFunc_VOID__POINTER_STRING_UINT callback; | ||
| 67 | register GCClosure *cc = (GCClosure*) closure; | ||
| 68 | register gpointer data1, data2; | ||
| 69 | |||
| 70 | g_return_if_fail (n_param_values == 4); | ||
| 71 | |||
| 72 | if (G_CCLOSURE_SWAP_DATA (closure)) | ||
| 73 | { | ||
| 74 | data1 = closure->data; | ||
| 75 | data2 = g_value_peek_pointer (param_values + 0); | ||
| 76 | } | ||
| 77 | else | ||
| 78 | { | ||
| 79 | data1 = g_value_peek_pointer (param_values + 0); | ||
| 80 | data2 = closure->data; | ||
| 81 | } | ||
| 82 | callback = (GMarshalFunc_VOID__POINTER_STRING_UINT) (marshal_data ? marshal_data : cc->callback); | ||
| 83 | |||
| 84 | callback (data1, | ||
| 85 | g_marshal_value_peek_pointer (param_values + 1), | ||
| 86 | g_marshal_value_peek_string (param_values + 2), | ||
| 87 | g_marshal_value_peek_uint (param_values + 3), | ||
| 88 | data2); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* VOID:POINTER (syl-marshal.list:3) */ | ||
| 92 | |||
| 93 | /* VOID:POINTER,STRING,STRING (syl-marshal.list:4) */ | ||
| 94 | void | ||
| 95 | syl_marshal_VOID__POINTER_STRING_STRING (GClosure *closure, | ||
| 96 | GValue *return_value G_GNUC_UNUSED, | ||
| 97 | guint n_param_values, | ||
| 98 | const GValue *param_values, | ||
| 99 | gpointer invocation_hint G_GNUC_UNUSED, | ||
| 100 | gpointer marshal_data) | ||
| 101 | { | ||
| 102 | typedef void (*GMarshalFunc_VOID__POINTER_STRING_STRING) (gpointer data1, | ||
| 103 | gpointer arg_1, | ||
| 104 | gpointer arg_2, | ||
| 105 | gpointer arg_3, | ||
| 106 | gpointer data2); | ||
| 107 | register GMarshalFunc_VOID__POINTER_STRING_STRING callback; | ||
| 108 | register GCClosure *cc = (GCClosure*) closure; | ||
| 109 | register gpointer data1, data2; | ||
| 110 | |||
| 111 | g_return_if_fail (n_param_values == 4); | ||
| 112 | |||
| 113 | if (G_CCLOSURE_SWAP_DATA (closure)) | ||
| 114 | { | ||
| 115 | data1 = closure->data; | ||
| 116 | data2 = g_value_peek_pointer (param_values + 0); | ||
| 117 | } | ||
| 118 | else | ||
| 119 | { | ||
| 120 | data1 = g_value_peek_pointer (param_values + 0); | ||
| 121 | data2 = closure->data; | ||
| 122 | } | ||
| 123 | callback = (GMarshalFunc_VOID__POINTER_STRING_STRING) (marshal_data ? marshal_data : cc->callback); | ||
| 124 | |||
| 125 | callback (data1, | ||
| 126 | g_marshal_value_peek_pointer (param_values + 1), | ||
| 127 | g_marshal_value_peek_string (param_values + 2), | ||
| 128 | g_marshal_value_peek_string (param_values + 3), | ||
| 129 | data2); | ||
| 130 | } | ||
| 131 | |||
diff --git a/libsylph/syl-marshal.h b/libsylph/syl-marshal.h new file mode 100644 index 0000000..5bf8714 --- /dev/null +++ b/libsylph/syl-marshal.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | |||
| 2 | #ifndef __syl_marshal_MARSHAL_H__ | ||
| 3 | #define __syl_marshal_MARSHAL_H__ | ||
| 4 | |||
| 5 | #include <glib-object.h> | ||
| 6 | |||
| 7 | G_BEGIN_DECLS | ||
| 8 | |||
| 9 | /* VOID:VOID (syl-marshal.list:1) */ | ||
| 10 | #define syl_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID | ||
| 11 | |||
| 12 | /* VOID:POINTER,STRING,UINT (syl-marshal.list:2) */ | ||
| 13 | extern void syl_marshal_VOID__POINTER_STRING_UINT (GClosure *closure, | ||
| 14 | GValue *return_value, | ||
| 15 | guint n_param_values, | ||
| 16 | const GValue *param_values, | ||
| 17 | gpointer invocation_hint, | ||
| 18 | gpointer marshal_data); | ||
| 19 | |||
| 20 | /* VOID:POINTER (syl-marshal.list:3) */ | ||
| 21 | #define syl_marshal_VOID__POINTER g_cclosure_marshal_VOID__POINTER | ||
| 22 | |||
| 23 | /* VOID:POINTER,STRING,STRING (syl-marshal.list:4) */ | ||
| 24 | extern void syl_marshal_VOID__POINTER_STRING_STRING (GClosure *closure, | ||
| 25 | GValue *return_value, | ||
| 26 | guint n_param_values, | ||
| 27 | const GValue *param_values, | ||
| 28 | gpointer invocation_hint, | ||
| 29 | gpointer marshal_data); | ||
| 30 | |||
| 31 | G_END_DECLS | ||
| 32 | |||
| 33 | #endif /* __syl_marshal_MARSHAL_H__ */ | ||
| 34 | |||
diff --git a/libsylph/syl-marshal.list b/libsylph/syl-marshal.list new file mode 100644 index 0000000..983f5ad --- /dev/null +++ b/libsylph/syl-marshal.list | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | VOID:VOID | ||
| 2 | VOID:POINTER,STRING,UINT | ||
| 3 | VOID:POINTER | ||
| 4 | VOID:POINTER,STRING,STRING | ||
diff --git a/libsylph/sylmain.c b/libsylph/sylmain.c new file mode 100644 index 0000000..b753b3c --- /dev/null +++ b/libsylph/sylmain.c | |||
| @@ -0,0 +1,342 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2010 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | |||
| 29 | #ifdef G_OS_UNIX | ||
| 30 | # include <signal.h> | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #if HAVE_LOCALE_H | ||
| 34 | # include <locale.h> | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #include "sylmain.h" | ||
| 38 | #include "syl-marshal.h" | ||
| 39 | #include "prefs_common.h" | ||
| 40 | #include "account.h" | ||
| 41 | #include "filter.h" | ||
| 42 | #include "folder.h" | ||
| 43 | #include "socket.h" | ||
| 44 | #include "codeconv.h" | ||
| 45 | #include "utils.h" | ||
| 46 | |||
| 47 | #if USE_SSL | ||
| 48 | # include "ssl.h" | ||
| 49 | #endif | ||
| 50 | |||
| 51 | #ifndef PACKAGE | ||
| 52 | # define PACKAGE GETTEXT_PACKAGE | ||
| 53 | #endif | ||
| 54 | |||
| 55 | G_DEFINE_TYPE(SylApp, syl_app, G_TYPE_OBJECT); | ||
| 56 | |||
| 57 | enum { | ||
| 58 | INIT_DONE, | ||
| 59 | APP_EXIT, | ||
| 60 | APP_FORCE_EXIT, | ||
| 61 | ADD_MSG, | ||
| 62 | REMOVE_MSG, | ||
| 63 | REMOVE_ALL_MSG, | ||
| 64 | REMOVE_FOLDER, | ||
| 65 | MOVE_FOLDER, | ||
| 66 | FOLDERLIST_UPDATED, | ||
| 67 | ACCOUNT_UPDATED, | ||
| 68 | LAST_SIGNAL | ||
| 69 | }; | ||
| 70 | |||
| 71 | static guint app_signals[LAST_SIGNAL] = { 0 }; | ||
| 72 | |||
| 73 | static GObject *app = NULL; | ||
| 74 | |||
| 75 | |||
| 76 | static void syl_app_init(SylApp *self) | ||
| 77 | { | ||
| 78 | } | ||
| 79 | |||
| 80 | static void syl_app_class_init(SylAppClass *klass) | ||
| 81 | { | ||
| 82 | GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | ||
| 83 | |||
| 84 | app_signals[INIT_DONE] = | ||
| 85 | g_signal_new("init-done", | ||
| 86 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 87 | G_SIGNAL_RUN_FIRST, | ||
| 88 | 0, | ||
| 89 | NULL, NULL, | ||
| 90 | syl_marshal_VOID__VOID, | ||
| 91 | G_TYPE_NONE, | ||
| 92 | 0); | ||
| 93 | app_signals[APP_EXIT] = | ||
| 94 | g_signal_new("app-exit", | ||
| 95 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 96 | G_SIGNAL_RUN_FIRST, | ||
| 97 | 0, | ||
| 98 | NULL, NULL, | ||
| 99 | syl_marshal_VOID__VOID, | ||
| 100 | G_TYPE_NONE, | ||
| 101 | 0); | ||
| 102 | app_signals[APP_FORCE_EXIT] = | ||
| 103 | g_signal_new("app-force-exit", | ||
| 104 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 105 | G_SIGNAL_RUN_FIRST, | ||
| 106 | 0, | ||
| 107 | NULL, NULL, | ||
| 108 | syl_marshal_VOID__VOID, | ||
| 109 | G_TYPE_NONE, | ||
| 110 | 0); | ||
| 111 | app_signals[ADD_MSG] = | ||
| 112 | g_signal_new("add-msg", | ||
| 113 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 114 | G_SIGNAL_RUN_FIRST, | ||
| 115 | 0, | ||
| 116 | NULL, NULL, | ||
| 117 | syl_marshal_VOID__POINTER_STRING_UINT, | ||
| 118 | G_TYPE_NONE, | ||
| 119 | 3, | ||
| 120 | G_TYPE_POINTER, | ||
| 121 | G_TYPE_STRING, | ||
| 122 | G_TYPE_UINT); | ||
| 123 | app_signals[REMOVE_MSG] = | ||
| 124 | g_signal_new("remove-msg", | ||
| 125 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 126 | G_SIGNAL_RUN_FIRST, | ||
| 127 | 0, | ||
| 128 | NULL, NULL, | ||
| 129 | syl_marshal_VOID__POINTER_STRING_UINT, | ||
| 130 | G_TYPE_NONE, | ||
| 131 | 3, | ||
| 132 | G_TYPE_POINTER, | ||
| 133 | G_TYPE_STRING, | ||
| 134 | G_TYPE_UINT); | ||
| 135 | app_signals[REMOVE_ALL_MSG] = | ||
| 136 | g_signal_new("remove-all-msg", | ||
| 137 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 138 | G_SIGNAL_RUN_FIRST, | ||
| 139 | 0, | ||
| 140 | NULL, NULL, | ||
| 141 | syl_marshal_VOID__POINTER, | ||
| 142 | G_TYPE_NONE, | ||
| 143 | 1, | ||
| 144 | G_TYPE_POINTER); | ||
| 145 | app_signals[REMOVE_FOLDER] = | ||
| 146 | g_signal_new("remove-folder", | ||
| 147 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 148 | G_SIGNAL_RUN_FIRST, | ||
| 149 | 0, | ||
| 150 | NULL, NULL, | ||
| 151 | syl_marshal_VOID__POINTER, | ||
| 152 | G_TYPE_NONE, | ||
| 153 | 1, | ||
| 154 | G_TYPE_POINTER); | ||
| 155 | app_signals[MOVE_FOLDER] = | ||
| 156 | g_signal_new("move-folder", | ||
| 157 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 158 | G_SIGNAL_RUN_FIRST, | ||
| 159 | 0, | ||
| 160 | NULL, NULL, | ||
| 161 | syl_marshal_VOID__POINTER_STRING_STRING, | ||
| 162 | G_TYPE_NONE, | ||
| 163 | 3, | ||
| 164 | G_TYPE_POINTER, | ||
| 165 | G_TYPE_STRING, | ||
| 166 | G_TYPE_STRING); | ||
| 167 | app_signals[FOLDERLIST_UPDATED] = | ||
| 168 | g_signal_new("folderlist-updated", | ||
| 169 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 170 | G_SIGNAL_RUN_FIRST, | ||
| 171 | 0, | ||
| 172 | NULL, NULL, | ||
| 173 | syl_marshal_VOID__VOID, | ||
| 174 | G_TYPE_NONE, | ||
| 175 | 0); | ||
| 176 | app_signals[ACCOUNT_UPDATED] = | ||
| 177 | g_signal_new("account-updated", | ||
| 178 | G_TYPE_FROM_CLASS(gobject_class), | ||
| 179 | G_SIGNAL_RUN_FIRST, | ||
| 180 | 0, | ||
| 181 | NULL, NULL, | ||
| 182 | syl_marshal_VOID__VOID, | ||
| 183 | G_TYPE_NONE, | ||
| 184 | 0); | ||
| 185 | } | ||
| 186 | |||
| 187 | GObject *syl_app_create(void) | ||
| 188 | { | ||
| 189 | if (!app) | ||
| 190 | app = g_object_new(SYL_TYPE_APP, NULL); | ||
| 191 | return app; | ||
| 192 | } | ||
| 193 | |||
| 194 | GObject *syl_app_get(void) | ||
| 195 | { | ||
| 196 | return app; | ||
| 197 | } | ||
| 198 | |||
| 199 | void syl_init(void) | ||
| 200 | { | ||
| 201 | #ifdef G_OS_WIN32 | ||
| 202 | gchar *newpath; | ||
| 203 | const gchar *lang_env; | ||
| 204 | |||
| 205 | /* disable locale variable such as "LANG=1041" */ | ||
| 206 | |||
| 207 | #define DISABLE_DIGIT_LOCALE(envstr) \ | ||
| 208 | { \ | ||
| 209 | lang_env = g_getenv(envstr); \ | ||
| 210 | if (lang_env && g_ascii_isdigit(lang_env[0])) \ | ||
| 211 | g_unsetenv(envstr); \ | ||
| 212 | } | ||
| 213 | |||
| 214 | DISABLE_DIGIT_LOCALE("LC_ALL"); | ||
| 215 | DISABLE_DIGIT_LOCALE("LANG"); | ||
| 216 | DISABLE_DIGIT_LOCALE("LC_CTYPE"); | ||
| 217 | DISABLE_DIGIT_LOCALE("LC_MESSAGES"); | ||
| 218 | |||
| 219 | #undef DISABLE_DIGIT_LOCALE | ||
| 220 | |||
| 221 | g_unsetenv("LANGUAGE"); | ||
| 222 | #endif /* G_OS_WIN32 */ | ||
| 223 | |||
| 224 | #ifdef HAVE_LOCALE_H | ||
| 225 | setlocale(LC_ALL, ""); | ||
| 226 | #endif | ||
| 227 | |||
| 228 | set_startup_dir(); | ||
| 229 | |||
| 230 | #ifdef G_OS_WIN32 | ||
| 231 | /* include startup directory into %PATH% for GSpawn */ | ||
| 232 | newpath = g_strconcat(get_startup_dir(), ";", g_getenv("PATH"), NULL); | ||
| 233 | g_setenv("PATH", newpath, TRUE); | ||
| 234 | g_free(newpath); | ||
| 235 | #endif | ||
| 236 | |||
| 237 | #ifdef ENABLE_NLS | ||
| 238 | syl_init_gettext(PACKAGE, LOCALEDIR); | ||
| 239 | textdomain(PACKAGE); | ||
| 240 | #endif | ||
| 241 | |||
| 242 | sock_init(); | ||
| 243 | |||
| 244 | #ifdef G_OS_UNIX | ||
| 245 | /* ignore SIGPIPE signal for preventing sudden death of program */ | ||
| 246 | signal(SIGPIPE, SIG_IGN); | ||
| 247 | #endif | ||
| 248 | } | ||
| 249 | |||
| 250 | #define MAKE_DIR_IF_NOT_EXIST(dir) \ | ||
| 251 | { \ | ||
| 252 | if (!is_dir_exist(dir)) { \ | ||
| 253 | if (is_file_exist(dir)) { \ | ||
| 254 | g_warning("File '%s' already exists. " \ | ||
| 255 | "Can't create folder.", dir); \ | ||
| 256 | return -1; \ | ||
| 257 | } \ | ||
| 258 | if (make_dir(dir) < 0) \ | ||
| 259 | return -1; \ | ||
| 260 | } \ | ||
| 261 | } | ||
| 262 | |||
| 263 | void syl_init_gettext(const gchar *package, const gchar *dirname) | ||
| 264 | { | ||
| 265 | #ifdef ENABLE_NLS | ||
| 266 | if (g_path_is_absolute(dirname)) | ||
| 267 | bindtextdomain(package, dirname); | ||
| 268 | else { | ||
| 269 | gchar *locale_dir; | ||
| 270 | |||
| 271 | locale_dir = g_strconcat(get_startup_dir(), G_DIR_SEPARATOR_S, | ||
| 272 | dirname, NULL); | ||
| 273 | #ifdef G_OS_WIN32 | ||
| 274 | { | ||
| 275 | gchar *locale_dir_; | ||
| 276 | |||
| 277 | locale_dir_ = g_locale_from_utf8(locale_dir, -1, | ||
| 278 | NULL, NULL, NULL); | ||
| 279 | if (locale_dir_) { | ||
| 280 | g_free(locale_dir); | ||
| 281 | locale_dir = locale_dir_; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | #endif /* G_OS_WIN32 */ | ||
| 285 | bindtextdomain(package, locale_dir); | ||
| 286 | g_free(locale_dir); | ||
| 287 | } | ||
| 288 | |||
| 289 | bind_textdomain_codeset(package, CS_UTF_8); | ||
| 290 | #endif /* ENABLE_NLS */ | ||
| 291 | } | ||
| 292 | |||
| 293 | gint syl_setup_rc_dir(void) | ||
| 294 | { | ||
| 295 | if (!is_dir_exist(get_rc_dir())) { | ||
| 296 | if (make_dir_hier(get_rc_dir()) < 0) | ||
| 297 | return -1; | ||
| 298 | } | ||
| 299 | |||
| 300 | MAKE_DIR_IF_NOT_EXIST(get_mail_base_dir()); | ||
| 301 | |||
| 302 | CHDIR_RETURN_VAL_IF_FAIL(get_rc_dir(), -1); | ||
| 303 | |||
| 304 | MAKE_DIR_IF_NOT_EXIST(get_imap_cache_dir()); | ||
| 305 | MAKE_DIR_IF_NOT_EXIST(get_news_cache_dir()); | ||
| 306 | MAKE_DIR_IF_NOT_EXIST(get_mime_tmp_dir()); | ||
| 307 | MAKE_DIR_IF_NOT_EXIST(get_tmp_dir()); | ||
| 308 | MAKE_DIR_IF_NOT_EXIST(UIDL_DIR); | ||
| 309 | MAKE_DIR_IF_NOT_EXIST(PLUGIN_DIR); | ||
| 310 | |||
| 311 | /* remove temporary files */ | ||
| 312 | remove_all_files(get_tmp_dir()); | ||
| 313 | remove_all_files(get_mime_tmp_dir()); | ||
| 314 | |||
| 315 | return 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | void syl_save_all_state(void) | ||
| 319 | { | ||
| 320 | folder_write_list(); | ||
| 321 | prefs_common_write_config(); | ||
| 322 | filter_write_config(); | ||
| 323 | account_write_config_all(); | ||
| 324 | } | ||
| 325 | |||
| 326 | void syl_cleanup(void) | ||
| 327 | { | ||
| 328 | /* remove temporary files */ | ||
| 329 | remove_all_files(get_tmp_dir()); | ||
| 330 | remove_all_files(get_mime_tmp_dir()); | ||
| 331 | #if GLIB_CHECK_VERSION(2, 6, 0) | ||
| 332 | g_log_set_default_handler(g_log_default_handler, NULL); | ||
| 333 | #endif | ||
| 334 | close_log_file(); | ||
| 335 | |||
| 336 | sock_cleanup(); | ||
| 337 | |||
| 338 | if (app) { | ||
| 339 | g_object_unref(app); | ||
| 340 | app = NULL; | ||
| 341 | } | ||
| 342 | } | ||
diff --git a/libsylph/sylmain.h b/libsylph/sylmain.h new file mode 100644 index 0000000..e09a604 --- /dev/null +++ b/libsylph/sylmain.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2009 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __SYLMAIN_H__ | ||
| 21 | #define __SYLMAIN_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <glib-object.h> | ||
| 25 | |||
| 26 | /* SylApp object */ | ||
| 27 | |||
| 28 | #define SYL_TYPE_APP (syl_app_get_type()) | ||
| 29 | #define SYL_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SYL_TYPE_APP, SylApp)) | ||
| 30 | #define SYL_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SYL_TYPE_APP)) | ||
| 31 | #define SYL_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SYL_TYPE_APP, SylAppClass)) | ||
| 32 | #define SYL_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SYL_TYPE_APP)) | ||
| 33 | #define SYL_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SYL_TYPE_APP, SylAppClass)) | ||
| 34 | |||
| 35 | typedef struct _SylApp SylApp; | ||
| 36 | typedef struct _SylAppClass SylAppClass; | ||
| 37 | |||
| 38 | struct _SylApp | ||
| 39 | { | ||
| 40 | GObject parent_instance; | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct _SylAppClass | ||
| 44 | { | ||
| 45 | GObjectClass parent_class; | ||
| 46 | }; | ||
| 47 | |||
| 48 | GObject *syl_app_create (void); | ||
| 49 | GObject *syl_app_get (void); | ||
| 50 | |||
| 51 | void syl_init (void); | ||
| 52 | void syl_init_gettext (const gchar *package, const gchar *dirname); | ||
| 53 | gint syl_setup_rc_dir (void); | ||
| 54 | void syl_save_all_state (void); | ||
| 55 | void syl_cleanup (void); | ||
| 56 | |||
| 57 | #endif /* __SYLMAIN_H__ */ | ||
diff --git a/libsylph/unmime.c b/libsylph/unmime.c new file mode 100644 index 0000000..34a3d73 --- /dev/null +++ b/libsylph/unmime.c | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include <glib.h> | ||
| 25 | #include <string.h> | ||
| 26 | #include <ctype.h> | ||
| 27 | |||
| 28 | #include "codeconv.h" | ||
| 29 | #include "base64.h" | ||
| 30 | #include "quoted-printable.h" | ||
| 31 | |||
| 32 | #define ENCODED_WORD_BEGIN "=?" | ||
| 33 | #define ENCODED_WORD_END "?=" | ||
| 34 | |||
| 35 | /* Decodes headers based on RFC2045 and RFC2047. */ | ||
| 36 | |||
| 37 | gchar *unmime_header(const gchar *encoded_str) | ||
| 38 | { | ||
| 39 | const gchar *p = encoded_str; | ||
| 40 | const gchar *eword_begin_p, *encoding_begin_p, *text_begin_p, | ||
| 41 | *eword_end_p; | ||
| 42 | gchar charset[32]; | ||
| 43 | gchar encoding; | ||
| 44 | gchar *conv_str; | ||
| 45 | GString *outbuf; | ||
| 46 | gchar *out_str; | ||
| 47 | gsize out_len; | ||
| 48 | |||
| 49 | outbuf = g_string_sized_new(strlen(encoded_str) * 2); | ||
| 50 | |||
| 51 | while (*p != '\0') { | ||
| 52 | gchar *decoded_text = NULL; | ||
| 53 | gint len; | ||
| 54 | |||
| 55 | eword_begin_p = strstr(p, ENCODED_WORD_BEGIN); | ||
| 56 | if (!eword_begin_p) { | ||
| 57 | g_string_append(outbuf, p); | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | encoding_begin_p = strchr(eword_begin_p + 2, '?'); | ||
| 61 | if (!encoding_begin_p) { | ||
| 62 | g_string_append(outbuf, p); | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | text_begin_p = strchr(encoding_begin_p + 1, '?'); | ||
| 66 | if (!text_begin_p) { | ||
| 67 | g_string_append(outbuf, p); | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | eword_end_p = strstr(text_begin_p + 1, ENCODED_WORD_END); | ||
| 71 | if (!eword_end_p) { | ||
| 72 | g_string_append(outbuf, p); | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | |||
| 76 | if (p == encoded_str) { | ||
| 77 | g_string_append_len(outbuf, p, eword_begin_p - p); | ||
| 78 | p = eword_begin_p; | ||
| 79 | } else { | ||
| 80 | /* ignore spaces between encoded words */ | ||
| 81 | const gchar *sp; | ||
| 82 | |||
| 83 | for (sp = p; sp < eword_begin_p; sp++) { | ||
| 84 | if (!g_ascii_isspace(*sp)) { | ||
| 85 | g_string_append_len | ||
| 86 | (outbuf, p, eword_begin_p - p); | ||
| 87 | p = eword_begin_p; | ||
| 88 | break; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | len = MIN(sizeof(charset) - 1, | ||
| 94 | encoding_begin_p - (eword_begin_p + 2)); | ||
| 95 | memcpy(charset, eword_begin_p + 2, len); | ||
| 96 | charset[len] = '\0'; | ||
| 97 | encoding = g_ascii_toupper(*(encoding_begin_p + 1)); | ||
| 98 | |||
| 99 | if (encoding == 'B') { | ||
| 100 | decoded_text = g_malloc | ||
| 101 | (eword_end_p - (text_begin_p + 1) + 1); | ||
| 102 | len = base64_decode((guchar *)decoded_text, | ||
| 103 | text_begin_p + 1, | ||
| 104 | eword_end_p - (text_begin_p + 1)); | ||
| 105 | decoded_text[len] = '\0'; | ||
| 106 | } else if (encoding == 'Q') { | ||
| 107 | decoded_text = g_malloc | ||
| 108 | (eword_end_p - (text_begin_p + 1) + 1); | ||
| 109 | len = qp_decode_q_encoding | ||
| 110 | ((guchar *)decoded_text, text_begin_p + 1, | ||
| 111 | eword_end_p - (text_begin_p + 1)); | ||
| 112 | } else { | ||
| 113 | g_string_append_len(outbuf, p, eword_end_p + 2 - p); | ||
| 114 | p = eword_end_p + 2; | ||
| 115 | continue; | ||
| 116 | } | ||
| 117 | |||
| 118 | /* convert to UTF-8 */ | ||
| 119 | conv_str = conv_codeset_strdup(decoded_text, charset, NULL); | ||
| 120 | if (!conv_str) | ||
| 121 | conv_str = conv_utf8todisp(decoded_text, NULL); | ||
| 122 | g_string_append(outbuf, conv_str); | ||
| 123 | g_free(conv_str); | ||
| 124 | |||
| 125 | g_free(decoded_text); | ||
| 126 | |||
| 127 | p = eword_end_p + 2; | ||
| 128 | } | ||
| 129 | |||
| 130 | out_str = outbuf->str; | ||
| 131 | out_len = outbuf->len; | ||
| 132 | g_string_free(outbuf, FALSE); | ||
| 133 | |||
| 134 | return g_realloc(out_str, out_len + 1); | ||
| 135 | } | ||
diff --git a/libsylph/unmime.h b/libsylph/unmime.h new file mode 100644 index 0000000..e87ba13 --- /dev/null +++ b/libsylph/unmime.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __UNMIME_H__ | ||
| 21 | #define __UNMIME_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | gchar *unmime_header (const gchar *encoded_str); | ||
| 26 | |||
| 27 | #endif /* __UNMIME_H__ */ | ||
diff --git a/libsylph/utils.c b/libsylph/utils.c new file mode 100644 index 0000000..aabce06 --- /dev/null +++ b/libsylph/utils.c | |||
| @@ -0,0 +1,4849 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <ctype.h> | ||
| 31 | #include <errno.h> | ||
| 32 | #include <stdlib.h> | ||
| 33 | #include <sys/stat.h> | ||
| 34 | #include <fcntl.h> | ||
| 35 | #include <unistd.h> | ||
| 36 | #include <stdarg.h> | ||
| 37 | #include <sys/types.h> | ||
| 38 | #if HAVE_SYS_WAIT_H | ||
| 39 | # include <sys/wait.h> | ||
| 40 | #endif | ||
| 41 | #include <dirent.h> | ||
| 42 | #include <time.h> | ||
| 43 | |||
| 44 | #ifdef G_OS_WIN32 | ||
| 45 | #ifndef WINVER | ||
| 46 | # define WINVER 0x0500 | ||
| 47 | #endif | ||
| 48 | # include <windows.h> | ||
| 49 | # include <wchar.h> | ||
| 50 | # include <direct.h> | ||
| 51 | # include <io.h> | ||
| 52 | # include <shlobj.h> | ||
| 53 | #endif | ||
| 54 | |||
| 55 | #ifdef __APPLE__ | ||
| 56 | # include <AppKit/AppKit.h> | ||
| 57 | #endif | ||
| 58 | |||
| 59 | #include "utils.h" | ||
| 60 | #include "socket.h" | ||
| 61 | |||
| 62 | #define BUFFSIZE 8192 | ||
| 63 | |||
| 64 | static gboolean debug_mode = FALSE; | ||
| 65 | |||
| 66 | |||
| 67 | #if !GLIB_CHECK_VERSION(2, 7, 0) && !defined(G_OS_UNIX) | ||
| 68 | gint g_chdir(const gchar *path) | ||
| 69 | { | ||
| 70 | #ifdef G_OS_WIN32 | ||
| 71 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 72 | wchar_t *wpath; | ||
| 73 | gint retval; | ||
| 74 | gint save_errno; | ||
| 75 | |||
| 76 | wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); | ||
| 77 | if (wpath == NULL) { | ||
| 78 | errno = EINVAL; | ||
| 79 | return -1; | ||
| 80 | } | ||
| 81 | |||
| 82 | retval = _wchdir(wpath); | ||
| 83 | save_errno = errno; | ||
| 84 | |||
| 85 | g_free(wpath); | ||
| 86 | |||
| 87 | errno = save_errno; | ||
| 88 | return retval; | ||
| 89 | } else { | ||
| 90 | gchar *cp_path; | ||
| 91 | gint retval; | ||
| 92 | gint save_errno; | ||
| 93 | |||
| 94 | cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); | ||
| 95 | if (cp_path == NULL) { | ||
| 96 | errno = EINVAL; | ||
| 97 | return -1; | ||
| 98 | } | ||
| 99 | |||
| 100 | retval = chdir(cp_path); | ||
| 101 | save_errno = errno; | ||
| 102 | |||
| 103 | g_free(cp_path); | ||
| 104 | |||
| 105 | errno = save_errno; | ||
| 106 | return retval; | ||
| 107 | } | ||
| 108 | #else | ||
| 109 | return chdir(path); | ||
| 110 | #endif | ||
| 111 | } | ||
| 112 | |||
| 113 | gint g_chmod(const gchar *path, gint mode) | ||
| 114 | { | ||
| 115 | #ifdef G_OS_WIN32 | ||
| 116 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 117 | wchar_t *wpath; | ||
| 118 | gint retval; | ||
| 119 | gint save_errno; | ||
| 120 | |||
| 121 | wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL); | ||
| 122 | if (wpath == NULL) { | ||
| 123 | errno = EINVAL; | ||
| 124 | return -1; | ||
| 125 | } | ||
| 126 | |||
| 127 | retval = _wchmod(wpath, mode); | ||
| 128 | save_errno = errno; | ||
| 129 | |||
| 130 | g_free(wpath); | ||
| 131 | |||
| 132 | errno = save_errno; | ||
| 133 | return retval; | ||
| 134 | } else { | ||
| 135 | gchar *cp_path; | ||
| 136 | gint retval; | ||
| 137 | gint save_errno; | ||
| 138 | |||
| 139 | cp_path = g_locale_from_utf8(path, -1, NULL, NULL, NULL); | ||
| 140 | if (cp_path == NULL) { | ||
| 141 | errno = EINVAL; | ||
| 142 | return -1; | ||
| 143 | } | ||
| 144 | |||
| 145 | retval = chmod(cp_path, mode); | ||
| 146 | save_errno = errno; | ||
| 147 | |||
| 148 | g_free(cp_path); | ||
| 149 | |||
| 150 | errno = save_errno; | ||
| 151 | return retval; | ||
| 152 | } | ||
| 153 | #else | ||
| 154 | return chmod(path, mode); | ||
| 155 | #endif | ||
| 156 | } | ||
| 157 | #endif /* GLIB_CHECK_VERSION && G_OS_UNIX */ | ||
| 158 | |||
| 159 | #ifndef G_OS_UNIX | ||
| 160 | gint syl_link(const gchar *src, const gchar *dest) | ||
| 161 | { | ||
| 162 | #ifdef G_OS_WIN32 | ||
| 163 | wchar_t *wsrc; | ||
| 164 | wchar_t *wdest; | ||
| 165 | gint retval; | ||
| 166 | gint save_errno; | ||
| 167 | |||
| 168 | wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL); | ||
| 169 | if (wsrc == NULL) { | ||
| 170 | errno = EINVAL; | ||
| 171 | return -1; | ||
| 172 | } | ||
| 173 | wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL); | ||
| 174 | if (wdest == NULL) { | ||
| 175 | g_free(wsrc); | ||
| 176 | errno = EINVAL; | ||
| 177 | return -1; | ||
| 178 | } | ||
| 179 | |||
| 180 | errno = 0; | ||
| 181 | if (CreateHardLinkW(wdest, wsrc, NULL)) { | ||
| 182 | retval = 0; | ||
| 183 | /* debug_print("hard link created: %s -> %s\n", src, dest); */ | ||
| 184 | } else { | ||
| 185 | retval = -1; | ||
| 186 | switch (GetLastError()) { | ||
| 187 | case ERROR_FILE_NOT_FOUND: | ||
| 188 | case ERROR_PATH_NOT_FOUND: | ||
| 189 | errno = ENOENT; break; | ||
| 190 | case ERROR_ACCESS_DENIED: | ||
| 191 | case ERROR_LOCK_VIOLATION: | ||
| 192 | case ERROR_SHARING_VIOLATION: | ||
| 193 | errno = EACCES; break; | ||
| 194 | case ERROR_NOT_SAME_DEVICE: | ||
| 195 | errno = EXDEV; break; | ||
| 196 | case ERROR_FILE_EXISTS: | ||
| 197 | case ERROR_ALREADY_EXISTS: | ||
| 198 | errno = EEXIST; break; | ||
| 199 | case ERROR_TOO_MANY_LINKS: | ||
| 200 | errno = EMLINK; break; | ||
| 201 | default: | ||
| 202 | errno = EIO; break; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | save_errno = errno; | ||
| 206 | |||
| 207 | g_free(wdest); | ||
| 208 | g_free(wsrc); | ||
| 209 | |||
| 210 | errno = save_errno; | ||
| 211 | return retval; | ||
| 212 | #else | ||
| 213 | return link(src, dest); | ||
| 214 | #endif | ||
| 215 | } | ||
| 216 | #endif /* !G_OS_UNIX */ | ||
| 217 | |||
| 218 | void list_free_strings(GList *list) | ||
| 219 | { | ||
| 220 | list = g_list_first(list); | ||
| 221 | |||
| 222 | while (list != NULL) { | ||
| 223 | g_free(list->data); | ||
| 224 | list = list->next; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | void slist_free_strings(GSList *list) | ||
| 229 | { | ||
| 230 | while (list != NULL) { | ||
| 231 | g_free(list->data); | ||
| 232 | list = list->next; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | static void hash_free_strings_func(gpointer key, gpointer value, gpointer data) | ||
| 237 | { | ||
| 238 | g_free(key); | ||
| 239 | } | ||
| 240 | |||
| 241 | void hash_free_strings(GHashTable *table) | ||
| 242 | { | ||
| 243 | g_hash_table_foreach(table, hash_free_strings_func, NULL); | ||
| 244 | } | ||
| 245 | |||
| 246 | static void hash_free_value_mem_func(gpointer key, gpointer value, | ||
| 247 | gpointer data) | ||
| 248 | { | ||
| 249 | g_free(value); | ||
| 250 | } | ||
| 251 | |||
| 252 | void hash_free_value_mem(GHashTable *table) | ||
| 253 | { | ||
| 254 | g_hash_table_foreach(table, hash_free_value_mem_func, NULL); | ||
| 255 | } | ||
| 256 | |||
| 257 | gint str_case_equal(gconstpointer v, gconstpointer v2) | ||
| 258 | { | ||
| 259 | return g_ascii_strcasecmp((const gchar *)v, (const gchar *)v2) == 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | guint str_case_hash(gconstpointer key) | ||
| 263 | { | ||
| 264 | const gchar *p = key; | ||
| 265 | guint h = *p; | ||
| 266 | |||
| 267 | if (h) { | ||
| 268 | h = g_ascii_tolower(h); | ||
| 269 | for (p += 1; *p != '\0'; p++) | ||
| 270 | h = (h << 5) - h + g_ascii_tolower(*p); | ||
| 271 | } | ||
| 272 | |||
| 273 | return h; | ||
| 274 | } | ||
| 275 | |||
| 276 | void ptr_array_free_strings(GPtrArray *array) | ||
| 277 | { | ||
| 278 | gint i; | ||
| 279 | gchar *str; | ||
| 280 | |||
| 281 | g_return_if_fail(array != NULL); | ||
| 282 | |||
| 283 | for (i = 0; i < array->len; i++) { | ||
| 284 | str = g_ptr_array_index(array, i); | ||
| 285 | g_free(str); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | gboolean str_find(const gchar *haystack, const gchar *needle) | ||
| 290 | { | ||
| 291 | return strstr(haystack, needle) != NULL ? TRUE : FALSE; | ||
| 292 | } | ||
| 293 | |||
| 294 | gboolean str_case_find(const gchar *haystack, const gchar *needle) | ||
| 295 | { | ||
| 296 | return strcasestr(haystack, needle) != NULL ? TRUE : FALSE; | ||
| 297 | } | ||
| 298 | |||
| 299 | gboolean str_find_equal(const gchar *haystack, const gchar *needle) | ||
| 300 | { | ||
| 301 | return strcmp(haystack, needle) == 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | gboolean str_case_find_equal(const gchar *haystack, const gchar *needle) | ||
| 305 | { | ||
| 306 | return g_ascii_strcasecmp(haystack, needle) == 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | gint to_number(const gchar *nstr) | ||
| 310 | { | ||
| 311 | register const gchar *p; | ||
| 312 | |||
| 313 | if (*nstr == '\0') return -1; | ||
| 314 | |||
| 315 | for (p = nstr; *p != '\0'; p++) | ||
| 316 | if (!g_ascii_isdigit(*p)) return -1; | ||
| 317 | |||
| 318 | return atoi(nstr); | ||
| 319 | } | ||
| 320 | |||
| 321 | guint to_unumber(const gchar *nstr) | ||
| 322 | { | ||
| 323 | register const gchar *p; | ||
| 324 | gulong val; | ||
| 325 | |||
| 326 | if (*nstr == '\0') return 0; | ||
| 327 | |||
| 328 | for (p = nstr; *p != '\0'; p++) | ||
| 329 | if (!g_ascii_isdigit(*p)) return 0; | ||
| 330 | |||
| 331 | errno = 0; | ||
| 332 | val = strtoul(nstr, NULL, 10); | ||
| 333 | if (val == ULONG_MAX && errno != 0) | ||
| 334 | val = 0; | ||
| 335 | |||
| 336 | return (guint)val; | ||
| 337 | } | ||
| 338 | |||
| 339 | /* convert integer into string, | ||
| 340 | nstr must be not lower than 11 characters length */ | ||
| 341 | gchar *itos_buf(gchar *nstr, gint n) | ||
| 342 | { | ||
| 343 | g_snprintf(nstr, 11, "%d", n); | ||
| 344 | return nstr; | ||
| 345 | } | ||
| 346 | |||
| 347 | /* convert integer into string */ | ||
| 348 | gchar *itos(gint n) | ||
| 349 | { | ||
| 350 | static gchar nstr[11]; | ||
| 351 | |||
| 352 | return itos_buf(nstr, n); | ||
| 353 | } | ||
| 354 | |||
| 355 | gchar *utos_buf(gchar *nstr, guint n) | ||
| 356 | { | ||
| 357 | g_snprintf(nstr, 11, "%u", n); | ||
| 358 | return nstr; | ||
| 359 | } | ||
| 360 | |||
| 361 | gchar *to_human_readable_buf(gchar *buf, size_t bufsize, gint64 size) | ||
| 362 | { | ||
| 363 | if (size < 1024) | ||
| 364 | g_snprintf(buf, bufsize, "%dB", (gint)size); | ||
| 365 | else if ((size >> 10) < 1024) | ||
| 366 | g_snprintf(buf, bufsize, "%.1fKB", (gfloat)size / (1 << 10)); | ||
| 367 | else if ((size >> 20) < 1024) | ||
| 368 | g_snprintf(buf, bufsize, "%.2fMB", (gfloat)size / (1 << 20)); | ||
| 369 | else | ||
| 370 | g_snprintf(buf, bufsize, "%.2fGB", (gfloat)size / (1 << 30)); | ||
| 371 | |||
| 372 | return buf; | ||
| 373 | } | ||
| 374 | |||
| 375 | gchar *to_human_readable(gint64 size) | ||
| 376 | { | ||
| 377 | static gchar str[16]; | ||
| 378 | |||
| 379 | return to_human_readable_buf(str, sizeof(str), size); | ||
| 380 | } | ||
| 381 | |||
| 382 | /* strcmp with NULL-checking */ | ||
| 383 | gint strcmp2(const gchar *s1, const gchar *s2) | ||
| 384 | { | ||
| 385 | if (s1 == NULL || s2 == NULL) | ||
| 386 | return -1; | ||
| 387 | else | ||
| 388 | return strcmp(s1, s2); | ||
| 389 | } | ||
| 390 | |||
| 391 | /* compare paths */ | ||
| 392 | gint path_cmp(const gchar *s1, const gchar *s2) | ||
| 393 | { | ||
| 394 | gint len1, len2; | ||
| 395 | #ifdef G_OS_WIN32 | ||
| 396 | gchar *s1_, *s2_; | ||
| 397 | #endif | ||
| 398 | |||
| 399 | if (s1 == NULL || s2 == NULL) return -1; | ||
| 400 | if (*s1 == '\0' || *s2 == '\0') return -1; | ||
| 401 | |||
| 402 | len1 = strlen(s1); | ||
| 403 | len2 = strlen(s2); | ||
| 404 | |||
| 405 | #ifdef G_OS_WIN32 | ||
| 406 | Xstrdup_a(s1_, s1, return -1); | ||
| 407 | Xstrdup_a(s2_, s2, return -1); | ||
| 408 | subst_char(s1_, '/', G_DIR_SEPARATOR); | ||
| 409 | subst_char(s2_, '/', G_DIR_SEPARATOR); | ||
| 410 | if (s1_[len1 - 1] == G_DIR_SEPARATOR) len1--; | ||
| 411 | if (s2_[len2 - 1] == G_DIR_SEPARATOR) len2--; | ||
| 412 | |||
| 413 | return strncmp(s1_, s2_, MAX(len1, len2)); | ||
| 414 | #else | ||
| 415 | if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--; | ||
| 416 | if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--; | ||
| 417 | |||
| 418 | return strncmp(s1, s2, MAX(len1, len2)); | ||
| 419 | #endif | ||
| 420 | } | ||
| 421 | |||
| 422 | /* return TRUE if parent is equal to or ancestor of child */ | ||
| 423 | gboolean is_path_parent(const gchar *parent, const gchar *child) | ||
| 424 | { | ||
| 425 | gint plen; | ||
| 426 | const gchar *base; | ||
| 427 | |||
| 428 | g_return_val_if_fail(parent != NULL, FALSE); | ||
| 429 | g_return_val_if_fail(child != NULL, FALSE); | ||
| 430 | |||
| 431 | plen = strlen(parent); | ||
| 432 | while (plen > 0 && G_IS_DIR_SEPARATOR(parent[plen - 1])) | ||
| 433 | plen--; | ||
| 434 | |||
| 435 | #ifdef G_OS_WIN32 | ||
| 436 | if (!g_ascii_strncasecmp(parent, child, plen)) { | ||
| 437 | #else | ||
| 438 | if (!strncmp(parent, child, plen)) { | ||
| 439 | #endif | ||
| 440 | base = child + plen; | ||
| 441 | if (!G_IS_DIR_SEPARATOR(*base) && *base != '\0') | ||
| 442 | return FALSE; | ||
| 443 | return TRUE; | ||
| 444 | } | ||
| 445 | |||
| 446 | return FALSE; | ||
| 447 | } | ||
| 448 | |||
| 449 | /* remove trailing return code */ | ||
| 450 | gchar *strretchomp(gchar *str) | ||
| 451 | { | ||
| 452 | register gchar *s; | ||
| 453 | |||
| 454 | if (!*str) return str; | ||
| 455 | |||
| 456 | for (s = str + strlen(str) - 1; | ||
| 457 | s >= str && (*s == '\n' || *s == '\r'); | ||
| 458 | s--) | ||
| 459 | *s = '\0'; | ||
| 460 | |||
| 461 | return str; | ||
| 462 | } | ||
| 463 | |||
| 464 | /* remove trailing character */ | ||
| 465 | gchar *strtailchomp(gchar *str, gchar tail_char) | ||
| 466 | { | ||
| 467 | register gchar *s; | ||
| 468 | |||
| 469 | if (!*str) return str; | ||
| 470 | if (tail_char == '\0') return str; | ||
| 471 | |||
| 472 | for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--) | ||
| 473 | *s = '\0'; | ||
| 474 | |||
| 475 | return str; | ||
| 476 | } | ||
| 477 | |||
| 478 | /* remove CR (carriage return) */ | ||
| 479 | gchar *strcrchomp(gchar *str) | ||
| 480 | { | ||
| 481 | register gchar *s; | ||
| 482 | |||
| 483 | if (!*str) return str; | ||
| 484 | |||
| 485 | s = str + strlen(str) - 1; | ||
| 486 | if (*s == '\n' && s > str && *(s - 1) == '\r') { | ||
| 487 | *(s - 1) = '\n'; | ||
| 488 | *s = '\0'; | ||
| 489 | } | ||
| 490 | |||
| 491 | return str; | ||
| 492 | } | ||
| 493 | |||
| 494 | /* Similar to `strstr' but this function ignores the case of both strings. */ | ||
| 495 | gchar *strcasestr(const gchar *haystack, const gchar *needle) | ||
| 496 | { | ||
| 497 | register size_t haystack_len, needle_len; | ||
| 498 | |||
| 499 | haystack_len = strlen(haystack); | ||
| 500 | needle_len = strlen(needle); | ||
| 501 | |||
| 502 | if (haystack_len < needle_len || needle_len == 0) | ||
| 503 | return NULL; | ||
| 504 | |||
| 505 | while (haystack_len >= needle_len) { | ||
| 506 | if (!g_ascii_strncasecmp(haystack, needle, needle_len)) | ||
| 507 | return (gchar *)haystack; | ||
| 508 | else { | ||
| 509 | haystack++; | ||
| 510 | haystack_len--; | ||
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 | return NULL; | ||
| 515 | } | ||
| 516 | |||
| 517 | gpointer my_memmem(gconstpointer haystack, size_t haystacklen, | ||
| 518 | gconstpointer needle, size_t needlelen) | ||
| 519 | { | ||
| 520 | const gchar *haystack_ = (const gchar *)haystack; | ||
| 521 | const gchar *needle_ = (const gchar *)needle; | ||
| 522 | const gchar *haystack_cur = (const gchar *)haystack; | ||
| 523 | size_t haystack_left = haystacklen; | ||
| 524 | |||
| 525 | if (needlelen == 1) | ||
| 526 | return memchr(haystack_, *needle_, haystacklen); | ||
| 527 | |||
| 528 | while ((haystack_cur = memchr(haystack_cur, *needle_, haystack_left)) | ||
| 529 | != NULL) { | ||
| 530 | if (haystacklen - (haystack_cur - haystack_) < needlelen) | ||
| 531 | break; | ||
| 532 | if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0) | ||
| 533 | return (gpointer)haystack_cur; | ||
| 534 | else { | ||
| 535 | haystack_cur++; | ||
| 536 | haystack_left = haystacklen - (haystack_cur - haystack_); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | return NULL; | ||
| 541 | } | ||
| 542 | |||
| 543 | /* Copy no more than N characters of SRC to DEST, with NULL terminating. */ | ||
| 544 | gchar *strncpy2(gchar *dest, const gchar *src, size_t n) | ||
| 545 | { | ||
| 546 | register const gchar *s = src; | ||
| 547 | register gchar *d = dest; | ||
| 548 | |||
| 549 | while (--n && *s) | ||
| 550 | *d++ = *s++; | ||
| 551 | *d = '\0'; | ||
| 552 | |||
| 553 | return dest; | ||
| 554 | } | ||
| 555 | |||
| 556 | /* Similar to g_str_has_suffix() but case-insensitive */ | ||
| 557 | gboolean str_has_suffix_case(const gchar *str, const gchar *suffix) | ||
| 558 | { | ||
| 559 | size_t len, s_len; | ||
| 560 | |||
| 561 | if (!str || !suffix) | ||
| 562 | return FALSE; | ||
| 563 | |||
| 564 | len = strlen(str); | ||
| 565 | s_len = strlen(suffix); | ||
| 566 | |||
| 567 | if (s_len > len) | ||
| 568 | return FALSE; | ||
| 569 | |||
| 570 | return (g_ascii_strcasecmp(str + (len - s_len), suffix) == 0); | ||
| 571 | } | ||
| 572 | |||
| 573 | gint str_find_format_times(const gchar *haystack, gchar ch) | ||
| 574 | { | ||
| 575 | gint n = 0; | ||
| 576 | const gchar *p = haystack; | ||
| 577 | |||
| 578 | while ((p = strchr(p, '%')) != NULL) { | ||
| 579 | ++p; | ||
| 580 | if (*p == '%') { | ||
| 581 | ++p; | ||
| 582 | } else if (*p == ch) { | ||
| 583 | ++p; | ||
| 584 | ++n; | ||
| 585 | } else | ||
| 586 | return -1; | ||
| 587 | } | ||
| 588 | |||
| 589 | return n; | ||
| 590 | } | ||
| 591 | |||
| 592 | /* Examine if next block is non-ASCII string */ | ||
| 593 | gboolean is_next_nonascii(const gchar *s) | ||
| 594 | { | ||
| 595 | const gchar *p; | ||
| 596 | gboolean in_quote = FALSE; | ||
| 597 | |||
| 598 | /* skip head space */ | ||
| 599 | for (p = s; *p != '\0' && g_ascii_isspace(*p); p++) | ||
| 600 | ; | ||
| 601 | while (*p != '\0') { | ||
| 602 | if (!in_quote && g_ascii_isspace(*p)) | ||
| 603 | break; | ||
| 604 | if (*p == '"') | ||
| 605 | in_quote ^= TRUE; | ||
| 606 | else if (*(guchar *)p > 127 || *(guchar *)p < 32) | ||
| 607 | return TRUE; | ||
| 608 | ++p; | ||
| 609 | } | ||
| 610 | |||
| 611 | return FALSE; | ||
| 612 | } | ||
| 613 | |||
| 614 | gint get_next_word_len(const gchar *s) | ||
| 615 | { | ||
| 616 | const gchar *p = s; | ||
| 617 | gboolean in_quote = FALSE; | ||
| 618 | |||
| 619 | while (*p != '\0') { | ||
| 620 | if (!in_quote && g_ascii_isspace(*p)) | ||
| 621 | break; | ||
| 622 | if (*p == '"') | ||
| 623 | in_quote ^= TRUE; | ||
| 624 | ++p; | ||
| 625 | } | ||
| 626 | |||
| 627 | return p - s; | ||
| 628 | } | ||
| 629 | |||
| 630 | /* compare subjects */ | ||
| 631 | gint subject_compare(const gchar *s1, const gchar *s2) | ||
| 632 | { | ||
| 633 | gchar *str1, *str2; | ||
| 634 | |||
| 635 | if (!s1 || !s2) return -1; | ||
| 636 | if (!*s1 || !*s2) return -1; | ||
| 637 | |||
| 638 | Xstrdup_a(str1, s1, return -1); | ||
| 639 | Xstrdup_a(str2, s2, return -1); | ||
| 640 | |||
| 641 | trim_subject_for_compare(str1); | ||
| 642 | trim_subject_for_compare(str2); | ||
| 643 | |||
| 644 | if (!*str1 || !*str2) return -1; | ||
| 645 | |||
| 646 | return strcmp(str1, str2); | ||
| 647 | } | ||
| 648 | |||
| 649 | gint subject_compare_for_sort(const gchar *s1, const gchar *s2) | ||
| 650 | { | ||
| 651 | gchar *str1, *str2; | ||
| 652 | |||
| 653 | if (!s1 || !s2) return -1; | ||
| 654 | |||
| 655 | Xstrdup_a(str1, s1, return -1); | ||
| 656 | Xstrdup_a(str2, s2, return -1); | ||
| 657 | |||
| 658 | trim_subject_for_sort(str1); | ||
| 659 | trim_subject_for_sort(str2); | ||
| 660 | |||
| 661 | return g_ascii_strcasecmp(str1, str2); | ||
| 662 | } | ||
| 663 | |||
| 664 | void trim_subject_for_compare(gchar *str) | ||
| 665 | { | ||
| 666 | gchar *srcp; | ||
| 667 | |||
| 668 | eliminate_parenthesis(str, '[', ']'); | ||
| 669 | eliminate_parenthesis(str, '(', ')'); | ||
| 670 | g_strstrip(str); | ||
| 671 | |||
| 672 | while (!g_ascii_strncasecmp(str, "Re:", 3)) { | ||
| 673 | srcp = str + 3; | ||
| 674 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 675 | memmove(str, srcp, strlen(srcp) + 1); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | void trim_subject_for_sort(gchar *str) | ||
| 680 | { | ||
| 681 | gchar *srcp; | ||
| 682 | |||
| 683 | g_strstrip(str); | ||
| 684 | |||
| 685 | while (!g_ascii_strncasecmp(str, "Re:", 3)) { | ||
| 686 | srcp = str + 3; | ||
| 687 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 688 | memmove(str, srcp, strlen(srcp) + 1); | ||
| 689 | } | ||
| 690 | } | ||
| 691 | |||
| 692 | void trim_subject(gchar *str) | ||
| 693 | { | ||
| 694 | register gchar *srcp, *destp; | ||
| 695 | gchar op, cl; | ||
| 696 | gint in_brace; | ||
| 697 | |||
| 698 | destp = str; | ||
| 699 | while (!g_ascii_strncasecmp(destp, "Re:", 3)) { | ||
| 700 | destp += 3; | ||
| 701 | while (g_ascii_isspace(*destp)) destp++; | ||
| 702 | } | ||
| 703 | |||
| 704 | if (*destp == '[') { | ||
| 705 | op = '['; | ||
| 706 | cl = ']'; | ||
| 707 | } else if (*destp == '(') { | ||
| 708 | op = '('; | ||
| 709 | cl = ')'; | ||
| 710 | } else | ||
| 711 | return; | ||
| 712 | |||
| 713 | srcp = destp + 1; | ||
| 714 | in_brace = 1; | ||
| 715 | while (*srcp) { | ||
| 716 | if (*srcp == op) | ||
| 717 | in_brace++; | ||
| 718 | else if (*srcp == cl) | ||
| 719 | in_brace--; | ||
| 720 | srcp++; | ||
| 721 | if (in_brace == 0) | ||
| 722 | break; | ||
| 723 | } | ||
| 724 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 725 | memmove(destp, srcp, strlen(srcp) + 1); | ||
| 726 | } | ||
| 727 | |||
| 728 | void eliminate_parenthesis(gchar *str, gchar op, gchar cl) | ||
| 729 | { | ||
| 730 | register gchar *srcp, *destp; | ||
| 731 | gint in_brace; | ||
| 732 | |||
| 733 | srcp = destp = str; | ||
| 734 | |||
| 735 | while ((destp = strchr(destp, op))) { | ||
| 736 | in_brace = 1; | ||
| 737 | srcp = destp + 1; | ||
| 738 | while (*srcp) { | ||
| 739 | if (*srcp == op) | ||
| 740 | in_brace++; | ||
| 741 | else if (*srcp == cl) | ||
| 742 | in_brace--; | ||
| 743 | srcp++; | ||
| 744 | if (in_brace == 0) | ||
| 745 | break; | ||
| 746 | } | ||
| 747 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 748 | memmove(destp, srcp, strlen(srcp) + 1); | ||
| 749 | } | ||
| 750 | } | ||
| 751 | |||
| 752 | void extract_parenthesis(gchar *str, gchar op, gchar cl) | ||
| 753 | { | ||
| 754 | register gchar *srcp, *destp; | ||
| 755 | gint in_brace; | ||
| 756 | |||
| 757 | srcp = destp = str; | ||
| 758 | |||
| 759 | while ((srcp = strchr(destp, op))) { | ||
| 760 | if (destp > str) | ||
| 761 | *destp++ = ' '; | ||
| 762 | memmove(destp, srcp + 1, strlen(srcp)); | ||
| 763 | in_brace = 1; | ||
| 764 | while(*destp) { | ||
| 765 | if (*destp == op) | ||
| 766 | in_brace++; | ||
| 767 | else if (*destp == cl) | ||
| 768 | in_brace--; | ||
| 769 | |||
| 770 | if (in_brace == 0) | ||
| 771 | break; | ||
| 772 | |||
| 773 | destp++; | ||
| 774 | } | ||
| 775 | } | ||
| 776 | *destp = '\0'; | ||
| 777 | } | ||
| 778 | |||
| 779 | void extract_parenthesis_with_escape(gchar *str, gchar op, gchar cl) | ||
| 780 | { | ||
| 781 | register gchar *srcp, *destp; | ||
| 782 | gint in_brace; | ||
| 783 | |||
| 784 | srcp = destp = str; | ||
| 785 | |||
| 786 | while ((srcp = strchr(srcp, op))) { | ||
| 787 | if (destp > str) | ||
| 788 | *destp++ = ' '; | ||
| 789 | ++srcp; | ||
| 790 | in_brace = 1; | ||
| 791 | while (*srcp) { | ||
| 792 | if (*srcp == op) | ||
| 793 | in_brace++; | ||
| 794 | else if (*srcp == cl) | ||
| 795 | in_brace--; | ||
| 796 | |||
| 797 | if (in_brace == 0) | ||
| 798 | break; | ||
| 799 | |||
| 800 | if (*srcp == '\\' && *(srcp + 1) != '\0') | ||
| 801 | ++srcp; | ||
| 802 | |||
| 803 | *destp++ = *srcp++; | ||
| 804 | } | ||
| 805 | } | ||
| 806 | *destp = '\0'; | ||
| 807 | } | ||
| 808 | |||
| 809 | void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr, | ||
| 810 | gchar op, gchar cl) | ||
| 811 | { | ||
| 812 | register gchar *srcp, *destp; | ||
| 813 | gint in_brace; | ||
| 814 | gboolean in_quote = FALSE; | ||
| 815 | |||
| 816 | srcp = destp = str; | ||
| 817 | |||
| 818 | while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) { | ||
| 819 | if (destp > str) | ||
| 820 | *destp++ = ' '; | ||
| 821 | memmove(destp, srcp + 1, strlen(srcp)); | ||
| 822 | in_brace = 1; | ||
| 823 | while(*destp) { | ||
| 824 | if (*destp == op && !in_quote) | ||
| 825 | in_brace++; | ||
| 826 | else if (*destp == cl && !in_quote) | ||
| 827 | in_brace--; | ||
| 828 | else if (*destp == quote_chr) | ||
| 829 | in_quote ^= TRUE; | ||
| 830 | |||
| 831 | if (in_brace == 0) | ||
| 832 | break; | ||
| 833 | |||
| 834 | destp++; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | *destp = '\0'; | ||
| 838 | } | ||
| 839 | |||
| 840 | void eliminate_quote(gchar *str, gchar quote_chr) | ||
| 841 | { | ||
| 842 | register gchar *srcp, *destp; | ||
| 843 | |||
| 844 | srcp = destp = str; | ||
| 845 | |||
| 846 | while ((destp = strchr(destp, quote_chr))) { | ||
| 847 | if ((srcp = strchr(destp + 1, quote_chr))) { | ||
| 848 | srcp++; | ||
| 849 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 850 | memmove(destp, srcp, strlen(srcp) + 1); | ||
| 851 | } else { | ||
| 852 | *destp = '\0'; | ||
| 853 | break; | ||
| 854 | } | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 858 | void extract_quote(gchar *str, gchar quote_chr) | ||
| 859 | { | ||
| 860 | register gchar *p; | ||
| 861 | |||
| 862 | if ((str = strchr(str, quote_chr))) { | ||
| 863 | if ((p = strchr(str + 1, quote_chr))) { | ||
| 864 | *p = '\0'; | ||
| 865 | memmove(str, str + 1, p - str); | ||
| 866 | } | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | void extract_quote_with_escape(gchar *str, gchar quote_chr) | ||
| 871 | { | ||
| 872 | register gchar *sp, *dp; | ||
| 873 | |||
| 874 | if ((sp = strchr(str, quote_chr))) { | ||
| 875 | dp = sp; | ||
| 876 | ++sp; | ||
| 877 | while (*sp) { | ||
| 878 | if (*sp == quote_chr) | ||
| 879 | break; | ||
| 880 | else if (*sp == '\\' && *(sp + 1) != '\0') | ||
| 881 | ++sp; | ||
| 882 | |||
| 883 | *dp++ = *sp++; | ||
| 884 | } | ||
| 885 | *dp = '\0'; | ||
| 886 | } | ||
| 887 | } | ||
| 888 | |||
| 889 | void eliminate_address_comment(gchar *str) | ||
| 890 | { | ||
| 891 | register gchar *srcp, *destp; | ||
| 892 | gint in_brace; | ||
| 893 | |||
| 894 | srcp = destp = str; | ||
| 895 | |||
| 896 | while ((destp = strchr(destp, '"'))) { | ||
| 897 | if ((srcp = strchr(destp + 1, '"'))) { | ||
| 898 | srcp++; | ||
| 899 | if (*srcp == '@') { | ||
| 900 | destp = srcp + 1; | ||
| 901 | } else { | ||
| 902 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 903 | memmove(destp, srcp, strlen(srcp) + 1); | ||
| 904 | } | ||
| 905 | } else { | ||
| 906 | *destp = '\0'; | ||
| 907 | break; | ||
| 908 | } | ||
| 909 | } | ||
| 910 | |||
| 911 | srcp = destp = str; | ||
| 912 | |||
| 913 | while ((destp = strchr_with_skip_quote(destp, '"', '('))) { | ||
| 914 | in_brace = 1; | ||
| 915 | srcp = destp + 1; | ||
| 916 | while (*srcp) { | ||
| 917 | if (*srcp == '(') | ||
| 918 | in_brace++; | ||
| 919 | else if (*srcp == ')') | ||
| 920 | in_brace--; | ||
| 921 | srcp++; | ||
| 922 | if (in_brace == 0) | ||
| 923 | break; | ||
| 924 | } | ||
| 925 | while (g_ascii_isspace(*srcp)) srcp++; | ||
| 926 | memmove(destp, srcp, strlen(srcp) + 1); | ||
| 927 | } | ||
| 928 | } | ||
| 929 | |||
| 930 | gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) | ||
| 931 | { | ||
| 932 | gboolean in_quote = FALSE; | ||
| 933 | |||
| 934 | while (*str) { | ||
| 935 | if (*str == c && !in_quote) | ||
| 936 | return (gchar *)str; | ||
| 937 | if (*str == quote_chr) | ||
| 938 | in_quote ^= TRUE; | ||
| 939 | str++; | ||
| 940 | } | ||
| 941 | |||
| 942 | return NULL; | ||
| 943 | } | ||
| 944 | |||
| 945 | gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c) | ||
| 946 | { | ||
| 947 | gboolean in_quote = FALSE; | ||
| 948 | const gchar *p; | ||
| 949 | |||
| 950 | p = str + strlen(str) - 1; | ||
| 951 | while (p >= str) { | ||
| 952 | if (*p == c && !in_quote) | ||
| 953 | return (gchar *)p; | ||
| 954 | if (*p == quote_chr) | ||
| 955 | in_quote ^= TRUE; | ||
| 956 | p--; | ||
| 957 | } | ||
| 958 | |||
| 959 | return NULL; | ||
| 960 | } | ||
| 961 | |||
| 962 | void extract_address(gchar *str) | ||
| 963 | { | ||
| 964 | eliminate_address_comment(str); | ||
| 965 | if (strchr_with_skip_quote(str, '"', '<')) | ||
| 966 | extract_parenthesis_with_skip_quote(str, '"', '<', '>'); | ||
| 967 | g_strstrip(str); | ||
| 968 | } | ||
| 969 | |||
| 970 | void extract_list_id_str(gchar *str) | ||
| 971 | { | ||
| 972 | if (strchr_with_skip_quote(str, '"', '<')) | ||
| 973 | extract_parenthesis_with_skip_quote(str, '"', '<', '>'); | ||
| 974 | g_strstrip(str); | ||
| 975 | } | ||
| 976 | |||
| 977 | gchar *extract_addresses(const gchar *str) | ||
| 978 | { | ||
| 979 | GString *new_str; | ||
| 980 | GSList *addr_list, *cur; | ||
| 981 | |||
| 982 | if (!str) | ||
| 983 | return NULL; | ||
| 984 | |||
| 985 | addr_list = address_list_append(NULL, str); | ||
| 986 | |||
| 987 | new_str = g_string_new(NULL); | ||
| 988 | |||
| 989 | for (cur = addr_list; cur != NULL; cur = cur->next) { | ||
| 990 | g_string_append(new_str, (gchar *)cur->data); | ||
| 991 | if (cur->next) | ||
| 992 | g_string_append(new_str, ", "); | ||
| 993 | } | ||
| 994 | |||
| 995 | slist_free_strings(addr_list); | ||
| 996 | g_slist_free(addr_list); | ||
| 997 | |||
| 998 | return g_string_free(new_str, FALSE); | ||
| 999 | } | ||
| 1000 | |||
| 1001 | gchar *normalize_address_field(const gchar *str) | ||
| 1002 | { | ||
| 1003 | GString *new_str; | ||
| 1004 | GSList *addr_list, *cur; | ||
| 1005 | gchar *addr, *p, *q, *r; | ||
| 1006 | gchar *ret_str; | ||
| 1007 | |||
| 1008 | addr_list = address_list_append_orig(NULL, str); | ||
| 1009 | |||
| 1010 | new_str = g_string_new(NULL); | ||
| 1011 | |||
| 1012 | for (cur = addr_list; cur != NULL; cur = cur->next) { | ||
| 1013 | p = addr = (gchar *)cur->data; | ||
| 1014 | q = strchr_with_skip_quote(p, '"', '<'); | ||
| 1015 | if (q && q > p) { | ||
| 1016 | r = q - 1; | ||
| 1017 | while (r > p && g_ascii_isspace(*r)) | ||
| 1018 | --r; | ||
| 1019 | g_string_append_len(new_str, p, r - p + 1); | ||
| 1020 | g_string_append_c(new_str, ' '); | ||
| 1021 | p = q; | ||
| 1022 | } | ||
| 1023 | if (*p == '<') { | ||
| 1024 | q = strchr(p, '>'); | ||
| 1025 | if (q) { | ||
| 1026 | r = q + 1; | ||
| 1027 | if (*r) { | ||
| 1028 | while (g_ascii_isspace(*r)) | ||
| 1029 | ++r; | ||
| 1030 | g_string_append(new_str, r); | ||
| 1031 | if (new_str->len > 0 && | ||
| 1032 | !g_ascii_isspace | ||
| 1033 | (new_str->str[new_str->len - 1])) | ||
| 1034 | g_string_append_c(new_str, ' '); | ||
| 1035 | } | ||
| 1036 | g_string_append_len(new_str, p, q - p + 1); | ||
| 1037 | } else { | ||
| 1038 | g_string_append(new_str, p); | ||
| 1039 | g_string_append_c(new_str, '>'); | ||
| 1040 | } | ||
| 1041 | } else | ||
| 1042 | g_string_append(new_str, p); | ||
| 1043 | |||
| 1044 | if (cur->next) | ||
| 1045 | g_string_append(new_str, ", "); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | slist_free_strings(addr_list); | ||
| 1049 | ret_str = new_str->str; | ||
| 1050 | g_string_free(new_str, FALSE); | ||
| 1051 | |||
| 1052 | return ret_str; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | gboolean address_equal(const gchar *addr1, const gchar *addr2) | ||
| 1056 | { | ||
| 1057 | gchar *addr1_, *addr2_; | ||
| 1058 | |||
| 1059 | if (!addr1 || !addr2) | ||
| 1060 | return FALSE; | ||
| 1061 | |||
| 1062 | Xstrdup_a(addr1_, addr1, return FALSE); | ||
| 1063 | Xstrdup_a(addr2_, addr2, return FALSE); | ||
| 1064 | |||
| 1065 | extract_address(addr1_); | ||
| 1066 | extract_address(addr2_); | ||
| 1067 | |||
| 1068 | return strcmp(addr1_, addr2_) == 0; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | GSList *address_list_append_orig(GSList *addr_list, const gchar *str) | ||
| 1072 | { | ||
| 1073 | const gchar *p = str, *q; | ||
| 1074 | gchar *addr; | ||
| 1075 | |||
| 1076 | if (!str) return addr_list; | ||
| 1077 | |||
| 1078 | while (*p) { | ||
| 1079 | if (*p == ',' || g_ascii_isspace(*p)) { | ||
| 1080 | ++p; | ||
| 1081 | } else if ((q = strchr_with_skip_quote(p, '"', ','))) { | ||
| 1082 | addr = g_strndup(p, q - p); | ||
| 1083 | g_strstrip(addr); | ||
| 1084 | addr_list = g_slist_append(addr_list, addr); | ||
| 1085 | p = q + 1; | ||
| 1086 | } else { | ||
| 1087 | addr = g_strdup(p); | ||
| 1088 | g_strstrip(addr); | ||
| 1089 | addr_list = g_slist_append(addr_list, addr); | ||
| 1090 | break; | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | return addr_list; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | GSList *address_list_append(GSList *addr_list, const gchar *str) | ||
| 1098 | { | ||
| 1099 | gchar *work; | ||
| 1100 | gchar *workp; | ||
| 1101 | |||
| 1102 | if (!str) return addr_list; | ||
| 1103 | |||
| 1104 | Xstrdup_a(work, str, return addr_list); | ||
| 1105 | |||
| 1106 | eliminate_address_comment(work); | ||
| 1107 | workp = work; | ||
| 1108 | |||
| 1109 | while (workp && *workp) { | ||
| 1110 | gchar *p, *next; | ||
| 1111 | |||
| 1112 | if ((p = strchr_with_skip_quote(workp, '"', ','))) { | ||
| 1113 | *p = '\0'; | ||
| 1114 | next = p + 1; | ||
| 1115 | } else | ||
| 1116 | next = NULL; | ||
| 1117 | |||
| 1118 | if (strchr_with_skip_quote(workp, '"', '<')) | ||
| 1119 | extract_parenthesis_with_skip_quote | ||
| 1120 | (workp, '"', '<', '>'); | ||
| 1121 | |||
| 1122 | g_strstrip(workp); | ||
| 1123 | if (*workp) | ||
| 1124 | addr_list = g_slist_append(addr_list, g_strdup(workp)); | ||
| 1125 | |||
| 1126 | workp = next; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | return addr_list; | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | GSList *references_list_prepend(GSList *msgid_list, const gchar *str) | ||
| 1133 | { | ||
| 1134 | const gchar *strp; | ||
| 1135 | |||
| 1136 | if (!str) return msgid_list; | ||
| 1137 | strp = str; | ||
| 1138 | |||
| 1139 | while (strp && *strp) { | ||
| 1140 | const gchar *start, *end; | ||
| 1141 | gchar *msgid; | ||
| 1142 | |||
| 1143 | if ((start = strchr(strp, '<')) != NULL) { | ||
| 1144 | end = strchr(start + 1, '>'); | ||
| 1145 | if (!end) break; | ||
| 1146 | } else | ||
| 1147 | break; | ||
| 1148 | |||
| 1149 | msgid = g_strndup(start + 1, end - start - 1); | ||
| 1150 | g_strstrip(msgid); | ||
| 1151 | if (*msgid) | ||
| 1152 | msgid_list = g_slist_prepend(msgid_list, msgid); | ||
| 1153 | else | ||
| 1154 | g_free(msgid); | ||
| 1155 | |||
| 1156 | strp = end + 1; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | return msgid_list; | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | GSList *references_list_append(GSList *msgid_list, const gchar *str) | ||
| 1163 | { | ||
| 1164 | GSList *list; | ||
| 1165 | |||
| 1166 | list = references_list_prepend(NULL, str); | ||
| 1167 | list = g_slist_reverse(list); | ||
| 1168 | msgid_list = g_slist_concat(msgid_list, list); | ||
| 1169 | |||
| 1170 | return msgid_list; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | GSList *newsgroup_list_append(GSList *group_list, const gchar *str) | ||
| 1174 | { | ||
| 1175 | gchar *work; | ||
| 1176 | gchar *workp; | ||
| 1177 | |||
| 1178 | if (!str) return group_list; | ||
| 1179 | |||
| 1180 | Xstrdup_a(work, str, return group_list); | ||
| 1181 | |||
| 1182 | workp = work; | ||
| 1183 | |||
| 1184 | while (workp && *workp) { | ||
| 1185 | gchar *p, *next; | ||
| 1186 | |||
| 1187 | if ((p = strchr_with_skip_quote(workp, '"', ','))) { | ||
| 1188 | *p = '\0'; | ||
| 1189 | next = p + 1; | ||
| 1190 | } else | ||
| 1191 | next = NULL; | ||
| 1192 | |||
| 1193 | g_strstrip(workp); | ||
| 1194 | if (*workp) | ||
| 1195 | group_list = g_slist_append(group_list, | ||
| 1196 | g_strdup(workp)); | ||
| 1197 | |||
| 1198 | workp = next; | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | return group_list; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | GList *add_history(GList *list, const gchar *str) | ||
| 1205 | { | ||
| 1206 | GList *old; | ||
| 1207 | |||
| 1208 | g_return_val_if_fail(str != NULL, list); | ||
| 1209 | |||
| 1210 | old = g_list_find_custom(list, (gpointer)str, (GCompareFunc)strcmp2); | ||
| 1211 | if (old) { | ||
| 1212 | g_free(old->data); | ||
| 1213 | list = g_list_remove(list, old->data); | ||
| 1214 | } else if (g_list_length(list) >= MAX_HISTORY_SIZE) { | ||
| 1215 | GList *last; | ||
| 1216 | |||
| 1217 | last = g_list_last(list); | ||
| 1218 | if (last) { | ||
| 1219 | g_free(last->data); | ||
| 1220 | list = g_list_remove(list, last->data); | ||
| 1221 | } | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | list = g_list_prepend(list, g_strdup(str)); | ||
| 1225 | |||
| 1226 | return list; | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | void remove_return(gchar *str) | ||
| 1230 | { | ||
| 1231 | register gchar *p = str; | ||
| 1232 | |||
| 1233 | while (*p) { | ||
| 1234 | if (*p == '\n' || *p == '\r') | ||
| 1235 | memmove(p, p + 1, strlen(p)); | ||
| 1236 | else | ||
| 1237 | p++; | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | void remove_space(gchar *str) | ||
| 1242 | { | ||
| 1243 | register gchar *p = str; | ||
| 1244 | register gint spc; | ||
| 1245 | |||
| 1246 | while (*p) { | ||
| 1247 | spc = 0; | ||
| 1248 | while (g_ascii_isspace(*(p + spc))) | ||
| 1249 | spc++; | ||
| 1250 | if (spc) | ||
| 1251 | memmove(p, p + spc, strlen(p + spc) + 1); | ||
| 1252 | else | ||
| 1253 | p++; | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | void unfold_line(gchar *str) | ||
| 1258 | { | ||
| 1259 | register gchar *p = str; | ||
| 1260 | register gint spc; | ||
| 1261 | |||
| 1262 | while (*p) { | ||
| 1263 | if (*p == '\n' || *p == '\r') { | ||
| 1264 | *p++ = ' '; | ||
| 1265 | spc = 0; | ||
| 1266 | while (g_ascii_isspace(*(p + spc))) | ||
| 1267 | spc++; | ||
| 1268 | if (spc) | ||
| 1269 | memmove(p, p + spc, strlen(p + spc) + 1); | ||
| 1270 | } else | ||
| 1271 | p++; | ||
| 1272 | } | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | void subst_char(gchar *str, gchar orig, gchar subst) | ||
| 1276 | { | ||
| 1277 | register gchar *p = str; | ||
| 1278 | |||
| 1279 | while (*p) { | ||
| 1280 | if (*p == orig) | ||
| 1281 | *p = subst; | ||
| 1282 | p++; | ||
| 1283 | } | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | void subst_chars(gchar *str, gchar *orig, gchar subst) | ||
| 1287 | { | ||
| 1288 | register gchar *p = str; | ||
| 1289 | |||
| 1290 | while (*p) { | ||
| 1291 | if (strchr(orig, *p) != NULL) | ||
| 1292 | *p = subst; | ||
| 1293 | ++p; | ||
| 1294 | } | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | void subst_null(gchar *str, gint len, gchar subst) | ||
| 1298 | { | ||
| 1299 | register gchar *p = str; | ||
| 1300 | |||
| 1301 | while (len--) { | ||
| 1302 | if (*p == '\0') | ||
| 1303 | *p = subst; | ||
| 1304 | ++p; | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | void subst_control(gchar *str, gchar subst) | ||
| 1309 | { | ||
| 1310 | register gchar *p = str; | ||
| 1311 | |||
| 1312 | while (*p) { | ||
| 1313 | if (g_ascii_iscntrl(*p)) | ||
| 1314 | *p = subst; | ||
| 1315 | ++p; | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | void subst_for_filename(gchar *str) | ||
| 1320 | { | ||
| 1321 | subst_chars(str, " \t\r\n\"'\\/:;*?<>|", '_'); | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | gchar *get_alt_filename(const gchar *filename, gint count) | ||
| 1325 | { | ||
| 1326 | const gchar *ext; | ||
| 1327 | gchar *alt_filename; | ||
| 1328 | |||
| 1329 | ext = strrchr(filename, '.'); | ||
| 1330 | |||
| 1331 | if (ext) { | ||
| 1332 | gchar *base; | ||
| 1333 | |||
| 1334 | base = g_strndup(filename, ext - filename); | ||
| 1335 | alt_filename = g_strdup_printf("%s-%d%s", base, count, ext); | ||
| 1336 | g_free(base); | ||
| 1337 | } else | ||
| 1338 | alt_filename = g_strdup_printf("%s-%d", filename, count); | ||
| 1339 | |||
| 1340 | return alt_filename; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | gboolean is_header_line(const gchar *str) | ||
| 1344 | { | ||
| 1345 | if (str[0] == ':') return FALSE; | ||
| 1346 | |||
| 1347 | while (*str != '\0' && *str != ' ') { | ||
| 1348 | if (*str == ':') | ||
| 1349 | return TRUE; | ||
| 1350 | str++; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | return FALSE; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | gboolean is_ascii_str(const gchar *str) | ||
| 1357 | { | ||
| 1358 | const guchar *p = (const guchar *)str; | ||
| 1359 | |||
| 1360 | while (*p != '\0') { | ||
| 1361 | if (*p != '\t' && *p != ' ' && | ||
| 1362 | *p != '\r' && *p != '\n' && | ||
| 1363 | (*p < 32 || *p >= 127)) | ||
| 1364 | return FALSE; | ||
| 1365 | p++; | ||
| 1366 | } | ||
| 1367 | |||
| 1368 | return TRUE; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | gint get_quote_level(const gchar *str) | ||
| 1372 | { | ||
| 1373 | const gchar *first_pos; | ||
| 1374 | const gchar *last_pos; | ||
| 1375 | const gchar *p = str; | ||
| 1376 | gint quote_level = -1; | ||
| 1377 | |||
| 1378 | /* speed up line processing by only searching to the last '>' */ | ||
| 1379 | if ((first_pos = strchr(str, '>')) != NULL) { | ||
| 1380 | /* skip a line if it contains a '<' before the initial '>' */ | ||
| 1381 | if (memchr(str, '<', first_pos - str) != NULL) | ||
| 1382 | return -1; | ||
| 1383 | last_pos = strrchr(first_pos, '>'); | ||
| 1384 | } else | ||
| 1385 | return -1; | ||
| 1386 | |||
| 1387 | while (p <= last_pos) { | ||
| 1388 | while (p < last_pos) { | ||
| 1389 | if (g_ascii_isspace(*p)) | ||
| 1390 | p++; | ||
| 1391 | else | ||
| 1392 | break; | ||
| 1393 | } | ||
| 1394 | |||
| 1395 | if (*p == '>') | ||
| 1396 | quote_level++; | ||
| 1397 | else if (*p != '-' && !g_ascii_isspace(*p) && p <= last_pos) { | ||
| 1398 | /* any characters are allowed except '-' and space */ | ||
| 1399 | while (*p != '-' && *p != '>' && !g_ascii_isspace(*p) && | ||
| 1400 | p < last_pos) | ||
| 1401 | p++; | ||
| 1402 | if (*p == '>') | ||
| 1403 | quote_level++; | ||
| 1404 | else | ||
| 1405 | break; | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | p++; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | return quote_level; | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | gint check_line_length(const gchar *str, gint max_chars, gint *line) | ||
| 1415 | { | ||
| 1416 | const gchar *p = str, *q; | ||
| 1417 | gint cur_line = 0, len; | ||
| 1418 | |||
| 1419 | while ((q = strchr(p, '\n')) != NULL) { | ||
| 1420 | len = q - p + 1; | ||
| 1421 | if (len > max_chars) { | ||
| 1422 | if (line) | ||
| 1423 | *line = cur_line; | ||
| 1424 | return -1; | ||
| 1425 | } | ||
| 1426 | p = q + 1; | ||
| 1427 | ++cur_line; | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | len = strlen(p); | ||
| 1431 | if (len > max_chars) { | ||
| 1432 | if (line) | ||
| 1433 | *line = cur_line; | ||
| 1434 | return -1; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | return 0; | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle) | ||
| 1441 | { | ||
| 1442 | register guint haystack_len, needle_len; | ||
| 1443 | gboolean in_squote = FALSE, in_dquote = FALSE; | ||
| 1444 | |||
| 1445 | haystack_len = strlen(haystack); | ||
| 1446 | needle_len = strlen(needle); | ||
| 1447 | |||
| 1448 | if (haystack_len < needle_len || needle_len == 0) | ||
| 1449 | return NULL; | ||
| 1450 | |||
| 1451 | while (haystack_len >= needle_len) { | ||
| 1452 | if (!in_squote && !in_dquote && | ||
| 1453 | !strncmp(haystack, needle, needle_len)) | ||
| 1454 | return (gchar *)haystack; | ||
| 1455 | |||
| 1456 | /* 'foo"bar"' -> foo"bar" | ||
| 1457 | "foo'bar'" -> foo'bar' */ | ||
| 1458 | if (*haystack == '\'') { | ||
| 1459 | if (in_squote) | ||
| 1460 | in_squote = FALSE; | ||
| 1461 | else if (!in_dquote) | ||
| 1462 | in_squote = TRUE; | ||
| 1463 | } else if (*haystack == '\"') { | ||
| 1464 | if (in_dquote) | ||
| 1465 | in_dquote = FALSE; | ||
| 1466 | else if (!in_squote) | ||
| 1467 | in_dquote = TRUE; | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | haystack++; | ||
| 1471 | haystack_len--; | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | return NULL; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | gchar *strcasestr_with_skip_quote(const gchar *haystack, const gchar *needle) | ||
| 1478 | { | ||
| 1479 | register guint haystack_len, needle_len; | ||
| 1480 | gboolean in_squote = FALSE, in_dquote = FALSE; | ||
| 1481 | |||
| 1482 | haystack_len = strlen(haystack); | ||
| 1483 | needle_len = strlen(needle); | ||
| 1484 | |||
| 1485 | if (haystack_len < needle_len || needle_len == 0) | ||
| 1486 | return NULL; | ||
| 1487 | |||
| 1488 | while (haystack_len >= needle_len) { | ||
| 1489 | if (!in_squote && !in_dquote && | ||
| 1490 | !g_ascii_strncasecmp(haystack, needle, needle_len)) | ||
| 1491 | return (gchar *)haystack; | ||
| 1492 | |||
| 1493 | /* 'foo"bar"' -> foo"bar" | ||
| 1494 | "foo'bar'" -> foo'bar' */ | ||
| 1495 | if (*haystack == '\'') { | ||
| 1496 | if (in_squote) | ||
| 1497 | in_squote = FALSE; | ||
| 1498 | else if (!in_dquote) | ||
| 1499 | in_squote = TRUE; | ||
| 1500 | } else if (*haystack == '\"') { | ||
| 1501 | if (in_dquote) | ||
| 1502 | in_dquote = FALSE; | ||
| 1503 | else if (!in_squote) | ||
| 1504 | in_dquote = TRUE; | ||
| 1505 | } | ||
| 1506 | |||
| 1507 | haystack++; | ||
| 1508 | haystack_len--; | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | return NULL; | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | gchar *strchr_parenthesis_close(const gchar *str, gchar op, gchar cl) | ||
| 1515 | { | ||
| 1516 | const gchar *p; | ||
| 1517 | gchar quote_chr = '"'; | ||
| 1518 | gint in_brace; | ||
| 1519 | gboolean in_quote = FALSE; | ||
| 1520 | |||
| 1521 | p = str; | ||
| 1522 | |||
| 1523 | if ((p = strchr_with_skip_quote(p, quote_chr, op))) { | ||
| 1524 | p++; | ||
| 1525 | in_brace = 1; | ||
| 1526 | while (*p) { | ||
| 1527 | if (*p == op && !in_quote) | ||
| 1528 | in_brace++; | ||
| 1529 | else if (*p == cl && !in_quote) | ||
| 1530 | in_brace--; | ||
| 1531 | else if (*p == quote_chr) | ||
| 1532 | in_quote ^= TRUE; | ||
| 1533 | |||
| 1534 | if (in_brace == 0) | ||
| 1535 | return (gchar *)p; | ||
| 1536 | |||
| 1537 | p++; | ||
| 1538 | } | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | return NULL; | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | gchar **strsplit_parenthesis(const gchar *str, gchar op, gchar cl, | ||
| 1545 | gint max_tokens) | ||
| 1546 | { | ||
| 1547 | GSList *string_list = NULL, *slist; | ||
| 1548 | gchar **str_array; | ||
| 1549 | const gchar *s_op, *s_cl; | ||
| 1550 | guint i, n = 1; | ||
| 1551 | |||
| 1552 | g_return_val_if_fail(str != NULL, NULL); | ||
| 1553 | |||
| 1554 | if (max_tokens < 1) | ||
| 1555 | max_tokens = G_MAXINT; | ||
| 1556 | |||
| 1557 | s_op = strchr_with_skip_quote(str, '"', op); | ||
| 1558 | if (!s_op) return NULL; | ||
| 1559 | str = s_op; | ||
| 1560 | s_cl = strchr_parenthesis_close(str, op, cl); | ||
| 1561 | if (s_cl) { | ||
| 1562 | do { | ||
| 1563 | guint len; | ||
| 1564 | gchar *new_string; | ||
| 1565 | |||
| 1566 | str++; | ||
| 1567 | len = s_cl - str; | ||
| 1568 | new_string = g_new(gchar, len + 1); | ||
| 1569 | strncpy(new_string, str, len); | ||
| 1570 | new_string[len] = 0; | ||
| 1571 | string_list = g_slist_prepend(string_list, new_string); | ||
| 1572 | n++; | ||
| 1573 | str = s_cl + 1; | ||
| 1574 | |||
| 1575 | while (*str && g_ascii_isspace(*str)) str++; | ||
| 1576 | if (*str != op) { | ||
| 1577 | string_list = g_slist_prepend(string_list, | ||
| 1578 | g_strdup("")); | ||
| 1579 | n++; | ||
| 1580 | s_op = strchr_with_skip_quote(str, '"', op); | ||
| 1581 | if (!--max_tokens || !s_op) break; | ||
| 1582 | str = s_op; | ||
| 1583 | } else | ||
| 1584 | s_op = str; | ||
| 1585 | s_cl = strchr_parenthesis_close(str, op, cl); | ||
| 1586 | } while (--max_tokens && s_cl); | ||
| 1587 | } | ||
| 1588 | |||
| 1589 | str_array = g_new(gchar*, n); | ||
| 1590 | |||
| 1591 | i = n - 1; | ||
| 1592 | str_array[i--] = NULL; | ||
| 1593 | for (slist = string_list; slist; slist = slist->next) | ||
| 1594 | str_array[i--] = slist->data; | ||
| 1595 | |||
| 1596 | g_slist_free(string_list); | ||
| 1597 | |||
| 1598 | return str_array; | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | gchar **strsplit_with_quote(const gchar *str, const gchar *delim, | ||
| 1602 | gint max_tokens) | ||
| 1603 | { | ||
| 1604 | GSList *string_list = NULL, *slist; | ||
| 1605 | gchar **str_array, *s, *new_str; | ||
| 1606 | guint i, n = 1, len; | ||
| 1607 | |||
| 1608 | g_return_val_if_fail(str != NULL, NULL); | ||
| 1609 | g_return_val_if_fail(delim != NULL, NULL); | ||
| 1610 | |||
| 1611 | if (max_tokens < 1) | ||
| 1612 | max_tokens = G_MAXINT; | ||
| 1613 | |||
| 1614 | s = strstr_with_skip_quote(str, delim); | ||
| 1615 | if (s) { | ||
| 1616 | guint delimiter_len = strlen(delim); | ||
| 1617 | |||
| 1618 | do { | ||
| 1619 | len = s - str; | ||
| 1620 | new_str = g_strndup(str, len); | ||
| 1621 | |||
| 1622 | if (new_str[0] == '\'' || new_str[0] == '\"') { | ||
| 1623 | if (new_str[len - 1] == new_str[0]) { | ||
| 1624 | new_str[len - 1] = '\0'; | ||
| 1625 | memmove(new_str, new_str + 1, len - 1); | ||
| 1626 | } | ||
| 1627 | } | ||
| 1628 | string_list = g_slist_prepend(string_list, new_str); | ||
| 1629 | n++; | ||
| 1630 | str = s + delimiter_len; | ||
| 1631 | s = strstr_with_skip_quote(str, delim); | ||
| 1632 | } while (--max_tokens && s); | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | if (*str) { | ||
| 1636 | new_str = g_strdup(str); | ||
| 1637 | if (new_str[0] == '\'' || new_str[0] == '\"') { | ||
| 1638 | len = strlen(str); | ||
| 1639 | if (new_str[len - 1] == new_str[0]) { | ||
| 1640 | new_str[len - 1] = '\0'; | ||
| 1641 | memmove(new_str, new_str + 1, len - 1); | ||
| 1642 | } | ||
| 1643 | } | ||
| 1644 | string_list = g_slist_prepend(string_list, new_str); | ||
| 1645 | n++; | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | str_array = g_new(gchar*, n); | ||
| 1649 | |||
| 1650 | i = n - 1; | ||
| 1651 | str_array[i--] = NULL; | ||
| 1652 | for (slist = string_list; slist; slist = slist->next) | ||
| 1653 | str_array[i--] = slist->data; | ||
| 1654 | |||
| 1655 | g_slist_free(string_list); | ||
| 1656 | |||
| 1657 | return str_array; | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | gchar **strsplit_csv(const gchar *str, gchar delim, gint max_tokens) | ||
| 1661 | { | ||
| 1662 | GSList *string_list = NULL, *slist; | ||
| 1663 | gchar **str_array, *s, *new_str; | ||
| 1664 | gchar *tmp, *tmpp, *p; | ||
| 1665 | guint i, n = 1, len; | ||
| 1666 | |||
| 1667 | g_return_val_if_fail(str != NULL, NULL); | ||
| 1668 | |||
| 1669 | if (max_tokens < 1) | ||
| 1670 | max_tokens = G_MAXINT; | ||
| 1671 | |||
| 1672 | s = strchr_with_skip_quote(str, '"', delim); | ||
| 1673 | if (s) { | ||
| 1674 | do { | ||
| 1675 | len = s - str; | ||
| 1676 | tmpp = tmp = g_strndup(str, len); | ||
| 1677 | |||
| 1678 | if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) { | ||
| 1679 | tmp[len - 1] = '\0'; | ||
| 1680 | ++tmpp; | ||
| 1681 | p = new_str = g_malloc(len - 1); | ||
| 1682 | while (*tmpp) { | ||
| 1683 | if (*tmpp == '"' && *(tmpp + 1) == '"') | ||
| 1684 | ++tmpp; | ||
| 1685 | *p++ = *tmpp++; | ||
| 1686 | } | ||
| 1687 | *p = '\0'; | ||
| 1688 | g_free(tmp); | ||
| 1689 | } else | ||
| 1690 | new_str = tmp; | ||
| 1691 | |||
| 1692 | string_list = g_slist_prepend(string_list, new_str); | ||
| 1693 | n++; | ||
| 1694 | str = s + 1; | ||
| 1695 | s = strchr_with_skip_quote(str, '"', delim); | ||
| 1696 | } while (--max_tokens && s); | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | if (*str) { | ||
| 1700 | len = strlen(str); | ||
| 1701 | tmpp = tmp = g_strdup(str); | ||
| 1702 | |||
| 1703 | if (tmp[0] == '"' && tmp[len - 1] == tmp[0]) { | ||
| 1704 | tmp[len - 1] = '\0'; | ||
| 1705 | ++tmpp; | ||
| 1706 | p = new_str = g_malloc(len - 1); | ||
| 1707 | while (*tmpp) { | ||
| 1708 | if (*tmpp == '"' && *(tmpp + 1) == '"') | ||
| 1709 | ++tmpp; | ||
| 1710 | *p++ = *tmpp++; | ||
| 1711 | } | ||
| 1712 | *p = '\0'; | ||
| 1713 | g_free(tmp); | ||
| 1714 | } else | ||
| 1715 | new_str = tmp; | ||
| 1716 | |||
| 1717 | string_list = g_slist_prepend(string_list, new_str); | ||
| 1718 | n++; | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | str_array = g_new(gchar*, n); | ||
| 1722 | |||
| 1723 | i = n - 1; | ||
| 1724 | str_array[i--] = NULL; | ||
| 1725 | for (slist = string_list; slist; slist = slist->next) | ||
| 1726 | str_array[i--] = slist->data; | ||
| 1727 | |||
| 1728 | g_slist_free(string_list); | ||
| 1729 | |||
| 1730 | return str_array; | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | /* Concatenate multiple strings into a CSV (TSV) record. | ||
| 1734 | * If delimiter characters or double-quotes appear in the string, | ||
| 1735 | it is enclosed by double quotes. | ||
| 1736 | * Double quote characters are escaped by another double quote. | ||
| 1737 | * Strings must not include line breaks in this implementation. | ||
| 1738 | */ | ||
| 1739 | gchar *strconcat_csv(gchar delim, const gchar *field1, ...) | ||
| 1740 | { | ||
| 1741 | va_list args; | ||
| 1742 | gchar *s; | ||
| 1743 | GString *csv; | ||
| 1744 | gint count = 0; | ||
| 1745 | const gchar *p; | ||
| 1746 | |||
| 1747 | g_return_val_if_fail(field1 != NULL, NULL); | ||
| 1748 | |||
| 1749 | csv = g_string_new(""); | ||
| 1750 | |||
| 1751 | va_start(args, field1); | ||
| 1752 | s = (gchar *)field1; | ||
| 1753 | while (s) { | ||
| 1754 | gboolean add_quote = FALSE; | ||
| 1755 | |||
| 1756 | if (count > 0) | ||
| 1757 | g_string_append_c(csv, delim); | ||
| 1758 | if (strchr(s, delim) != NULL || strchr(s, '"') != NULL) | ||
| 1759 | add_quote = TRUE; | ||
| 1760 | if (add_quote) | ||
| 1761 | g_string_append_c(csv, '"'); | ||
| 1762 | for (p = s; *p != '\0'; p++) { | ||
| 1763 | if (*p == '"') | ||
| 1764 | g_string_append_c(csv, '"'); | ||
| 1765 | g_string_append_c(csv, *p); | ||
| 1766 | } | ||
| 1767 | if (add_quote) | ||
| 1768 | g_string_append_c(csv, '"'); | ||
| 1769 | count++; | ||
| 1770 | s = va_arg(args, gchar*); | ||
| 1771 | } | ||
| 1772 | va_end(args); | ||
| 1773 | |||
| 1774 | return g_string_free(csv, FALSE); | ||
| 1775 | } | ||
| 1776 | |||
| 1777 | gchar *get_abbrev_newsgroup_name(const gchar *group, gint len) | ||
| 1778 | { | ||
| 1779 | gchar *abbrev_group; | ||
| 1780 | gchar *ap; | ||
| 1781 | const gchar *p = group; | ||
| 1782 | const gchar *last; | ||
| 1783 | |||
| 1784 | last = group + strlen(group); | ||
| 1785 | abbrev_group = ap = g_malloc(strlen(group) + 1); | ||
| 1786 | |||
| 1787 | while (*p) { | ||
| 1788 | while (*p == '.') | ||
| 1789 | *ap++ = *p++; | ||
| 1790 | if ((ap - abbrev_group) + (last - p) > len && strchr(p, '.')) { | ||
| 1791 | *ap++ = *p++; | ||
| 1792 | while (*p != '.') p++; | ||
| 1793 | } else { | ||
| 1794 | strcpy(ap, p); | ||
| 1795 | return abbrev_group; | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | *ap = '\0'; | ||
| 1800 | return abbrev_group; | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | gchar *trim_string(const gchar *str, gint len) | ||
| 1804 | { | ||
| 1805 | const gchar *p = str; | ||
| 1806 | gint mb_len; | ||
| 1807 | gchar *new_str; | ||
| 1808 | gint new_len = 0; | ||
| 1809 | |||
| 1810 | if (!str) return NULL; | ||
| 1811 | if (strlen(str) <= len) | ||
| 1812 | return g_strdup(str); | ||
| 1813 | if (g_utf8_validate(str, -1, NULL) == FALSE) | ||
| 1814 | return g_strdup(str); | ||
| 1815 | |||
| 1816 | while (*p != '\0') { | ||
| 1817 | mb_len = g_utf8_skip[*(guchar *)p]; | ||
| 1818 | if (mb_len == 0) | ||
| 1819 | break; | ||
| 1820 | else if (new_len + mb_len > len) | ||
| 1821 | break; | ||
| 1822 | |||
| 1823 | new_len += mb_len; | ||
| 1824 | p += mb_len; | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | Xstrndup_a(new_str, str, new_len, return g_strdup(str)); | ||
| 1828 | return g_strconcat(new_str, "...", NULL); | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | gchar *trim_string_before(const gchar *str, gint len) | ||
| 1832 | { | ||
| 1833 | const gchar *p = str; | ||
| 1834 | gint mb_len; | ||
| 1835 | gint new_len; | ||
| 1836 | |||
| 1837 | if (!str) return NULL; | ||
| 1838 | if ((new_len = strlen(str)) <= len) | ||
| 1839 | return g_strdup(str); | ||
| 1840 | if (g_utf8_validate(str, -1, NULL) == FALSE) | ||
| 1841 | return g_strdup(str); | ||
| 1842 | |||
| 1843 | while (*p != '\0') { | ||
| 1844 | mb_len = g_utf8_skip[*(guchar *)p]; | ||
| 1845 | if (mb_len == 0) | ||
| 1846 | break; | ||
| 1847 | |||
| 1848 | new_len -= mb_len; | ||
| 1849 | p += mb_len; | ||
| 1850 | |||
| 1851 | if (new_len <= len) | ||
| 1852 | break; | ||
| 1853 | } | ||
| 1854 | |||
| 1855 | return g_strconcat("...", p, NULL); | ||
| 1856 | } | ||
| 1857 | |||
| 1858 | GList *uri_list_extract_filenames(const gchar *uri_list) | ||
| 1859 | { | ||
| 1860 | GList *result = NULL; | ||
| 1861 | gchar *file; | ||
| 1862 | |||
| 1863 | #if GLIB_CHECK_VERSION(2, 6, 0) | ||
| 1864 | gchar **uris; | ||
| 1865 | gint i; | ||
| 1866 | |||
| 1867 | uris = g_uri_list_extract_uris(uri_list); | ||
| 1868 | g_return_val_if_fail(uris != NULL, NULL); | ||
| 1869 | |||
| 1870 | for (i = 0; uris[i] != NULL; i++) { | ||
| 1871 | file = g_filename_from_uri(uris[i], NULL, NULL); | ||
| 1872 | if (file) | ||
| 1873 | result = g_list_append(result, file); | ||
| 1874 | } | ||
| 1875 | |||
| 1876 | g_strfreev(uris); | ||
| 1877 | |||
| 1878 | return result; | ||
| 1879 | #else | ||
| 1880 | const gchar *p, *q; | ||
| 1881 | |||
| 1882 | p = uri_list; | ||
| 1883 | |||
| 1884 | while (p) { | ||
| 1885 | if (*p != '#') { | ||
| 1886 | while (g_ascii_isspace(*p)) p++; | ||
| 1887 | if (!strncmp(p, "file:", 5)) { | ||
| 1888 | p += 5; | ||
| 1889 | while (*p == '/' && *(p + 1) == '/') p++; | ||
| 1890 | q = p; | ||
| 1891 | while (*q && *q != '\n' && *q != '\r') q++; | ||
| 1892 | |||
| 1893 | if (q > p) { | ||
| 1894 | q--; | ||
| 1895 | while (q > p && g_ascii_isspace(*q)) | ||
| 1896 | q--; | ||
| 1897 | file = g_malloc(q - p + 2); | ||
| 1898 | strncpy(file, p, q - p + 1); | ||
| 1899 | file[q - p + 1] = '\0'; | ||
| 1900 | decode_uri(file, file); | ||
| 1901 | result = g_list_append(result, file); | ||
| 1902 | } | ||
| 1903 | } | ||
| 1904 | } | ||
| 1905 | p = strchr(p, '\n'); | ||
| 1906 | if (p) p++; | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | return result; | ||
| 1910 | #endif | ||
| 1911 | } | ||
| 1912 | |||
| 1913 | #define HEX_TO_INT(val, hex) \ | ||
| 1914 | { \ | ||
| 1915 | gchar c = hex; \ | ||
| 1916 | \ | ||
| 1917 | if ('0' <= c && c <= '9') { \ | ||
| 1918 | val = c - '0'; \ | ||
| 1919 | } else if ('a' <= c && c <= 'f') { \ | ||
| 1920 | val = c - 'a' + 10; \ | ||
| 1921 | } else if ('A' <= c && c <= 'F') { \ | ||
| 1922 | val = c - 'A' + 10; \ | ||
| 1923 | } else { \ | ||
| 1924 | val = 0; \ | ||
| 1925 | } \ | ||
| 1926 | } | ||
| 1927 | |||
| 1928 | #define INT_TO_HEX(hex, val) \ | ||
| 1929 | { \ | ||
| 1930 | if ((val) < 10) \ | ||
| 1931 | hex = '0' + (val); \ | ||
| 1932 | else \ | ||
| 1933 | hex = 'a' + (val) - 10; \ | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | /* Converts two-digit hexadecimal to decimal. Used for unescaping escaped | ||
| 1937 | * characters. | ||
| 1938 | */ | ||
| 1939 | static gint axtoi(const gchar *hex_str) | ||
| 1940 | { | ||
| 1941 | gint hi, lo; | ||
| 1942 | |||
| 1943 | HEX_TO_INT(hi, hex_str[0]); | ||
| 1944 | HEX_TO_INT(lo, hex_str[1]); | ||
| 1945 | |||
| 1946 | return (hi << 4) + lo; | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | static void get_hex_str(gchar *out, guchar ch) | ||
| 1950 | { | ||
| 1951 | gchar hex; | ||
| 1952 | |||
| 1953 | INT_TO_HEX(hex, ch >> 4); | ||
| 1954 | *out++ = hex; | ||
| 1955 | INT_TO_HEX(hex, ch & 0x0f); | ||
| 1956 | *out++ = hex; | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | gboolean is_uri_string(const gchar *str) | ||
| 1960 | { | ||
| 1961 | return (g_ascii_strncasecmp(str, "http://", 7) == 0 || | ||
| 1962 | g_ascii_strncasecmp(str, "https://", 8) == 0 || | ||
| 1963 | g_ascii_strncasecmp(str, "ftp://", 6) == 0 || | ||
| 1964 | g_ascii_strncasecmp(str, "www.", 4) == 0); | ||
| 1965 | } | ||
| 1966 | |||
| 1967 | gchar *get_uri_path(const gchar *uri) | ||
| 1968 | { | ||
| 1969 | if (g_ascii_strncasecmp(uri, "http://", 7) == 0) | ||
| 1970 | return (gchar *)(uri + 7); | ||
| 1971 | else if (g_ascii_strncasecmp(uri, "https://", 8) == 0) | ||
| 1972 | return (gchar *)(uri + 8); | ||
| 1973 | else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0) | ||
| 1974 | return (gchar *)(uri + 6); | ||
| 1975 | else | ||
| 1976 | return (gchar *)uri; | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | gint get_uri_len(const gchar *str) | ||
| 1980 | { | ||
| 1981 | const gchar *p; | ||
| 1982 | |||
| 1983 | if (is_uri_string(str)) { | ||
| 1984 | for (p = str; *p != '\0'; p++) { | ||
| 1985 | if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p)) | ||
| 1986 | break; | ||
| 1987 | } | ||
| 1988 | return p - str; | ||
| 1989 | } | ||
| 1990 | |||
| 1991 | return 0; | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | /* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by | ||
| 1995 | * plusses, and escape characters are used) | ||
| 1996 | * Note: decoded_uri and encoded_uri can point the same location | ||
| 1997 | */ | ||
| 1998 | void decode_uri(gchar *decoded_uri, const gchar *encoded_uri) | ||
| 1999 | { | ||
| 2000 | gchar *dec = decoded_uri; | ||
| 2001 | const gchar *enc = encoded_uri; | ||
| 2002 | |||
| 2003 | while (*enc) { | ||
| 2004 | if (*enc == '%') { | ||
| 2005 | enc++; | ||
| 2006 | if (g_ascii_isxdigit((guchar)enc[0]) && | ||
| 2007 | g_ascii_isxdigit((guchar)enc[1])) { | ||
| 2008 | *dec = axtoi(enc); | ||
| 2009 | dec++; | ||
| 2010 | enc += 2; | ||
| 2011 | } | ||
| 2012 | } else { | ||
| 2013 | if (*enc == '+') | ||
| 2014 | *dec = ' '; | ||
| 2015 | else | ||
| 2016 | *dec = *enc; | ||
| 2017 | dec++; | ||
| 2018 | enc++; | ||
| 2019 | } | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | *dec = '\0'; | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | void decode_xdigit_encoded_str(gchar *decoded, const gchar *encoded) | ||
| 2026 | { | ||
| 2027 | gchar *dec = decoded; | ||
| 2028 | const gchar *enc = encoded; | ||
| 2029 | |||
| 2030 | while (*enc) { | ||
| 2031 | if (*enc == '%') { | ||
| 2032 | enc++; | ||
| 2033 | if (g_ascii_isxdigit((guchar)enc[0]) && | ||
| 2034 | g_ascii_isxdigit((guchar)enc[1])) { | ||
| 2035 | *dec++ = axtoi(enc); | ||
| 2036 | enc += 2; | ||
| 2037 | } | ||
| 2038 | } else | ||
| 2039 | *dec++ = *enc++; | ||
| 2040 | } | ||
| 2041 | |||
| 2042 | *dec = '\0'; | ||
| 2043 | } | ||
| 2044 | |||
| 2045 | gchar *encode_uri(const gchar *filename) | ||
| 2046 | { | ||
| 2047 | gchar *uri; | ||
| 2048 | |||
| 2049 | uri = g_filename_to_uri(filename, NULL, NULL); | ||
| 2050 | if (!uri) | ||
| 2051 | uri = g_strconcat("file://", filename, NULL); | ||
| 2052 | |||
| 2053 | return uri; | ||
| 2054 | } | ||
| 2055 | |||
| 2056 | gchar *uriencode_for_filename(const gchar *filename) | ||
| 2057 | { | ||
| 2058 | const gchar *p = filename; | ||
| 2059 | gchar *enc, *outp; | ||
| 2060 | |||
| 2061 | outp = enc = g_malloc(strlen(filename) * 3 + 1); | ||
| 2062 | |||
| 2063 | for (p = filename; *p != '\0'; p++) { | ||
| 2064 | if (strchr("\t\r\n\"'\\/:;*?<>|", *p)) { | ||
| 2065 | *outp++ = '%'; | ||
| 2066 | get_hex_str(outp, *p); | ||
| 2067 | outp += 2; | ||
| 2068 | } else | ||
| 2069 | *outp++ = *p; | ||
| 2070 | } | ||
| 2071 | |||
| 2072 | *outp = '\0'; | ||
| 2073 | return enc; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | gchar *uriencode_for_mailto(const gchar *mailto) | ||
| 2077 | { | ||
| 2078 | const gchar *p = mailto; | ||
| 2079 | gchar *enc, *outp; | ||
| 2080 | |||
| 2081 | outp = enc = g_malloc(strlen(mailto) * 3 + 1); | ||
| 2082 | |||
| 2083 | for (p = mailto; *p != '\0'; p++) { | ||
| 2084 | if (*p == '+') { | ||
| 2085 | *outp++ = '%'; | ||
| 2086 | get_hex_str(outp, *p); | ||
| 2087 | outp += 2; | ||
| 2088 | } else | ||
| 2089 | *outp++ = *p; | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | *outp = '\0'; | ||
| 2093 | return enc; | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc, | ||
| 2097 | gchar **subject, gchar **inreplyto, gchar **body) | ||
| 2098 | { | ||
| 2099 | gchar *tmp_mailto; | ||
| 2100 | gchar *p; | ||
| 2101 | |||
| 2102 | Xstrdup_a(tmp_mailto, mailto, return -1); | ||
| 2103 | |||
| 2104 | if (!strncmp(tmp_mailto, "mailto:", 7)) | ||
| 2105 | tmp_mailto += 7; | ||
| 2106 | |||
| 2107 | p = strchr(tmp_mailto, '?'); | ||
| 2108 | if (p) { | ||
| 2109 | *p = '\0'; | ||
| 2110 | p++; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | if (to && !*to) { | ||
| 2114 | *to = g_malloc(strlen(tmp_mailto) + 1); | ||
| 2115 | decode_uri(*to, tmp_mailto); | ||
| 2116 | } | ||
| 2117 | |||
| 2118 | while (p) { | ||
| 2119 | gchar *field, *value; | ||
| 2120 | |||
| 2121 | field = p; | ||
| 2122 | |||
| 2123 | p = strchr(p, '='); | ||
| 2124 | if (!p) break; | ||
| 2125 | *p = '\0'; | ||
| 2126 | p++; | ||
| 2127 | |||
| 2128 | value = p; | ||
| 2129 | |||
| 2130 | p = strchr(p, '&'); | ||
| 2131 | if (p) { | ||
| 2132 | *p = '\0'; | ||
| 2133 | p++; | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | if (*value == '\0') continue; | ||
| 2137 | |||
| 2138 | if (cc && !*cc && !g_ascii_strcasecmp(field, "cc")) { | ||
| 2139 | *cc = g_malloc(strlen(value) + 1); | ||
| 2140 | decode_uri(*cc, value); | ||
| 2141 | } else if (bcc && !*bcc && !g_ascii_strcasecmp(field, "bcc")) { | ||
| 2142 | *bcc = g_malloc(strlen(value) + 1); | ||
| 2143 | decode_uri(*bcc, value); | ||
| 2144 | } else if (subject && !*subject && | ||
| 2145 | !g_ascii_strcasecmp(field, "subject")) { | ||
| 2146 | *subject = g_malloc(strlen(value) + 1); | ||
| 2147 | decode_uri(*subject, value); | ||
| 2148 | } else if (inreplyto && !*inreplyto && | ||
| 2149 | !g_ascii_strcasecmp(field, "in-reply-to")) { | ||
| 2150 | *inreplyto = g_malloc(strlen(value) + 1); | ||
| 2151 | decode_uri(*inreplyto, value); | ||
| 2152 | } else if (body && !*body && | ||
| 2153 | !g_ascii_strcasecmp(field, "body")) { | ||
| 2154 | *body = g_malloc(strlen(value) + 1); | ||
| 2155 | decode_uri(*body, value); | ||
| 2156 | } | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | return 0; | ||
| 2160 | } | ||
| 2161 | |||
| 2162 | static gchar *startup_dir = NULL; | ||
| 2163 | static gchar *rc_dir = NULL; | ||
| 2164 | |||
| 2165 | void set_startup_dir(void) | ||
| 2166 | { | ||
| 2167 | #ifdef G_OS_WIN32 | ||
| 2168 | if (!startup_dir) { | ||
| 2169 | startup_dir = g_win32_get_package_installation_directory | ||
| 2170 | (NULL, NULL); | ||
| 2171 | if (startup_dir) { | ||
| 2172 | if (g_chdir(startup_dir) < 0) { | ||
| 2173 | FILE_OP_ERROR(startup_dir, "chdir"); | ||
| 2174 | g_free(startup_dir); | ||
| 2175 | startup_dir = g_get_current_dir(); | ||
| 2176 | } | ||
| 2177 | } else | ||
| 2178 | startup_dir = g_get_current_dir(); | ||
| 2179 | } | ||
| 2180 | #elif defined(__APPLE__) | ||
| 2181 | if (!startup_dir) { | ||
| 2182 | const gchar *path; | ||
| 2183 | NSAutoreleasePool *pool; | ||
| 2184 | |||
| 2185 | pool = [[NSAutoreleasePool alloc] init]; | ||
| 2186 | |||
| 2187 | path = [[[NSBundle mainBundle] bundlePath] UTF8String]; | ||
| 2188 | startup_dir = g_strdup(path); | ||
| 2189 | |||
| 2190 | [pool release]; | ||
| 2191 | |||
| 2192 | if (!startup_dir) | ||
| 2193 | startup_dir = g_get_current_dir(); | ||
| 2194 | } | ||
| 2195 | #else | ||
| 2196 | if (!startup_dir) | ||
| 2197 | startup_dir = g_get_current_dir(); | ||
| 2198 | #endif | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | void set_rc_dir(const gchar *dir) | ||
| 2202 | { | ||
| 2203 | if (rc_dir) | ||
| 2204 | g_free(rc_dir); | ||
| 2205 | |||
| 2206 | if (dir) { | ||
| 2207 | if (g_path_is_absolute(dir)) | ||
| 2208 | rc_dir = g_strdup(dir); | ||
| 2209 | else | ||
| 2210 | rc_dir = g_strconcat(get_startup_dir(), | ||
| 2211 | G_DIR_SEPARATOR_S, dir, NULL); | ||
| 2212 | } else | ||
| 2213 | rc_dir = NULL; | ||
| 2214 | } | ||
| 2215 | |||
| 2216 | const gchar *get_startup_dir(void) | ||
| 2217 | { | ||
| 2218 | if (!startup_dir) | ||
| 2219 | set_startup_dir(); | ||
| 2220 | |||
| 2221 | return startup_dir; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | #ifdef G_OS_WIN32 | ||
| 2225 | static gchar *get_win32_special_folder_path(gint nfolder) | ||
| 2226 | { | ||
| 2227 | gchar *folder = NULL; | ||
| 2228 | HRESULT hr; | ||
| 2229 | |||
| 2230 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 2231 | wchar_t path[MAX_PATH + 1]; | ||
| 2232 | hr = SHGetFolderPathW(NULL, nfolder, NULL, 0, path); | ||
| 2233 | if (hr == S_OK) | ||
| 2234 | folder = g_utf16_to_utf8(path, -1, NULL, NULL, NULL); | ||
| 2235 | } else { | ||
| 2236 | gchar path[MAX_PATH + 1]; | ||
| 2237 | hr = SHGetFolderPathA(NULL, nfolder, NULL, 0, path); | ||
| 2238 | if (hr == S_OK) | ||
| 2239 | folder = g_locale_to_utf8(path, -1, NULL, NULL, NULL); | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | return folder; | ||
| 2243 | } | ||
| 2244 | #endif | ||
| 2245 | |||
| 2246 | const gchar *get_home_dir(void) | ||
| 2247 | { | ||
| 2248 | #ifdef G_OS_WIN32 | ||
| 2249 | static const gchar *home_dir = NULL; | ||
| 2250 | |||
| 2251 | if (!home_dir) { | ||
| 2252 | home_dir = g_get_home_dir(); | ||
| 2253 | if (!home_dir) | ||
| 2254 | home_dir = "C:\\Sylpheed"; | ||
| 2255 | } | ||
| 2256 | |||
| 2257 | return home_dir; | ||
| 2258 | #else | ||
| 2259 | return g_get_home_dir(); | ||
| 2260 | #endif | ||
| 2261 | } | ||
| 2262 | |||
| 2263 | const gchar *get_document_dir(void) | ||
| 2264 | { | ||
| 2265 | #ifdef G_OS_WIN32 | ||
| 2266 | static const gchar *document_dir = NULL; | ||
| 2267 | HRESULT hr; | ||
| 2268 | |||
| 2269 | if (!document_dir) { | ||
| 2270 | document_dir = get_win32_special_folder_path(CSIDL_PERSONAL); | ||
| 2271 | if (!document_dir) | ||
| 2272 | document_dir = get_home_dir(); | ||
| 2273 | } | ||
| 2274 | |||
| 2275 | return document_dir; | ||
| 2276 | #elif defined(__APPLE__) | ||
| 2277 | static const gchar *document_dir = NULL; | ||
| 2278 | |||
| 2279 | if (!document_dir) { | ||
| 2280 | document_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 2281 | "Documents", NULL); | ||
| 2282 | } | ||
| 2283 | |||
| 2284 | return document_dir; | ||
| 2285 | #else | ||
| 2286 | return get_home_dir(); | ||
| 2287 | #endif | ||
| 2288 | } | ||
| 2289 | |||
| 2290 | const gchar *get_rc_dir(void) | ||
| 2291 | { | ||
| 2292 | if (!rc_dir) { | ||
| 2293 | #ifdef G_OS_WIN32 | ||
| 2294 | gchar *appdata; | ||
| 2295 | |||
| 2296 | appdata = get_win32_special_folder_path(CSIDL_APPDATA); | ||
| 2297 | if (appdata) | ||
| 2298 | rc_dir = g_strconcat(appdata, G_DIR_SEPARATOR_S, | ||
| 2299 | RC_DIR, NULL); | ||
| 2300 | else | ||
| 2301 | rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 2302 | RC_DIR, NULL); | ||
| 2303 | g_free(appdata); | ||
| 2304 | #elif defined(__APPLE__) | ||
| 2305 | rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 2306 | "Library", G_DIR_SEPARATOR_S, | ||
| 2307 | "Application Support", G_DIR_SEPARATOR_S, | ||
| 2308 | RC_DIR, NULL); | ||
| 2309 | #else | ||
| 2310 | rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 2311 | RC_DIR, NULL); | ||
| 2312 | #endif | ||
| 2313 | } | ||
| 2314 | |||
| 2315 | return rc_dir; | ||
| 2316 | } | ||
| 2317 | |||
| 2318 | const gchar *get_old_rc_dir(void) | ||
| 2319 | { | ||
| 2320 | static gchar *old_rc_dir = NULL; | ||
| 2321 | |||
| 2322 | if (!old_rc_dir) | ||
| 2323 | old_rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, | ||
| 2324 | OLD_RC_DIR, NULL); | ||
| 2325 | |||
| 2326 | return old_rc_dir; | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | const gchar *get_mail_base_dir(void) | ||
| 2330 | { | ||
| 2331 | #if defined(G_OS_WIN32) || defined(__APPLE__) | ||
| 2332 | static gchar *mail_base_dir = NULL; | ||
| 2333 | |||
| 2334 | if (!mail_base_dir) | ||
| 2335 | mail_base_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2336 | "Mailboxes", NULL); | ||
| 2337 | |||
| 2338 | return mail_base_dir; | ||
| 2339 | #else | ||
| 2340 | return get_home_dir(); | ||
| 2341 | #endif | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | const gchar *get_news_cache_dir(void) | ||
| 2345 | { | ||
| 2346 | static gchar *news_cache_dir = NULL; | ||
| 2347 | |||
| 2348 | if (!news_cache_dir) | ||
| 2349 | news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2350 | NEWS_CACHE_DIR, NULL); | ||
| 2351 | |||
| 2352 | return news_cache_dir; | ||
| 2353 | } | ||
| 2354 | |||
| 2355 | const gchar *get_imap_cache_dir(void) | ||
| 2356 | { | ||
| 2357 | static gchar *imap_cache_dir = NULL; | ||
| 2358 | |||
| 2359 | if (!imap_cache_dir) | ||
| 2360 | imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2361 | IMAP_CACHE_DIR, NULL); | ||
| 2362 | |||
| 2363 | return imap_cache_dir; | ||
| 2364 | } | ||
| 2365 | |||
| 2366 | const gchar *get_mime_tmp_dir(void) | ||
| 2367 | { | ||
| 2368 | static gchar *mime_tmp_dir = NULL; | ||
| 2369 | |||
| 2370 | if (!mime_tmp_dir) | ||
| 2371 | mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2372 | MIME_TMP_DIR, NULL); | ||
| 2373 | |||
| 2374 | return mime_tmp_dir; | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | const gchar *get_template_dir(void) | ||
| 2378 | { | ||
| 2379 | static gchar *template_dir = NULL; | ||
| 2380 | |||
| 2381 | if (!template_dir) | ||
| 2382 | template_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2383 | TEMPLATE_DIR, NULL); | ||
| 2384 | |||
| 2385 | return template_dir; | ||
| 2386 | } | ||
| 2387 | |||
| 2388 | const gchar *get_tmp_dir(void) | ||
| 2389 | { | ||
| 2390 | static gchar *tmp_dir = NULL; | ||
| 2391 | |||
| 2392 | if (!tmp_dir) | ||
| 2393 | tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, | ||
| 2394 | TMP_DIR, NULL); | ||
| 2395 | |||
| 2396 | return tmp_dir; | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | gchar *get_tmp_file(void) | ||
| 2400 | { | ||
| 2401 | gchar *tmp_file; | ||
| 2402 | static guint32 id = 0; | ||
| 2403 | |||
| 2404 | tmp_file = g_strdup_printf("%s%ctmpfile.%08x", | ||
| 2405 | get_tmp_dir(), G_DIR_SEPARATOR, id++); | ||
| 2406 | |||
| 2407 | return tmp_file; | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | const gchar *get_domain_name(void) | ||
| 2411 | { | ||
| 2412 | static gchar *domain_name = NULL; | ||
| 2413 | |||
| 2414 | if (!domain_name) { | ||
| 2415 | gchar buf[128] = ""; | ||
| 2416 | struct hostent *hp; | ||
| 2417 | |||
| 2418 | if (gethostname(buf, sizeof(buf)) < 0) { | ||
| 2419 | perror("gethostname"); | ||
| 2420 | domain_name = "unknown"; | ||
| 2421 | } else { | ||
| 2422 | buf[sizeof(buf) - 1] = '\0'; | ||
| 2423 | if ((hp = my_gethostbyname(buf)) == NULL) { | ||
| 2424 | perror("gethostbyname"); | ||
| 2425 | domain_name = g_strdup(buf); | ||
| 2426 | } else { | ||
| 2427 | domain_name = g_strdup(hp->h_name); | ||
| 2428 | } | ||
| 2429 | } | ||
| 2430 | |||
| 2431 | debug_print("domain name = %s\n", domain_name); | ||
| 2432 | if (is_next_nonascii(domain_name)) { | ||
| 2433 | g_warning("invalid domain name: %s\n", domain_name); | ||
| 2434 | g_free(domain_name); | ||
| 2435 | domain_name = "unknown"; | ||
| 2436 | } | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | return domain_name; | ||
| 2440 | } | ||
| 2441 | |||
| 2442 | off_t get_file_size(const gchar *file) | ||
| 2443 | { | ||
| 2444 | GStatBuf s; | ||
| 2445 | |||
| 2446 | if (g_stat(file, &s) < 0) { | ||
| 2447 | FILE_OP_ERROR(file, "stat"); | ||
| 2448 | return -1; | ||
| 2449 | } | ||
| 2450 | |||
| 2451 | return s.st_size; | ||
| 2452 | } | ||
| 2453 | |||
| 2454 | off_t get_file_size_as_crlf(const gchar *file) | ||
| 2455 | { | ||
| 2456 | FILE *fp; | ||
| 2457 | off_t size = 0; | ||
| 2458 | gchar buf[BUFFSIZE]; | ||
| 2459 | |||
| 2460 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 2461 | FILE_OP_ERROR(file, "fopen"); | ||
| 2462 | return -1; | ||
| 2463 | } | ||
| 2464 | |||
| 2465 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 2466 | strretchomp(buf); | ||
| 2467 | size += strlen(buf) + 2; | ||
| 2468 | } | ||
| 2469 | |||
| 2470 | if (ferror(fp)) { | ||
| 2471 | FILE_OP_ERROR(file, "fgets"); | ||
| 2472 | size = -1; | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | fclose(fp); | ||
| 2476 | |||
| 2477 | return size; | ||
| 2478 | } | ||
| 2479 | |||
| 2480 | off_t get_left_file_size(FILE *fp) | ||
| 2481 | { | ||
| 2482 | glong pos; | ||
| 2483 | glong end; | ||
| 2484 | off_t size; | ||
| 2485 | |||
| 2486 | if ((pos = ftell(fp)) < 0) { | ||
| 2487 | perror("ftell"); | ||
| 2488 | return -1; | ||
| 2489 | } | ||
| 2490 | if (fseek(fp, 0L, SEEK_END) < 0) { | ||
| 2491 | perror("fseek"); | ||
| 2492 | return -1; | ||
| 2493 | } | ||
| 2494 | if ((end = ftell(fp)) < 0) { | ||
| 2495 | perror("fseek"); | ||
| 2496 | return -1; | ||
| 2497 | } | ||
| 2498 | size = end - pos; | ||
| 2499 | if (fseek(fp, pos, SEEK_SET) < 0) { | ||
| 2500 | perror("fseek"); | ||
| 2501 | return -1; | ||
| 2502 | } | ||
| 2503 | |||
| 2504 | return size; | ||
| 2505 | } | ||
| 2506 | |||
| 2507 | gint get_last_empty_line_size(FILE *fp, off_t size) | ||
| 2508 | { | ||
| 2509 | glong pos; | ||
| 2510 | gint lsize = 0; | ||
| 2511 | gchar buf[4]; | ||
| 2512 | size_t nread; | ||
| 2513 | |||
| 2514 | if (size < 4) | ||
| 2515 | return -1; | ||
| 2516 | |||
| 2517 | if ((pos = ftell(fp)) < 0) { | ||
| 2518 | perror("ftell"); | ||
| 2519 | return -1; | ||
| 2520 | } | ||
| 2521 | if (fseek(fp, size - 4, SEEK_CUR) < 0) { | ||
| 2522 | perror("fseek"); | ||
| 2523 | return -1; | ||
| 2524 | } | ||
| 2525 | |||
| 2526 | /* read last 4 bytes */ | ||
| 2527 | nread = fread(buf, sizeof(buf), 1, fp); | ||
| 2528 | if (nread != 1) { | ||
| 2529 | perror("fread"); | ||
| 2530 | return -1; | ||
| 2531 | } | ||
| 2532 | /* g_print("last 4 bytes: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); */ | ||
| 2533 | if (buf[3] == '\n') { | ||
| 2534 | if (buf[2] == '\n') | ||
| 2535 | lsize = 1; | ||
| 2536 | else if (buf[2] == '\r') { | ||
| 2537 | if (buf[1] == '\n') | ||
| 2538 | lsize = 2; | ||
| 2539 | } | ||
| 2540 | } | ||
| 2541 | |||
| 2542 | if (fseek(fp, pos, SEEK_SET) < 0) { | ||
| 2543 | perror("fseek"); | ||
| 2544 | return -1; | ||
| 2545 | } | ||
| 2546 | |||
| 2547 | return lsize; | ||
| 2548 | } | ||
| 2549 | |||
| 2550 | gboolean file_exist(const gchar *file, gboolean allow_fifo) | ||
| 2551 | { | ||
| 2552 | if (file == NULL) | ||
| 2553 | return FALSE; | ||
| 2554 | |||
| 2555 | if (allow_fifo) { | ||
| 2556 | GStatBuf s; | ||
| 2557 | |||
| 2558 | if (g_stat(file, &s) < 0) { | ||
| 2559 | if (ENOENT != errno) FILE_OP_ERROR(file, "stat"); | ||
| 2560 | return FALSE; | ||
| 2561 | } | ||
| 2562 | if (S_ISREG(s.st_mode) || S_ISFIFO(s.st_mode)) | ||
| 2563 | return TRUE; | ||
| 2564 | } else { | ||
| 2565 | return g_file_test(file, G_FILE_TEST_IS_REGULAR); | ||
| 2566 | } | ||
| 2567 | |||
| 2568 | return FALSE; | ||
| 2569 | } | ||
| 2570 | |||
| 2571 | gboolean is_dir_exist(const gchar *dir) | ||
| 2572 | { | ||
| 2573 | if (dir == NULL) | ||
| 2574 | return FALSE; | ||
| 2575 | |||
| 2576 | return g_file_test(dir, G_FILE_TEST_IS_DIR); | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | gboolean is_file_entry_exist(const gchar *file) | ||
| 2580 | { | ||
| 2581 | if (file == NULL) | ||
| 2582 | return FALSE; | ||
| 2583 | |||
| 2584 | return g_file_test(file, G_FILE_TEST_EXISTS); | ||
| 2585 | } | ||
| 2586 | |||
| 2587 | gboolean dirent_is_regular_file(struct dirent *d) | ||
| 2588 | { | ||
| 2589 | #ifdef HAVE_DIRENT_D_TYPE | ||
| 2590 | if (d->d_type == DT_REG) | ||
| 2591 | return TRUE; | ||
| 2592 | else if (d->d_type != DT_UNKNOWN) | ||
| 2593 | return FALSE; | ||
| 2594 | #endif | ||
| 2595 | |||
| 2596 | return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR); | ||
| 2597 | } | ||
| 2598 | |||
| 2599 | gboolean dirent_is_directory(struct dirent *d) | ||
| 2600 | { | ||
| 2601 | #ifdef HAVE_DIRENT_D_TYPE | ||
| 2602 | if (d->d_type == DT_DIR) | ||
| 2603 | return TRUE; | ||
| 2604 | else if (d->d_type != DT_UNKNOWN) | ||
| 2605 | return FALSE; | ||
| 2606 | #endif | ||
| 2607 | |||
| 2608 | return g_file_test(d->d_name, G_FILE_TEST_IS_DIR); | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | gint change_dir(const gchar *dir) | ||
| 2612 | { | ||
| 2613 | gchar *prevdir = NULL; | ||
| 2614 | |||
| 2615 | if (debug_mode) | ||
| 2616 | prevdir = g_get_current_dir(); | ||
| 2617 | |||
| 2618 | if (g_chdir(dir) < 0) { | ||
| 2619 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2620 | if (debug_mode) g_free(prevdir); | ||
| 2621 | return -1; | ||
| 2622 | } else if (debug_mode) { | ||
| 2623 | gchar *cwd; | ||
| 2624 | |||
| 2625 | cwd = g_get_current_dir(); | ||
| 2626 | if (strcmp(prevdir, cwd) != 0) | ||
| 2627 | g_print("current dir: %s\n", cwd); | ||
| 2628 | g_free(cwd); | ||
| 2629 | g_free(prevdir); | ||
| 2630 | } | ||
| 2631 | |||
| 2632 | return 0; | ||
| 2633 | } | ||
| 2634 | |||
| 2635 | gint make_dir(const gchar *dir) | ||
| 2636 | { | ||
| 2637 | if (g_mkdir(dir, S_IRWXU) < 0) { | ||
| 2638 | FILE_OP_ERROR(dir, "mkdir"); | ||
| 2639 | return -1; | ||
| 2640 | } | ||
| 2641 | if (g_chmod(dir, S_IRWXU) < 0) | ||
| 2642 | FILE_OP_ERROR(dir, "chmod"); | ||
| 2643 | |||
| 2644 | return 0; | ||
| 2645 | } | ||
| 2646 | |||
| 2647 | gint make_dir_hier(const gchar *dir) | ||
| 2648 | { | ||
| 2649 | gchar *parent_dir; | ||
| 2650 | const gchar *p; | ||
| 2651 | |||
| 2652 | for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) { | ||
| 2653 | parent_dir = g_strndup(dir, p - dir); | ||
| 2654 | if (*parent_dir != '\0') { | ||
| 2655 | if (!is_dir_exist(parent_dir)) { | ||
| 2656 | if (make_dir(parent_dir) < 0) { | ||
| 2657 | g_free(parent_dir); | ||
| 2658 | return -1; | ||
| 2659 | } | ||
| 2660 | } | ||
| 2661 | } | ||
| 2662 | g_free(parent_dir); | ||
| 2663 | } | ||
| 2664 | |||
| 2665 | if (!is_dir_exist(dir)) { | ||
| 2666 | if (make_dir(dir) < 0) | ||
| 2667 | return -1; | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | return 0; | ||
| 2671 | } | ||
| 2672 | |||
| 2673 | gint remove_all_files(const gchar *dir) | ||
| 2674 | { | ||
| 2675 | GDir *dp; | ||
| 2676 | const gchar *dir_name; | ||
| 2677 | gchar *prev_dir; | ||
| 2678 | |||
| 2679 | prev_dir = g_get_current_dir(); | ||
| 2680 | |||
| 2681 | if (g_chdir(dir) < 0) { | ||
| 2682 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2683 | g_free(prev_dir); | ||
| 2684 | return -1; | ||
| 2685 | } | ||
| 2686 | |||
| 2687 | if ((dp = g_dir_open(".", 0, NULL)) == NULL) { | ||
| 2688 | g_warning("failed to open directory: %s\n", dir); | ||
| 2689 | g_free(prev_dir); | ||
| 2690 | return -1; | ||
| 2691 | } | ||
| 2692 | |||
| 2693 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 2694 | if (g_unlink(dir_name) < 0) | ||
| 2695 | FILE_OP_ERROR(dir_name, "unlink"); | ||
| 2696 | } | ||
| 2697 | |||
| 2698 | g_dir_close(dp); | ||
| 2699 | |||
| 2700 | if (g_chdir(prev_dir) < 0) { | ||
| 2701 | FILE_OP_ERROR(prev_dir, "chdir"); | ||
| 2702 | g_free(prev_dir); | ||
| 2703 | return -1; | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | g_free(prev_dir); | ||
| 2707 | |||
| 2708 | return 0; | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | gint remove_numbered_files(const gchar *dir, guint first, guint last) | ||
| 2712 | { | ||
| 2713 | GDir *dp; | ||
| 2714 | const gchar *dir_name; | ||
| 2715 | gchar *prev_dir; | ||
| 2716 | guint file_no; | ||
| 2717 | |||
| 2718 | prev_dir = g_get_current_dir(); | ||
| 2719 | |||
| 2720 | if (g_chdir(dir) < 0) { | ||
| 2721 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2722 | g_free(prev_dir); | ||
| 2723 | return -1; | ||
| 2724 | } | ||
| 2725 | |||
| 2726 | if ((dp = g_dir_open(".", 0, NULL)) == NULL) { | ||
| 2727 | g_warning("failed to open directory: %s\n", dir); | ||
| 2728 | g_free(prev_dir); | ||
| 2729 | return -1; | ||
| 2730 | } | ||
| 2731 | |||
| 2732 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 2733 | file_no = to_unumber(dir_name); | ||
| 2734 | if (file_no > 0 && first <= file_no && file_no <= last) { | ||
| 2735 | if (is_dir_exist(dir_name)) | ||
| 2736 | continue; | ||
| 2737 | if (g_unlink(dir_name) < 0) | ||
| 2738 | FILE_OP_ERROR(dir_name, "unlink"); | ||
| 2739 | } | ||
| 2740 | } | ||
| 2741 | |||
| 2742 | g_dir_close(dp); | ||
| 2743 | |||
| 2744 | if (g_chdir(prev_dir) < 0) { | ||
| 2745 | FILE_OP_ERROR(prev_dir, "chdir"); | ||
| 2746 | g_free(prev_dir); | ||
| 2747 | return -1; | ||
| 2748 | } | ||
| 2749 | |||
| 2750 | g_free(prev_dir); | ||
| 2751 | |||
| 2752 | return 0; | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | gint remove_all_numbered_files(const gchar *dir) | ||
| 2756 | { | ||
| 2757 | return remove_numbered_files(dir, 0, UINT_MAX); | ||
| 2758 | } | ||
| 2759 | |||
| 2760 | gint remove_expired_files(const gchar *dir, guint hours) | ||
| 2761 | { | ||
| 2762 | GDir *dp; | ||
| 2763 | const gchar *dir_name; | ||
| 2764 | GStatBuf s; | ||
| 2765 | gchar *prev_dir; | ||
| 2766 | guint file_no; | ||
| 2767 | time_t mtime, now, expire_time; | ||
| 2768 | |||
| 2769 | prev_dir = g_get_current_dir(); | ||
| 2770 | |||
| 2771 | if (g_chdir(dir) < 0) { | ||
| 2772 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2773 | g_free(prev_dir); | ||
| 2774 | return -1; | ||
| 2775 | } | ||
| 2776 | |||
| 2777 | if ((dp = g_dir_open(".", 0, NULL)) == NULL) { | ||
| 2778 | g_warning("failed to open directory: %s\n", dir); | ||
| 2779 | g_free(prev_dir); | ||
| 2780 | return -1; | ||
| 2781 | } | ||
| 2782 | |||
| 2783 | now = time(NULL); | ||
| 2784 | expire_time = hours * 60 * 60; | ||
| 2785 | |||
| 2786 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 2787 | file_no = to_unumber(dir_name); | ||
| 2788 | if (file_no > 0) { | ||
| 2789 | if (g_stat(dir_name, &s) < 0) { | ||
| 2790 | FILE_OP_ERROR(dir_name, "stat"); | ||
| 2791 | continue; | ||
| 2792 | } | ||
| 2793 | if (S_ISDIR(s.st_mode)) | ||
| 2794 | continue; | ||
| 2795 | mtime = MAX(s.st_mtime, s.st_atime); | ||
| 2796 | if (now - mtime > expire_time) { | ||
| 2797 | if (g_unlink(dir_name) < 0) | ||
| 2798 | FILE_OP_ERROR(dir_name, "unlink"); | ||
| 2799 | } | ||
| 2800 | } | ||
| 2801 | } | ||
| 2802 | |||
| 2803 | g_dir_close(dp); | ||
| 2804 | |||
| 2805 | if (g_chdir(prev_dir) < 0) { | ||
| 2806 | FILE_OP_ERROR(prev_dir, "chdir"); | ||
| 2807 | g_free(prev_dir); | ||
| 2808 | return -1; | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | g_free(prev_dir); | ||
| 2812 | |||
| 2813 | return 0; | ||
| 2814 | } | ||
| 2815 | |||
| 2816 | static gint remove_dir_recursive_real(const gchar *dir) | ||
| 2817 | { | ||
| 2818 | GStatBuf s; | ||
| 2819 | GDir *dp; | ||
| 2820 | const gchar *dir_name; | ||
| 2821 | gchar *prev_dir; | ||
| 2822 | |||
| 2823 | if (g_stat(dir, &s) < 0) { | ||
| 2824 | FILE_OP_ERROR(dir, "stat"); | ||
| 2825 | if (ENOENT == errno) return 0; | ||
| 2826 | return -1; | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | if (!S_ISDIR(s.st_mode)) { | ||
| 2830 | if (g_unlink(dir) < 0) { | ||
| 2831 | FILE_OP_ERROR(dir, "unlink"); | ||
| 2832 | return -1; | ||
| 2833 | } | ||
| 2834 | |||
| 2835 | return 0; | ||
| 2836 | } | ||
| 2837 | |||
| 2838 | prev_dir = g_get_current_dir(); | ||
| 2839 | /* g_print("prev_dir = %s\n", prev_dir); */ | ||
| 2840 | |||
| 2841 | if (g_chdir(dir) < 0) { | ||
| 2842 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2843 | g_free(prev_dir); | ||
| 2844 | return -1; | ||
| 2845 | } | ||
| 2846 | |||
| 2847 | if ((dp = g_dir_open(".", 0, NULL)) == NULL) { | ||
| 2848 | g_warning("failed to open directory: %s\n", dir); | ||
| 2849 | g_chdir(prev_dir); | ||
| 2850 | g_free(prev_dir); | ||
| 2851 | return -1; | ||
| 2852 | } | ||
| 2853 | |||
| 2854 | /* remove all files in the directory */ | ||
| 2855 | while ((dir_name = g_dir_read_name(dp)) != NULL) { | ||
| 2856 | /* g_print("removing %s\n", dir_name); */ | ||
| 2857 | |||
| 2858 | if (is_dir_exist(dir_name)) { | ||
| 2859 | if (remove_dir_recursive_real(dir_name) < 0) { | ||
| 2860 | g_warning("can't remove directory\n"); | ||
| 2861 | return -1; | ||
| 2862 | } | ||
| 2863 | } else { | ||
| 2864 | if (g_unlink(dir_name) < 0) | ||
| 2865 | FILE_OP_ERROR(dir_name, "unlink"); | ||
| 2866 | } | ||
| 2867 | } | ||
| 2868 | |||
| 2869 | g_dir_close(dp); | ||
| 2870 | |||
| 2871 | if (g_chdir(prev_dir) < 0) { | ||
| 2872 | FILE_OP_ERROR(prev_dir, "chdir"); | ||
| 2873 | g_free(prev_dir); | ||
| 2874 | return -1; | ||
| 2875 | } | ||
| 2876 | |||
| 2877 | g_free(prev_dir); | ||
| 2878 | |||
| 2879 | if (g_rmdir(dir) < 0) { | ||
| 2880 | if (ENOTDIR == errno) { | ||
| 2881 | if (g_unlink(dir) < 0) { | ||
| 2882 | FILE_OP_ERROR(dir, "unlink"); | ||
| 2883 | return -1; | ||
| 2884 | } | ||
| 2885 | } else { | ||
| 2886 | FILE_OP_ERROR(dir, "rmdir"); | ||
| 2887 | return -1; | ||
| 2888 | } | ||
| 2889 | } | ||
| 2890 | |||
| 2891 | return 0; | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | gint remove_dir_recursive(const gchar *dir) | ||
| 2895 | { | ||
| 2896 | gchar *cur_dir; | ||
| 2897 | gint ret; | ||
| 2898 | |||
| 2899 | debug_print("remove_dir_recursive: %s\n", dir); | ||
| 2900 | |||
| 2901 | cur_dir = g_get_current_dir(); | ||
| 2902 | |||
| 2903 | if (g_chdir(dir) < 0) { | ||
| 2904 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2905 | ret = -1; | ||
| 2906 | goto leave; | ||
| 2907 | } | ||
| 2908 | if (g_chdir("..") < 0) { | ||
| 2909 | FILE_OP_ERROR(dir, "chdir"); | ||
| 2910 | ret = -1; | ||
| 2911 | goto leave; | ||
| 2912 | } | ||
| 2913 | |||
| 2914 | ret = remove_dir_recursive_real(dir); | ||
| 2915 | |||
| 2916 | leave: | ||
| 2917 | if (is_dir_exist(cur_dir)) { | ||
| 2918 | if (g_chdir(cur_dir) < 0) { | ||
| 2919 | FILE_OP_ERROR(cur_dir, "chdir"); | ||
| 2920 | } | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | g_free(cur_dir); | ||
| 2924 | |||
| 2925 | return ret; | ||
| 2926 | } | ||
| 2927 | |||
| 2928 | gint rename_force(const gchar *oldpath, const gchar *newpath) | ||
| 2929 | { | ||
| 2930 | #if !defined(G_OS_UNIX) && !GLIB_CHECK_VERSION(2, 9, 1) | ||
| 2931 | if (!is_file_entry_exist(oldpath)) { | ||
| 2932 | errno = ENOENT; | ||
| 2933 | return -1; | ||
| 2934 | } | ||
| 2935 | if (is_file_exist(newpath)) { | ||
| 2936 | if (g_unlink(newpath) < 0) | ||
| 2937 | FILE_OP_ERROR(newpath, "unlink"); | ||
| 2938 | } | ||
| 2939 | #endif | ||
| 2940 | return g_rename(oldpath, newpath); | ||
| 2941 | } | ||
| 2942 | |||
| 2943 | gint copy_file(const gchar *src, const gchar *dest, gboolean keep_backup) | ||
| 2944 | { | ||
| 2945 | #ifdef G_OS_WIN32 | ||
| 2946 | wchar_t *wsrc; | ||
| 2947 | wchar_t *wdest; | ||
| 2948 | gchar *dest_bak = NULL; | ||
| 2949 | gboolean err = FALSE; | ||
| 2950 | |||
| 2951 | wsrc = g_utf8_to_utf16(src, -1, NULL, NULL, NULL); | ||
| 2952 | if (wsrc == NULL) { | ||
| 2953 | return -1; | ||
| 2954 | } | ||
| 2955 | wdest = g_utf8_to_utf16(dest, -1, NULL, NULL, NULL); | ||
| 2956 | if (wdest == NULL) { | ||
| 2957 | g_free(wsrc); | ||
| 2958 | return -1; | ||
| 2959 | } | ||
| 2960 | |||
| 2961 | if (keep_backup == FALSE) { | ||
| 2962 | if (CopyFileW(wsrc, wdest, FALSE) == 0) | ||
| 2963 | err = TRUE; | ||
| 2964 | g_free(wdest); | ||
| 2965 | g_free(wsrc); | ||
| 2966 | return err ? -1 : 0; | ||
| 2967 | } | ||
| 2968 | |||
| 2969 | if (is_file_exist(dest)) { | ||
| 2970 | dest_bak = g_strconcat(dest, ".bak", NULL); | ||
| 2971 | if (rename_force(dest, dest_bak) < 0) { | ||
| 2972 | FILE_OP_ERROR(dest, "rename"); | ||
| 2973 | g_free(dest_bak); | ||
| 2974 | g_free(wdest); | ||
| 2975 | g_free(wsrc); | ||
| 2976 | return -1; | ||
| 2977 | } | ||
| 2978 | } | ||
| 2979 | |||
| 2980 | if (CopyFileW(wsrc, wdest, FALSE) == 0) | ||
| 2981 | err = TRUE; | ||
| 2982 | |||
| 2983 | g_free(wdest); | ||
| 2984 | g_free(wsrc); | ||
| 2985 | #else | ||
| 2986 | gint srcfd, destfd; | ||
| 2987 | gint n_read; | ||
| 2988 | gchar buf[BUFFSIZE]; | ||
| 2989 | gchar *dest_bak = NULL; | ||
| 2990 | gboolean err = FALSE; | ||
| 2991 | |||
| 2992 | if ((srcfd = g_open(src, O_RDONLY, 0600)) < 0) { | ||
| 2993 | FILE_OP_ERROR(src, "open"); | ||
| 2994 | return -1; | ||
| 2995 | } | ||
| 2996 | if (is_file_exist(dest)) { | ||
| 2997 | dest_bak = g_strconcat(dest, ".bak", NULL); | ||
| 2998 | if (rename_force(dest, dest_bak) < 0) { | ||
| 2999 | FILE_OP_ERROR(dest, "rename"); | ||
| 3000 | close(srcfd); | ||
| 3001 | g_free(dest_bak); | ||
| 3002 | return -1; | ||
| 3003 | } | ||
| 3004 | } | ||
| 3005 | |||
| 3006 | if ((destfd = g_open(dest, O_WRONLY | O_CREAT, 0600)) < 0) { | ||
| 3007 | FILE_OP_ERROR(dest, "open"); | ||
| 3008 | close(srcfd); | ||
| 3009 | if (dest_bak) { | ||
| 3010 | if (rename_force(dest_bak, dest) < 0) | ||
| 3011 | FILE_OP_ERROR(dest_bak, "rename"); | ||
| 3012 | g_free(dest_bak); | ||
| 3013 | } | ||
| 3014 | return -1; | ||
| 3015 | } | ||
| 3016 | |||
| 3017 | while ((n_read = read(srcfd, buf, sizeof(buf))) > 0) { | ||
| 3018 | gchar *p = buf; | ||
| 3019 | const gchar *endp = buf + n_read; | ||
| 3020 | gint n_write; | ||
| 3021 | |||
| 3022 | while (p < endp) { | ||
| 3023 | if ((n_write = write(destfd, p, endp - p)) < 0) { | ||
| 3024 | g_warning(_("writing to %s failed.\n"), dest); | ||
| 3025 | close(destfd); | ||
| 3026 | close(srcfd); | ||
| 3027 | g_unlink(dest); | ||
| 3028 | if (dest_bak) { | ||
| 3029 | if (rename_force(dest_bak, dest) < 0) | ||
| 3030 | FILE_OP_ERROR(dest_bak, "rename"); | ||
| 3031 | g_free(dest_bak); | ||
| 3032 | } | ||
| 3033 | return -1; | ||
| 3034 | } | ||
| 3035 | p += n_write; | ||
| 3036 | } | ||
| 3037 | } | ||
| 3038 | |||
| 3039 | if (close(destfd) < 0) { | ||
| 3040 | FILE_OP_ERROR(dest, "close"); | ||
| 3041 | err = TRUE; | ||
| 3042 | } | ||
| 3043 | close(srcfd); | ||
| 3044 | #endif | ||
| 3045 | |||
| 3046 | if (err) { | ||
| 3047 | g_unlink(dest); | ||
| 3048 | if (dest_bak) { | ||
| 3049 | if (rename_force(dest_bak, dest) < 0) | ||
| 3050 | FILE_OP_ERROR(dest_bak, "rename"); | ||
| 3051 | g_free(dest_bak); | ||
| 3052 | } | ||
| 3053 | return -1; | ||
| 3054 | } | ||
| 3055 | |||
| 3056 | if (keep_backup == FALSE && dest_bak) | ||
| 3057 | g_unlink(dest_bak); | ||
| 3058 | |||
| 3059 | g_free(dest_bak); | ||
| 3060 | |||
| 3061 | return 0; | ||
| 3062 | } | ||
| 3063 | |||
| 3064 | gint copy_dir(const gchar *src, const gchar *dest) | ||
| 3065 | { | ||
| 3066 | GDir *dir; | ||
| 3067 | const gchar *dir_name; | ||
| 3068 | gchar *src_file; | ||
| 3069 | gchar *dest_file; | ||
| 3070 | |||
| 3071 | if ((dir = g_dir_open(src, 0, NULL)) == NULL) { | ||
| 3072 | g_warning("failed to open directory: %s\n", src); | ||
| 3073 | return -1; | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | if (make_dir_hier(dest) < 0) { | ||
| 3077 | g_dir_close(dir); | ||
| 3078 | return -1; | ||
| 3079 | } | ||
| 3080 | |||
| 3081 | while ((dir_name = g_dir_read_name(dir)) != NULL) { | ||
| 3082 | src_file = g_strconcat(src, G_DIR_SEPARATOR_S, dir_name, NULL); | ||
| 3083 | dest_file = g_strconcat(dest, G_DIR_SEPARATOR_S, dir_name, | ||
| 3084 | NULL); | ||
| 3085 | if (is_file_exist(src_file)) | ||
| 3086 | copy_file(src_file, dest_file, FALSE); | ||
| 3087 | g_free(dest_file); | ||
| 3088 | g_free(src_file); | ||
| 3089 | } | ||
| 3090 | |||
| 3091 | g_dir_close(dir); | ||
| 3092 | |||
| 3093 | return 0; | ||
| 3094 | } | ||
| 3095 | |||
| 3096 | gint move_file(const gchar *src, const gchar *dest, gboolean overwrite) | ||
| 3097 | { | ||
| 3098 | if (overwrite == FALSE && is_file_entry_exist(dest)) { | ||
| 3099 | g_warning("move_file(): file %s already exists.", dest); | ||
| 3100 | return -1; | ||
| 3101 | } | ||
| 3102 | |||
| 3103 | if (rename_force(src, dest) == 0) return 0; | ||
| 3104 | |||
| 3105 | if (EXDEV != errno) { | ||
| 3106 | FILE_OP_ERROR(src, "rename"); | ||
| 3107 | return -1; | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | if (copy_file(src, dest, FALSE) < 0) return -1; | ||
| 3111 | |||
| 3112 | g_unlink(src); | ||
| 3113 | |||
| 3114 | return 0; | ||
| 3115 | } | ||
| 3116 | |||
| 3117 | gint append_file_part(FILE *fp, off_t offset, size_t length, FILE *dest_fp) | ||
| 3118 | { | ||
| 3119 | gint n_read; | ||
| 3120 | gint bytes_left, to_read; | ||
| 3121 | gchar buf[BUFSIZ]; | ||
| 3122 | |||
| 3123 | g_return_val_if_fail(fp != NULL, -1); | ||
| 3124 | g_return_val_if_fail(dest_fp != NULL, -1); | ||
| 3125 | |||
| 3126 | if (fseek(fp, offset, SEEK_SET) < 0) { | ||
| 3127 | perror("fseek"); | ||
| 3128 | return -1; | ||
| 3129 | } | ||
| 3130 | |||
| 3131 | bytes_left = length; | ||
| 3132 | to_read = MIN(bytes_left, sizeof(buf)); | ||
| 3133 | |||
| 3134 | while ((n_read = fread(buf, sizeof(gchar), to_read, fp)) > 0) { | ||
| 3135 | if (n_read < to_read && ferror(fp)) | ||
| 3136 | break; | ||
| 3137 | if (fwrite(buf, n_read, 1, dest_fp) < 1) { | ||
| 3138 | g_warning("append_file_part: writing to file failed.\n"); | ||
| 3139 | return -1; | ||
| 3140 | } | ||
| 3141 | bytes_left -= n_read; | ||
| 3142 | if (bytes_left == 0) | ||
| 3143 | break; | ||
| 3144 | to_read = MIN(bytes_left, sizeof(buf)); | ||
| 3145 | } | ||
| 3146 | |||
| 3147 | if (ferror(fp)) { | ||
| 3148 | perror("fread"); | ||
| 3149 | return -1; | ||
| 3150 | } | ||
| 3151 | if (fflush(dest_fp) == EOF) { | ||
| 3152 | FILE_OP_ERROR("append_file_part", "fflush"); | ||
| 3153 | return -1; | ||
| 3154 | } | ||
| 3155 | |||
| 3156 | return 0; | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest) | ||
| 3160 | { | ||
| 3161 | FILE *dest_fp; | ||
| 3162 | |||
| 3163 | if ((dest_fp = g_fopen(dest, "wb")) == NULL) { | ||
| 3164 | FILE_OP_ERROR(dest, "fopen"); | ||
| 3165 | return -1; | ||
| 3166 | } | ||
| 3167 | |||
| 3168 | if (change_file_mode_rw(dest_fp, dest) < 0) { | ||
| 3169 | FILE_OP_ERROR(dest, "chmod"); | ||
| 3170 | g_warning("can't change file mode\n"); | ||
| 3171 | } | ||
| 3172 | |||
| 3173 | if (append_file_part(fp, offset, length, dest_fp) < 0) { | ||
| 3174 | g_warning("writing to %s failed.\n", dest); | ||
| 3175 | fclose(dest_fp); | ||
| 3176 | g_unlink(dest); | ||
| 3177 | return -1; | ||
| 3178 | } | ||
| 3179 | |||
| 3180 | if (fclose(dest_fp) == EOF) { | ||
| 3181 | FILE_OP_ERROR(dest, "fclose"); | ||
| 3182 | g_unlink(dest); | ||
| 3183 | return -1; | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | return 0; | ||
| 3187 | } | ||
| 3188 | |||
| 3189 | gint copy_file_stream(FILE *fp, FILE *dest_fp) | ||
| 3190 | { | ||
| 3191 | gint n_read; | ||
| 3192 | gchar buf[BUFFSIZE]; | ||
| 3193 | |||
| 3194 | g_return_val_if_fail(fp != NULL, -1); | ||
| 3195 | g_return_val_if_fail(dest_fp != NULL, -1); | ||
| 3196 | |||
| 3197 | while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { | ||
| 3198 | if (n_read < sizeof(buf) && ferror(fp)) | ||
| 3199 | break; | ||
| 3200 | if (fwrite(buf, n_read, 1, dest_fp) < 1) { | ||
| 3201 | g_warning("copy_file_stream: writing to file failed.\n"); | ||
| 3202 | return -1; | ||
| 3203 | } | ||
| 3204 | } | ||
| 3205 | |||
| 3206 | if (ferror(fp)) { | ||
| 3207 | perror("fread"); | ||
| 3208 | return -1; | ||
| 3209 | } | ||
| 3210 | if (fflush(dest_fp) == EOF) { | ||
| 3211 | FILE_OP_ERROR("copy_file_stream", "fflush"); | ||
| 3212 | return -1; | ||
| 3213 | } | ||
| 3214 | |||
| 3215 | return 0; | ||
| 3216 | } | ||
| 3217 | |||
| 3218 | /* convert line endings into CRLF. If the last line doesn't end with | ||
| 3219 | * linebreak, add it. | ||
| 3220 | */ | ||
| 3221 | gchar *canonicalize_str(const gchar *str) | ||
| 3222 | { | ||
| 3223 | const gchar *p; | ||
| 3224 | guint new_len = 0; | ||
| 3225 | gchar *out, *outp; | ||
| 3226 | |||
| 3227 | for (p = str; *p != '\0'; ++p) { | ||
| 3228 | if (*p != '\r') { | ||
| 3229 | ++new_len; | ||
| 3230 | if (*p == '\n') | ||
| 3231 | ++new_len; | ||
| 3232 | } | ||
| 3233 | } | ||
| 3234 | if (p == str || *(p - 1) != '\n') | ||
| 3235 | new_len += 2; | ||
| 3236 | |||
| 3237 | out = outp = g_malloc(new_len + 1); | ||
| 3238 | for (p = str; *p != '\0'; ++p) { | ||
| 3239 | if (*p != '\r') { | ||
| 3240 | if (*p == '\n') | ||
| 3241 | *outp++ = '\r'; | ||
| 3242 | *outp++ = *p; | ||
| 3243 | } | ||
| 3244 | } | ||
| 3245 | if (p == str || *(p - 1) != '\n') { | ||
| 3246 | *outp++ = '\r'; | ||
| 3247 | *outp++ = '\n'; | ||
| 3248 | } | ||
| 3249 | *outp = '\0'; | ||
| 3250 | |||
| 3251 | return out; | ||
| 3252 | } | ||
| 3253 | |||
| 3254 | gint canonicalize_file(const gchar *src, const gchar *dest) | ||
| 3255 | { | ||
| 3256 | FILE *src_fp, *dest_fp; | ||
| 3257 | gchar buf[BUFFSIZE]; | ||
| 3258 | gint len; | ||
| 3259 | gboolean err = FALSE; | ||
| 3260 | gboolean last_linebreak = FALSE; | ||
| 3261 | |||
| 3262 | if ((src_fp = g_fopen(src, "rb")) == NULL) { | ||
| 3263 | FILE_OP_ERROR(src, "fopen"); | ||
| 3264 | return -1; | ||
| 3265 | } | ||
| 3266 | |||
| 3267 | if ((dest_fp = g_fopen(dest, "wb")) == NULL) { | ||
| 3268 | FILE_OP_ERROR(dest, "fopen"); | ||
| 3269 | fclose(src_fp); | ||
| 3270 | return -1; | ||
| 3271 | } | ||
| 3272 | |||
| 3273 | if (change_file_mode_rw(dest_fp, dest) < 0) { | ||
| 3274 | FILE_OP_ERROR(dest, "chmod"); | ||
| 3275 | g_warning("can't change file mode\n"); | ||
| 3276 | } | ||
| 3277 | |||
| 3278 | while (fgets(buf, sizeof(buf), src_fp) != NULL) { | ||
| 3279 | gint r = 0; | ||
| 3280 | |||
| 3281 | len = strlen(buf); | ||
| 3282 | if (len == 0) break; | ||
| 3283 | last_linebreak = FALSE; | ||
| 3284 | |||
| 3285 | if (buf[len - 1] != '\n') { | ||
| 3286 | last_linebreak = TRUE; | ||
| 3287 | r = fputs(buf, dest_fp); | ||
| 3288 | } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') { | ||
| 3289 | r = fputs(buf, dest_fp); | ||
| 3290 | } else { | ||
| 3291 | if (len > 1) { | ||
| 3292 | r = fwrite(buf, len - 1, 1, dest_fp); | ||
| 3293 | if (r != 1) | ||
| 3294 | r = EOF; | ||
| 3295 | } | ||
| 3296 | if (r != EOF) | ||
| 3297 | r = fputs("\r\n", dest_fp); | ||
| 3298 | } | ||
| 3299 | |||
| 3300 | if (r == EOF) { | ||
| 3301 | g_warning("writing to %s failed.\n", dest); | ||
| 3302 | fclose(dest_fp); | ||
| 3303 | fclose(src_fp); | ||
| 3304 | g_unlink(dest); | ||
| 3305 | return -1; | ||
| 3306 | } | ||
| 3307 | } | ||
| 3308 | |||
| 3309 | if (last_linebreak == TRUE) { | ||
| 3310 | if (fputs("\r\n", dest_fp) == EOF) | ||
| 3311 | err = TRUE; | ||
| 3312 | } | ||
| 3313 | |||
| 3314 | if (ferror(src_fp)) { | ||
| 3315 | FILE_OP_ERROR(src, "fgets"); | ||
| 3316 | err = TRUE; | ||
| 3317 | } | ||
| 3318 | fclose(src_fp); | ||
| 3319 | if (fclose(dest_fp) == EOF) { | ||
| 3320 | FILE_OP_ERROR(dest, "fclose"); | ||
| 3321 | err = TRUE; | ||
| 3322 | } | ||
| 3323 | |||
| 3324 | if (err) { | ||
| 3325 | g_unlink(dest); | ||
| 3326 | return -1; | ||
| 3327 | } | ||
| 3328 | |||
| 3329 | return 0; | ||
| 3330 | } | ||
| 3331 | |||
| 3332 | gint canonicalize_file_replace(const gchar *file) | ||
| 3333 | { | ||
| 3334 | gchar *tmp_file; | ||
| 3335 | |||
| 3336 | tmp_file = get_tmp_file(); | ||
| 3337 | |||
| 3338 | if (canonicalize_file(file, tmp_file) < 0) { | ||
| 3339 | g_free(tmp_file); | ||
| 3340 | return -1; | ||
| 3341 | } | ||
| 3342 | |||
| 3343 | if (move_file(tmp_file, file, TRUE) < 0) { | ||
| 3344 | g_warning("can't replace %s .\n", file); | ||
| 3345 | g_unlink(tmp_file); | ||
| 3346 | g_free(tmp_file); | ||
| 3347 | return -1; | ||
| 3348 | } | ||
| 3349 | |||
| 3350 | g_free(tmp_file); | ||
| 3351 | return 0; | ||
| 3352 | } | ||
| 3353 | |||
| 3354 | FILE *canonicalize_file_stream(FILE *src_fp, gint *length) | ||
| 3355 | { | ||
| 3356 | FILE *dest_fp; | ||
| 3357 | gchar buf[BUFFSIZE]; | ||
| 3358 | gint len; | ||
| 3359 | gint length_ = 0; | ||
| 3360 | gboolean err = FALSE; | ||
| 3361 | gboolean last_linebreak = FALSE; | ||
| 3362 | |||
| 3363 | if ((dest_fp = my_tmpfile()) == NULL) | ||
| 3364 | return NULL; | ||
| 3365 | |||
| 3366 | while (fgets(buf, sizeof(buf), src_fp) != NULL) { | ||
| 3367 | gint r = 0; | ||
| 3368 | |||
| 3369 | len = strlen(buf); | ||
| 3370 | if (len == 0) break; | ||
| 3371 | last_linebreak = FALSE; | ||
| 3372 | |||
| 3373 | if (buf[len - 1] != '\n') { | ||
| 3374 | last_linebreak = TRUE; | ||
| 3375 | r = fputs(buf, dest_fp); | ||
| 3376 | length_ += len; | ||
| 3377 | } else if (len > 1 && buf[len - 1] == '\n' && buf[len - 2] == '\r') { | ||
| 3378 | r = fputs(buf, dest_fp); | ||
| 3379 | length_ += len; | ||
| 3380 | } else { | ||
| 3381 | if (len > 1) { | ||
| 3382 | r = fwrite(buf, len - 1, 1, dest_fp); | ||
| 3383 | if (r != 1) | ||
| 3384 | r = EOF; | ||
| 3385 | else | ||
| 3386 | length_ += len - 1; | ||
| 3387 | } | ||
| 3388 | if (r != EOF) { | ||
| 3389 | r = fputs("\r\n", dest_fp); | ||
| 3390 | length_ += 2; | ||
| 3391 | } | ||
| 3392 | } | ||
| 3393 | |||
| 3394 | if (r == EOF) { | ||
| 3395 | g_warning("writing to temporary file failed.\n"); | ||
| 3396 | fclose(dest_fp); | ||
| 3397 | return NULL; | ||
| 3398 | } | ||
| 3399 | } | ||
| 3400 | |||
| 3401 | if (last_linebreak == TRUE) { | ||
| 3402 | if (fputs("\r\n", dest_fp) == EOF) | ||
| 3403 | err = TRUE; | ||
| 3404 | else | ||
| 3405 | length_ += 2; | ||
| 3406 | } | ||
| 3407 | |||
| 3408 | if (ferror(src_fp)) { | ||
| 3409 | FILE_OP_ERROR("canonicalize_file_stream", "fgets"); | ||
| 3410 | err = TRUE; | ||
| 3411 | } | ||
| 3412 | if (fflush(dest_fp) == EOF) { | ||
| 3413 | FILE_OP_ERROR("canonicalize_file_stream", "fflush"); | ||
| 3414 | err = TRUE; | ||
| 3415 | } | ||
| 3416 | |||
| 3417 | if (err) { | ||
| 3418 | fclose(dest_fp); | ||
| 3419 | return NULL; | ||
| 3420 | } | ||
| 3421 | |||
| 3422 | if (length) | ||
| 3423 | *length = length_; | ||
| 3424 | |||
| 3425 | rewind(dest_fp); | ||
| 3426 | return dest_fp; | ||
| 3427 | } | ||
| 3428 | |||
| 3429 | gint uncanonicalize_file(const gchar *src, const gchar *dest) | ||
| 3430 | { | ||
| 3431 | FILE *src_fp, *dest_fp; | ||
| 3432 | gchar buf[BUFFSIZE]; | ||
| 3433 | gboolean err = FALSE; | ||
| 3434 | |||
| 3435 | if ((src_fp = g_fopen(src, "rb")) == NULL) { | ||
| 3436 | FILE_OP_ERROR(src, "fopen"); | ||
| 3437 | return -1; | ||
| 3438 | } | ||
| 3439 | |||
| 3440 | if ((dest_fp = g_fopen(dest, "wb")) == NULL) { | ||
| 3441 | FILE_OP_ERROR(dest, "fopen"); | ||
| 3442 | fclose(src_fp); | ||
| 3443 | return -1; | ||
| 3444 | } | ||
| 3445 | |||
| 3446 | if (change_file_mode_rw(dest_fp, dest) < 0) { | ||
| 3447 | FILE_OP_ERROR(dest, "chmod"); | ||
| 3448 | g_warning("can't change file mode\n"); | ||
| 3449 | } | ||
| 3450 | |||
| 3451 | while (fgets(buf, sizeof(buf), src_fp) != NULL) { | ||
| 3452 | strcrchomp(buf); | ||
| 3453 | if (fputs(buf, dest_fp) == EOF) { | ||
| 3454 | g_warning("writing to %s failed.\n", dest); | ||
| 3455 | fclose(dest_fp); | ||
| 3456 | fclose(src_fp); | ||
| 3457 | g_unlink(dest); | ||
| 3458 | return -1; | ||
| 3459 | } | ||
| 3460 | } | ||
| 3461 | |||
| 3462 | if (ferror(src_fp)) { | ||
| 3463 | FILE_OP_ERROR(src, "fgets"); | ||
| 3464 | err = TRUE; | ||
| 3465 | } | ||
| 3466 | fclose(src_fp); | ||
| 3467 | if (fclose(dest_fp) == EOF) { | ||
| 3468 | FILE_OP_ERROR(dest, "fclose"); | ||
| 3469 | err = TRUE; | ||
| 3470 | } | ||
| 3471 | |||
| 3472 | if (err) { | ||
| 3473 | g_unlink(dest); | ||
| 3474 | return -1; | ||
| 3475 | } | ||
| 3476 | |||
| 3477 | return 0; | ||
| 3478 | } | ||
| 3479 | |||
| 3480 | gint uncanonicalize_file_replace(const gchar *file) | ||
| 3481 | { | ||
| 3482 | gchar *tmp_file; | ||
| 3483 | |||
| 3484 | tmp_file = get_tmp_file(); | ||
| 3485 | |||
| 3486 | if (uncanonicalize_file(file, tmp_file) < 0) { | ||
| 3487 | g_free(tmp_file); | ||
| 3488 | return -1; | ||
| 3489 | } | ||
| 3490 | |||
| 3491 | if (move_file(tmp_file, file, TRUE) < 0) { | ||
| 3492 | g_warning("can't replace %s .\n", file); | ||
| 3493 | g_unlink(tmp_file); | ||
| 3494 | g_free(tmp_file); | ||
| 3495 | return -1; | ||
| 3496 | } | ||
| 3497 | |||
| 3498 | g_free(tmp_file); | ||
| 3499 | return 0; | ||
| 3500 | } | ||
| 3501 | |||
| 3502 | gchar *normalize_newlines(const gchar *str) | ||
| 3503 | { | ||
| 3504 | const gchar *p = str; | ||
| 3505 | gchar *out, *outp; | ||
| 3506 | |||
| 3507 | out = outp = g_malloc(strlen(str) + 1); | ||
| 3508 | for (p = str; *p != '\0'; ++p) { | ||
| 3509 | if (*p == '\r') { | ||
| 3510 | if (*(p + 1) != '\n') | ||
| 3511 | *outp++ = '\n'; | ||
| 3512 | } else | ||
| 3513 | *outp++ = *p; | ||
| 3514 | } | ||
| 3515 | |||
| 3516 | *outp = '\0'; | ||
| 3517 | |||
| 3518 | return out; | ||
| 3519 | } | ||
| 3520 | |||
| 3521 | gchar *strchomp_all(const gchar *str) | ||
| 3522 | { | ||
| 3523 | const gchar *p = str; | ||
| 3524 | const gchar *newline, *last; | ||
| 3525 | gchar *out, *outp; | ||
| 3526 | |||
| 3527 | out = outp = g_malloc(strlen(str) + 1); | ||
| 3528 | while (*p != '\0') { | ||
| 3529 | newline = strchr(p, '\n'); | ||
| 3530 | if (newline) { | ||
| 3531 | for (last = newline; | ||
| 3532 | p < last && g_ascii_isspace(*(last - 1)); --last) | ||
| 3533 | ; | ||
| 3534 | strncpy(outp, p, last - p); | ||
| 3535 | outp += last - p; | ||
| 3536 | |||
| 3537 | if (p < newline && *(newline - 1) == '\r') { | ||
| 3538 | strncpy(outp, newline - 1, 2); | ||
| 3539 | outp += 2; | ||
| 3540 | } else { | ||
| 3541 | *outp++ = *newline; | ||
| 3542 | } | ||
| 3543 | |||
| 3544 | p = newline + 1; | ||
| 3545 | } else { | ||
| 3546 | for (last = p + strlen(p); | ||
| 3547 | p < last && g_ascii_isspace(*(last - 1)); --last) | ||
| 3548 | ; | ||
| 3549 | strncpy(outp, p, last - p); | ||
| 3550 | outp += last - p; | ||
| 3551 | break; | ||
| 3552 | } | ||
| 3553 | } | ||
| 3554 | |||
| 3555 | *outp = '\0'; | ||
| 3556 | |||
| 3557 | return out; | ||
| 3558 | } | ||
| 3559 | |||
| 3560 | FILE *get_outgoing_rfc2822_file(FILE *fp) | ||
| 3561 | { | ||
| 3562 | gchar buf[BUFFSIZE]; | ||
| 3563 | FILE *outfp; | ||
| 3564 | |||
| 3565 | outfp = my_tmpfile(); | ||
| 3566 | if (!outfp) { | ||
| 3567 | FILE_OP_ERROR("get_outgoing_rfc2822_file", "my_tmpfile"); | ||
| 3568 | return NULL; | ||
| 3569 | } | ||
| 3570 | |||
| 3571 | /* output header part */ | ||
| 3572 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 3573 | strretchomp(buf); | ||
| 3574 | if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) { | ||
| 3575 | gint next; | ||
| 3576 | |||
| 3577 | for (;;) { | ||
| 3578 | next = fgetc(fp); | ||
| 3579 | if (next == EOF) | ||
| 3580 | break; | ||
| 3581 | else if (next != ' ' && next != '\t') { | ||
| 3582 | ungetc(next, fp); | ||
| 3583 | break; | ||
| 3584 | } | ||
| 3585 | if (fgets(buf, sizeof(buf), fp) == NULL) | ||
| 3586 | break; | ||
| 3587 | } | ||
| 3588 | } else { | ||
| 3589 | if (fputs(buf, outfp) == EOF) | ||
| 3590 | goto file_error; | ||
| 3591 | if (fputs("\r\n", outfp) == EOF) | ||
| 3592 | goto file_error; | ||
| 3593 | if (buf[0] == '\0') | ||
| 3594 | break; | ||
| 3595 | } | ||
| 3596 | } | ||
| 3597 | |||
| 3598 | /* output body part */ | ||
| 3599 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 3600 | strretchomp(buf); | ||
| 3601 | if (buf[0] == '.') { | ||
| 3602 | if (fputc('.', outfp) == EOF) | ||
| 3603 | goto file_error; | ||
| 3604 | } | ||
| 3605 | if (fputs(buf, outfp) == EOF) | ||
| 3606 | goto file_error; | ||
| 3607 | if (fputs("\r\n", outfp) == EOF) | ||
| 3608 | goto file_error; | ||
| 3609 | } | ||
| 3610 | |||
| 3611 | if (fflush(outfp) == EOF) { | ||
| 3612 | FILE_OP_ERROR("get_outgoing_rfc2822_file", "fflush"); | ||
| 3613 | goto file_error; | ||
| 3614 | } | ||
| 3615 | |||
| 3616 | rewind(outfp); | ||
| 3617 | return outfp; | ||
| 3618 | |||
| 3619 | file_error: | ||
| 3620 | g_warning("get_outgoing_rfc2822_file(): writing to temporary file failed.\n"); | ||
| 3621 | fclose(outfp); | ||
| 3622 | return NULL; | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | gchar *get_outgoing_rfc2822_str(FILE *fp) | ||
| 3626 | { | ||
| 3627 | gchar buf[BUFFSIZE]; | ||
| 3628 | GString *str; | ||
| 3629 | gchar *ret; | ||
| 3630 | |||
| 3631 | str = g_string_new(NULL); | ||
| 3632 | |||
| 3633 | /* output header part */ | ||
| 3634 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 3635 | strretchomp(buf); | ||
| 3636 | if (!g_ascii_strncasecmp(buf, "Bcc:", 4)) { | ||
| 3637 | gint next; | ||
| 3638 | |||
| 3639 | for (;;) { | ||
| 3640 | next = fgetc(fp); | ||
| 3641 | if (next == EOF) | ||
| 3642 | break; | ||
| 3643 | else if (next != ' ' && next != '\t') { | ||
| 3644 | ungetc(next, fp); | ||
| 3645 | break; | ||
| 3646 | } | ||
| 3647 | if (fgets(buf, sizeof(buf), fp) == NULL) | ||
| 3648 | break; | ||
| 3649 | } | ||
| 3650 | #if 0 | ||
| 3651 | } else if (!g_ascii_strncasecmp(buf, "Date:", 5)) { | ||
| 3652 | get_rfc822_date(buf, sizeof(buf)); | ||
| 3653 | g_string_append_printf(str, "Date: %s\r\n", buf); | ||
| 3654 | #endif | ||
| 3655 | } else { | ||
| 3656 | g_string_append(str, buf); | ||
| 3657 | g_string_append(str, "\r\n"); | ||
| 3658 | if (buf[0] == '\0') | ||
| 3659 | break; | ||
| 3660 | } | ||
| 3661 | } | ||
| 3662 | |||
| 3663 | /* output body part */ | ||
| 3664 | while (fgets(buf, sizeof(buf), fp) != NULL) { | ||
| 3665 | strretchomp(buf); | ||
| 3666 | if (buf[0] == '.') | ||
| 3667 | g_string_append_c(str, '.'); | ||
| 3668 | g_string_append(str, buf); | ||
| 3669 | g_string_append(str, "\r\n"); | ||
| 3670 | } | ||
| 3671 | |||
| 3672 | ret = str->str; | ||
| 3673 | g_string_free(str, FALSE); | ||
| 3674 | |||
| 3675 | return ret; | ||
| 3676 | } | ||
| 3677 | |||
| 3678 | /* | ||
| 3679 | * Create a new boundary in a way that it is very unlikely that this | ||
| 3680 | * will occur in the following text. It would be easy to ensure | ||
| 3681 | * uniqueness if everything is either quoted-printable or base64 | ||
| 3682 | * encoded (note that conversion is allowed), but because MIME bodies | ||
| 3683 | * may be nested, it may happen that the same boundary has already | ||
| 3684 | * been used. We avoid scanning the message for conflicts and hope the | ||
| 3685 | * best. | ||
| 3686 | * | ||
| 3687 | * boundary := 0*69<bchars> bcharsnospace | ||
| 3688 | * bchars := bcharsnospace / " " | ||
| 3689 | * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / | ||
| 3690 | * "+" / "_" / "," / "-" / "." / | ||
| 3691 | * "/" / ":" / "=" / "?" | ||
| 3692 | * | ||
| 3693 | * some special characters removed because of buggy MTAs | ||
| 3694 | */ | ||
| 3695 | |||
| 3696 | gchar *generate_mime_boundary(const gchar *prefix) | ||
| 3697 | { | ||
| 3698 | static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 3699 | "abcdefghijklmnopqrstuvwxyz" | ||
| 3700 | "1234567890+_./="; | ||
| 3701 | gchar buf_uniq[17]; | ||
| 3702 | gchar buf_date[64]; | ||
| 3703 | gint i; | ||
| 3704 | |||
| 3705 | for (i = 0; i < sizeof(buf_uniq) - 1; i++) | ||
| 3706 | buf_uniq[i] = tbl[g_random_int_range(0, sizeof(tbl) - 1)]; | ||
| 3707 | buf_uniq[i] = '\0'; | ||
| 3708 | |||
| 3709 | get_rfc822_date(buf_date, sizeof(buf_date)); | ||
| 3710 | subst_chars(buf_date, " ,:", '_'); | ||
| 3711 | |||
| 3712 | return g_strdup_printf("%s=_%s_%s", prefix ? prefix : "Multipart", | ||
| 3713 | buf_date, buf_uniq); | ||
| 3714 | } | ||
| 3715 | |||
| 3716 | gint change_file_mode_rw(FILE *fp, const gchar *file) | ||
| 3717 | { | ||
| 3718 | #ifdef G_OS_WIN32 | ||
| 3719 | DWORD attr; | ||
| 3720 | BOOL retval; | ||
| 3721 | |||
| 3722 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 3723 | wchar_t *wpath; | ||
| 3724 | |||
| 3725 | wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL); | ||
| 3726 | if (wpath == NULL) | ||
| 3727 | return -1; | ||
| 3728 | |||
| 3729 | attr = GetFileAttributesW(wpath); | ||
| 3730 | retval = SetFileAttributesW | ||
| 3731 | (wpath, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN)); | ||
| 3732 | |||
| 3733 | g_free(wpath); | ||
| 3734 | } else { | ||
| 3735 | gchar *cp_path; | ||
| 3736 | |||
| 3737 | cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL); | ||
| 3738 | if (cp_path == NULL) | ||
| 3739 | return -1; | ||
| 3740 | |||
| 3741 | attr = GetFileAttributesA(cp_path); | ||
| 3742 | retval = SetFileAttributesA | ||
| 3743 | (cp_path, attr & ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN)); | ||
| 3744 | |||
| 3745 | g_free(cp_path); | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | if (retval) | ||
| 3749 | return 0; | ||
| 3750 | else | ||
| 3751 | return -1; | ||
| 3752 | #else | ||
| 3753 | #if HAVE_FCHMOD | ||
| 3754 | if (fp) | ||
| 3755 | return fchmod(fileno(fp), S_IRUSR|S_IWUSR); | ||
| 3756 | else | ||
| 3757 | #endif | ||
| 3758 | return g_chmod(file, S_IRUSR|S_IWUSR); | ||
| 3759 | #endif | ||
| 3760 | } | ||
| 3761 | |||
| 3762 | #ifdef G_OS_WIN32 | ||
| 3763 | gchar *_s_tempnam(const gchar *dir, const gchar *prefix) | ||
| 3764 | { | ||
| 3765 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 3766 | wchar_t *wpath; | ||
| 3767 | wchar_t *wprefix; | ||
| 3768 | wchar_t *wname; | ||
| 3769 | gint save_errno; | ||
| 3770 | gchar *name; | ||
| 3771 | |||
| 3772 | wpath = g_utf8_to_utf16(dir, -1, NULL, NULL, NULL); | ||
| 3773 | if (wpath == NULL) { | ||
| 3774 | errno = EINVAL; | ||
| 3775 | return NULL; | ||
| 3776 | } | ||
| 3777 | wprefix = g_utf8_to_utf16(prefix, -1, NULL, NULL, NULL); | ||
| 3778 | if (wprefix == NULL) { | ||
| 3779 | errno = EINVAL; | ||
| 3780 | g_free(wpath); | ||
| 3781 | return NULL; | ||
| 3782 | } | ||
| 3783 | |||
| 3784 | wname = _wtempnam(wpath, wprefix); | ||
| 3785 | save_errno = errno; | ||
| 3786 | |||
| 3787 | name = g_utf16_to_utf8(wname, -1, NULL, NULL, NULL); | ||
| 3788 | if (name == NULL) { | ||
| 3789 | save_errno = EINVAL; | ||
| 3790 | } | ||
| 3791 | |||
| 3792 | free(wname); /* must be freed by same MSVCRT used for | ||
| 3793 | _wtempnam() */ | ||
| 3794 | g_free(wprefix); | ||
| 3795 | g_free(wpath); | ||
| 3796 | |||
| 3797 | errno = save_errno; | ||
| 3798 | return name; | ||
| 3799 | } else { | ||
| 3800 | gchar *cp_path; | ||
| 3801 | gchar *cp_prefix; | ||
| 3802 | gchar *cp_name; | ||
| 3803 | gint save_errno; | ||
| 3804 | gchar *name; | ||
| 3805 | |||
| 3806 | cp_path = g_locale_from_utf8(dir, -1, NULL, NULL, NULL); | ||
| 3807 | if (cp_path == NULL) { | ||
| 3808 | errno = EINVAL; | ||
| 3809 | return NULL; | ||
| 3810 | } | ||
| 3811 | |||
| 3812 | cp_prefix = g_locale_from_utf8(prefix, -1, NULL, NULL, NULL); | ||
| 3813 | if (cp_prefix == NULL) { | ||
| 3814 | errno = EINVAL; | ||
| 3815 | g_free(cp_path); | ||
| 3816 | return NULL; | ||
| 3817 | } | ||
| 3818 | |||
| 3819 | cp_name = _tempnam(cp_path, cp_prefix); | ||
| 3820 | save_errno = errno; | ||
| 3821 | |||
| 3822 | name = g_locale_to_utf8(cp_name, -1, NULL, NULL, NULL); | ||
| 3823 | if (name == NULL) { | ||
| 3824 | save_errno = EINVAL; | ||
| 3825 | } | ||
| 3826 | |||
| 3827 | free(cp_name); | ||
| 3828 | g_free(cp_prefix); | ||
| 3829 | g_free(cp_path); | ||
| 3830 | |||
| 3831 | errno = save_errno; | ||
| 3832 | return name; | ||
| 3833 | } | ||
| 3834 | } | ||
| 3835 | #endif | ||
| 3836 | |||
| 3837 | FILE *my_tmpfile(void) | ||
| 3838 | { | ||
| 3839 | #ifdef G_OS_WIN32 | ||
| 3840 | const gchar *tmpdir; | ||
| 3841 | gchar *fname; | ||
| 3842 | gint fd; | ||
| 3843 | FILE *fp; | ||
| 3844 | |||
| 3845 | tmpdir = get_tmp_dir(); | ||
| 3846 | fname = _s_tempnam(tmpdir, "sylph"); | ||
| 3847 | if (!fname) | ||
| 3848 | return NULL; | ||
| 3849 | |||
| 3850 | fd = g_open(fname, O_RDWR | O_CREAT | O_EXCL | | ||
| 3851 | _O_TEMPORARY | _O_SHORT_LIVED | _O_BINARY, 0600); | ||
| 3852 | if (fd < 0) { | ||
| 3853 | g_free(fname); | ||
| 3854 | return NULL; | ||
| 3855 | } | ||
| 3856 | |||
| 3857 | fp = fdopen(fd, "w+b"); | ||
| 3858 | if (!fp) { | ||
| 3859 | perror("fdopen"); | ||
| 3860 | close(fd); | ||
| 3861 | } | ||
| 3862 | |||
| 3863 | g_free(fname); | ||
| 3864 | |||
| 3865 | return fp; | ||
| 3866 | #else | ||
| 3867 | const gchar suffix[] = ".XXXXXX"; | ||
| 3868 | const gchar *tmpdir; | ||
| 3869 | guint tmplen; | ||
| 3870 | const gchar *progname; | ||
| 3871 | guint proglen; | ||
| 3872 | gchar *fname; | ||
| 3873 | gint fd; | ||
| 3874 | FILE *fp; | ||
| 3875 | |||
| 3876 | tmpdir = get_tmp_dir(); | ||
| 3877 | tmplen = strlen(tmpdir); | ||
| 3878 | progname = g_get_prgname(); | ||
| 3879 | if (!progname) | ||
| 3880 | progname = "sylph"; | ||
| 3881 | proglen = strlen(progname); | ||
| 3882 | fname = g_malloc(tmplen + 1 + proglen + sizeof(suffix)); | ||
| 3883 | |||
| 3884 | memcpy(fname, tmpdir, tmplen); | ||
| 3885 | fname[tmplen] = G_DIR_SEPARATOR; | ||
| 3886 | memcpy(fname + tmplen + 1, progname, proglen); | ||
| 3887 | memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix)); | ||
| 3888 | |||
| 3889 | fd = g_mkstemp(fname); | ||
| 3890 | if (fd < 0) { | ||
| 3891 | g_free(fname); | ||
| 3892 | return tmpfile(); | ||
| 3893 | } | ||
| 3894 | |||
| 3895 | g_unlink(fname); | ||
| 3896 | |||
| 3897 | fp = fdopen(fd, "w+b"); | ||
| 3898 | if (!fp) { | ||
| 3899 | perror("fdopen"); | ||
| 3900 | close(fd); | ||
| 3901 | } | ||
| 3902 | |||
| 3903 | g_free(fname); | ||
| 3904 | |||
| 3905 | return fp; | ||
| 3906 | #endif | ||
| 3907 | } | ||
| 3908 | |||
| 3909 | FILE *str_open_as_stream(const gchar *str) | ||
| 3910 | { | ||
| 3911 | FILE *fp; | ||
| 3912 | size_t len; | ||
| 3913 | |||
| 3914 | g_return_val_if_fail(str != NULL, NULL); | ||
| 3915 | |||
| 3916 | fp = my_tmpfile(); | ||
| 3917 | if (!fp) { | ||
| 3918 | FILE_OP_ERROR("str_open_as_stream", "my_tmpfile"); | ||
| 3919 | return NULL; | ||
| 3920 | } | ||
| 3921 | |||
| 3922 | len = strlen(str); | ||
| 3923 | if (len == 0) return fp; | ||
| 3924 | |||
| 3925 | if (fwrite(str, len, 1, fp) != 1) { | ||
| 3926 | FILE_OP_ERROR("str_open_as_stream", "fwrite"); | ||
| 3927 | fclose(fp); | ||
| 3928 | return NULL; | ||
| 3929 | } | ||
| 3930 | if (fflush(fp) == EOF) { | ||
| 3931 | FILE_OP_ERROR("str_open_as_stream", "fflush"); | ||
| 3932 | fclose(fp); | ||
| 3933 | return NULL; | ||
| 3934 | } | ||
| 3935 | |||
| 3936 | rewind(fp); | ||
| 3937 | return fp; | ||
| 3938 | } | ||
| 3939 | |||
| 3940 | gint str_write_to_file(const gchar *str, const gchar *file) | ||
| 3941 | { | ||
| 3942 | FILE *fp; | ||
| 3943 | size_t len; | ||
| 3944 | |||
| 3945 | g_return_val_if_fail(str != NULL, -1); | ||
| 3946 | g_return_val_if_fail(file != NULL, -1); | ||
| 3947 | |||
| 3948 | if ((fp = g_fopen(file, "wb")) == NULL) { | ||
| 3949 | FILE_OP_ERROR(file, "fopen"); | ||
| 3950 | return -1; | ||
| 3951 | } | ||
| 3952 | |||
| 3953 | len = strlen(str); | ||
| 3954 | if (len == 0) { | ||
| 3955 | fclose(fp); | ||
| 3956 | return 0; | ||
| 3957 | } | ||
| 3958 | |||
| 3959 | if (fwrite(str, len, 1, fp) != 1) { | ||
| 3960 | FILE_OP_ERROR(file, "fwrite"); | ||
| 3961 | fclose(fp); | ||
| 3962 | g_unlink(file); | ||
| 3963 | return -1; | ||
| 3964 | } | ||
| 3965 | |||
| 3966 | if (fclose(fp) == EOF) { | ||
| 3967 | FILE_OP_ERROR(file, "fclose"); | ||
| 3968 | g_unlink(file); | ||
| 3969 | return -1; | ||
| 3970 | } | ||
| 3971 | |||
| 3972 | return 0; | ||
| 3973 | } | ||
| 3974 | |||
| 3975 | gchar *file_read_to_str(const gchar *file) | ||
| 3976 | { | ||
| 3977 | FILE *fp; | ||
| 3978 | gchar *str; | ||
| 3979 | |||
| 3980 | g_return_val_if_fail(file != NULL, NULL); | ||
| 3981 | |||
| 3982 | if ((fp = g_fopen(file, "rb")) == NULL) { | ||
| 3983 | FILE_OP_ERROR(file, "fopen"); | ||
| 3984 | return NULL; | ||
| 3985 | } | ||
| 3986 | |||
| 3987 | str = file_read_stream_to_str(fp); | ||
| 3988 | |||
| 3989 | fclose(fp); | ||
| 3990 | |||
| 3991 | return str; | ||
| 3992 | } | ||
| 3993 | |||
| 3994 | gchar *file_read_stream_to_str(FILE *fp) | ||
| 3995 | { | ||
| 3996 | GByteArray *array; | ||
| 3997 | guchar buf[BUFSIZ]; | ||
| 3998 | gint n_read; | ||
| 3999 | gchar *str; | ||
| 4000 | |||
| 4001 | g_return_val_if_fail(fp != NULL, NULL); | ||
| 4002 | |||
| 4003 | array = g_byte_array_new(); | ||
| 4004 | |||
| 4005 | while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) { | ||
| 4006 | if (n_read < sizeof(buf) && ferror(fp)) | ||
| 4007 | break; | ||
| 4008 | g_byte_array_append(array, buf, n_read); | ||
| 4009 | } | ||
| 4010 | |||
| 4011 | if (ferror(fp)) { | ||
| 4012 | FILE_OP_ERROR("file stream", "fread"); | ||
| 4013 | g_byte_array_free(array, TRUE); | ||
| 4014 | return NULL; | ||
| 4015 | } | ||
| 4016 | |||
| 4017 | buf[0] = '\0'; | ||
| 4018 | g_byte_array_append(array, buf, 1); | ||
| 4019 | str = (gchar *)array->data; | ||
| 4020 | g_byte_array_free(array, FALSE); | ||
| 4021 | |||
| 4022 | return str; | ||
| 4023 | } | ||
| 4024 | |||
| 4025 | #if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2) | ||
| 4026 | static gchar **argv_utf8_to_locale(gchar **argv) | ||
| 4027 | { | ||
| 4028 | gint argc = 0, i; | ||
| 4029 | gchar **cp_argv; | ||
| 4030 | |||
| 4031 | while (argv[argc] != NULL) | ||
| 4032 | argc++; | ||
| 4033 | |||
| 4034 | cp_argv = g_new(gchar *, argc + 1); | ||
| 4035 | |||
| 4036 | for (i = 0; i < argc; i++) { | ||
| 4037 | cp_argv[i] = g_locale_from_utf8(argv[i], -1, NULL, NULL, NULL); | ||
| 4038 | if (cp_argv[i] == NULL) { | ||
| 4039 | g_warning("Failed to convert from UTF-8 to locale encoding: %s\n", argv[i]); | ||
| 4040 | g_strfreev(cp_argv); | ||
| 4041 | return NULL; | ||
| 4042 | } | ||
| 4043 | } | ||
| 4044 | cp_argv[i] = NULL; | ||
| 4045 | |||
| 4046 | return cp_argv; | ||
| 4047 | } | ||
| 4048 | #endif | ||
| 4049 | |||
| 4050 | gint execute_async(gchar *const argv[]) | ||
| 4051 | { | ||
| 4052 | #if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2) | ||
| 4053 | gchar **cp_argv; | ||
| 4054 | |||
| 4055 | g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); | ||
| 4056 | |||
| 4057 | cp_argv = argv_utf8_to_locale((gchar **)argv); | ||
| 4058 | if (!cp_argv) | ||
| 4059 | return -1; | ||
| 4060 | if (g_spawn_async(NULL, cp_argv, NULL, G_SPAWN_SEARCH_PATH, | ||
| 4061 | NULL, NULL, NULL, NULL) == FALSE) { | ||
| 4062 | g_warning("Can't execute command: %s\n", argv[0]); | ||
| 4063 | g_strfreev(cp_argv); | ||
| 4064 | return -1; | ||
| 4065 | } | ||
| 4066 | g_strfreev(cp_argv); | ||
| 4067 | #else | ||
| 4068 | g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); | ||
| 4069 | |||
| 4070 | if (g_spawn_async(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, | ||
| 4071 | NULL, NULL, NULL, NULL) == FALSE) { | ||
| 4072 | g_warning("Can't execute command: %s\n", argv[0]); | ||
| 4073 | return -1; | ||
| 4074 | } | ||
| 4075 | #endif | ||
| 4076 | |||
| 4077 | return 0; | ||
| 4078 | } | ||
| 4079 | |||
| 4080 | gint execute_sync(gchar *const argv[]) | ||
| 4081 | { | ||
| 4082 | gint status; | ||
| 4083 | #if defined(G_OS_WIN32) && !GLIB_CHECK_VERSION(2, 8, 2) | ||
| 4084 | gchar **cp_argv; | ||
| 4085 | #endif | ||
| 4086 | |||
| 4087 | g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1); | ||
| 4088 | |||
| 4089 | #ifdef G_OS_WIN32 | ||
| 4090 | #if !GLIB_CHECK_VERSION(2, 8, 2) | ||
| 4091 | cp_argv = argv_utf8_to_locale((gchar **)argv); | ||
| 4092 | if (!cp_argv) | ||
| 4093 | return -1; | ||
| 4094 | if (g_spawn_sync(NULL, cp_argv, NULL, | ||
| 4095 | G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN | | ||
| 4096 | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, | ||
| 4097 | NULL, NULL, NULL, NULL, &status, NULL) == FALSE) { | ||
| 4098 | g_warning("Can't execute command: %s\n", argv[0]); | ||
| 4099 | g_strfreev(cp_argv); | ||
| 4100 | return -1; | ||
| 4101 | } | ||
| 4102 | g_strfreev(cp_argv); | ||
| 4103 | #else /* !GLIB_CHECK_VERSION */ | ||
| 4104 | if (g_spawn_sync(NULL, (gchar **)argv, NULL, | ||
| 4105 | G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN | | ||
| 4106 | G_SPAWN_LEAVE_DESCRIPTORS_OPEN, | ||
| 4107 | NULL, NULL, NULL, NULL, &status, NULL) == FALSE) { | ||
| 4108 | g_warning("Can't execute command: %s\n", argv[0]); | ||
| 4109 | return -1; | ||
| 4110 | } | ||
| 4111 | #endif /* !GLIB_CHECK_VERSION */ | ||
| 4112 | |||
| 4113 | return status; | ||
| 4114 | #else /* G_OS_WIN32 */ | ||
| 4115 | if (g_spawn_sync(NULL, (gchar **)argv, NULL, G_SPAWN_SEARCH_PATH, | ||
| 4116 | NULL, NULL, NULL, NULL, &status, NULL) == FALSE) { | ||
| 4117 | g_warning("Can't execute command: %s\n", argv[0]); | ||
| 4118 | return -1; | ||
| 4119 | } | ||
| 4120 | |||
| 4121 | if (WIFEXITED(status)) | ||
| 4122 | return WEXITSTATUS(status); | ||
| 4123 | else | ||
| 4124 | return -1; | ||
| 4125 | #endif /* G_OS_WIN32 */ | ||
| 4126 | } | ||
| 4127 | |||
| 4128 | gint execute_command_line(const gchar *cmdline, gboolean async) | ||
| 4129 | { | ||
| 4130 | gchar **argv; | ||
| 4131 | gint ret; | ||
| 4132 | |||
| 4133 | if (debug_mode) { | ||
| 4134 | gchar *utf8_cmdline; | ||
| 4135 | |||
| 4136 | utf8_cmdline = g_filename_to_utf8 | ||
| 4137 | (cmdline, -1, NULL, NULL, NULL); | ||
| 4138 | debug_print("execute_command_line(): executing: %s\n", | ||
| 4139 | utf8_cmdline ? utf8_cmdline : cmdline); | ||
| 4140 | g_free(utf8_cmdline); | ||
| 4141 | } | ||
| 4142 | |||
| 4143 | argv = strsplit_with_quote(cmdline, " ", 0); | ||
| 4144 | |||
| 4145 | if (async) | ||
| 4146 | ret = execute_async(argv); | ||
| 4147 | else | ||
| 4148 | ret = execute_sync(argv); | ||
| 4149 | |||
| 4150 | g_strfreev(argv); | ||
| 4151 | |||
| 4152 | return ret; | ||
| 4153 | } | ||
| 4154 | |||
| 4155 | #if USE_THREADS | ||
| 4156 | typedef struct _CmdData | ||
| 4157 | { | ||
| 4158 | const gchar *cmdline; | ||
| 4159 | volatile gint flag; | ||
| 4160 | gint status; | ||
| 4161 | } CmdData; | ||
| 4162 | |||
| 4163 | static gpointer execute_command_line_async_func(gpointer data) | ||
| 4164 | { | ||
| 4165 | CmdData *cmd_data = (CmdData *)data; | ||
| 4166 | gchar **argv; | ||
| 4167 | |||
| 4168 | argv = strsplit_with_quote(cmd_data->cmdline, " ", 0); | ||
| 4169 | cmd_data->status = execute_sync(argv); | ||
| 4170 | g_strfreev(argv); | ||
| 4171 | |||
| 4172 | debug_print("execute_command_line_async_func: exec done: %s\n", | ||
| 4173 | cmd_data->cmdline); | ||
| 4174 | g_atomic_int_set(&cmd_data->flag, 1); | ||
| 4175 | g_main_context_wakeup(NULL); | ||
| 4176 | |||
| 4177 | return GINT_TO_POINTER(0); | ||
| 4178 | } | ||
| 4179 | |||
| 4180 | gint execute_command_line_async_wait(const gchar *cmdline) | ||
| 4181 | { | ||
| 4182 | CmdData data = {NULL, 0, 0}; | ||
| 4183 | GThread *thread; | ||
| 4184 | |||
| 4185 | if (debug_mode) { | ||
| 4186 | gchar *utf8_cmdline; | ||
| 4187 | |||
| 4188 | utf8_cmdline = g_filename_to_utf8 | ||
| 4189 | (cmdline, -1, NULL, NULL, NULL); | ||
| 4190 | debug_print("execute_command_line(): executing: %s\n", | ||
| 4191 | utf8_cmdline ? utf8_cmdline : cmdline); | ||
| 4192 | g_free(utf8_cmdline); | ||
| 4193 | } | ||
| 4194 | |||
| 4195 | data.cmdline = cmdline; | ||
| 4196 | thread = g_thread_create(execute_command_line_async_func, &data, TRUE, | ||
| 4197 | NULL); | ||
| 4198 | if (!thread) | ||
| 4199 | return -1; | ||
| 4200 | |||
| 4201 | debug_print("execute_command_line_async_wait: waiting thread\n"); | ||
| 4202 | while (g_atomic_int_get(&data.flag) == 0) | ||
| 4203 | event_loop_iterate(); | ||
| 4204 | |||
| 4205 | g_thread_join(thread); | ||
| 4206 | debug_print("execute_command_line_async_wait: thread exited\n"); | ||
| 4207 | |||
| 4208 | return data.status; | ||
| 4209 | } | ||
| 4210 | #else /* USE_THREADS */ | ||
| 4211 | gint execute_command_line_async_wait(const gchar *cmdline) | ||
| 4212 | { | ||
| 4213 | return execute_command_line(cmdline, FALSE); | ||
| 4214 | } | ||
| 4215 | #endif /* USE_THREADS */ | ||
| 4216 | |||
| 4217 | gint execute_open_file(const gchar *file, const gchar *content_type) | ||
| 4218 | { | ||
| 4219 | #ifdef G_OS_WIN32 | ||
| 4220 | g_return_val_if_fail(file != NULL, -1); | ||
| 4221 | |||
| 4222 | log_print("opening %s - %s\n", file, content_type ? content_type : ""); | ||
| 4223 | |||
| 4224 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 4225 | wchar_t *wpath; | ||
| 4226 | |||
| 4227 | wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL); | ||
| 4228 | if (wpath == NULL) | ||
| 4229 | return -1; | ||
| 4230 | |||
| 4231 | ShellExecuteW(NULL, L"open", wpath, NULL, NULL, SW_SHOWNORMAL); | ||
| 4232 | |||
| 4233 | g_free(wpath); | ||
| 4234 | |||
| 4235 | return 0; | ||
| 4236 | } else { | ||
| 4237 | gchar *cp_path; | ||
| 4238 | |||
| 4239 | cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL); | ||
| 4240 | if (cp_path == NULL) | ||
| 4241 | return -1; | ||
| 4242 | |||
| 4243 | ShellExecuteA(NULL, "open", cp_path, NULL, NULL, SW_SHOWNORMAL); | ||
| 4244 | |||
| 4245 | g_free(cp_path); | ||
| 4246 | |||
| 4247 | return 0; | ||
| 4248 | } | ||
| 4249 | #elif defined(__APPLE__) | ||
| 4250 | const gchar *argv[3] = {"open", NULL, NULL}; | ||
| 4251 | |||
| 4252 | g_return_val_if_fail(file != NULL, -1); | ||
| 4253 | |||
| 4254 | log_print("opening %s - %s\n", file, content_type ? content_type : ""); | ||
| 4255 | |||
| 4256 | argv[1] = file; | ||
| 4257 | execute_async(argv); | ||
| 4258 | #else | ||
| 4259 | const gchar *argv[3] = {"xdg-open", NULL, NULL}; | ||
| 4260 | |||
| 4261 | g_return_val_if_fail(file != NULL, -1); | ||
| 4262 | |||
| 4263 | log_print("opening %s - %s\n", file, content_type ? content_type : ""); | ||
| 4264 | |||
| 4265 | argv[1] = file; | ||
| 4266 | execute_async(argv); | ||
| 4267 | #endif | ||
| 4268 | |||
| 4269 | return 0; | ||
| 4270 | } | ||
| 4271 | |||
| 4272 | gint execute_print_file(const gchar *file) | ||
| 4273 | { | ||
| 4274 | g_return_val_if_fail(file != NULL, -1); | ||
| 4275 | |||
| 4276 | #ifdef G_OS_WIN32 | ||
| 4277 | log_print("printing %s\n", file); | ||
| 4278 | |||
| 4279 | if (G_WIN32_HAVE_WIDECHAR_API()) { | ||
| 4280 | wchar_t *wpath; | ||
| 4281 | |||
| 4282 | wpath = g_utf8_to_utf16(file, -1, NULL, NULL, NULL); | ||
| 4283 | if (wpath == NULL) | ||
| 4284 | return -1; | ||
| 4285 | |||
| 4286 | ShellExecuteW(NULL, L"print", wpath, NULL, NULL, SW_SHOWNORMAL); | ||
| 4287 | |||
| 4288 | g_free(wpath); | ||
| 4289 | |||
| 4290 | return 0; | ||
| 4291 | } else { | ||
| 4292 | gchar *cp_path; | ||
| 4293 | |||
| 4294 | cp_path = g_locale_from_utf8(file, -1, NULL, NULL, NULL); | ||
| 4295 | if (cp_path == NULL) | ||
| 4296 | return -1; | ||
| 4297 | |||
| 4298 | ShellExecuteA(NULL, "print", cp_path, NULL, NULL, | ||
| 4299 | SW_SHOWNORMAL); | ||
| 4300 | |||
| 4301 | g_free(cp_path); | ||
| 4302 | |||
| 4303 | return 0; | ||
| 4304 | } | ||
| 4305 | #endif | ||
| 4306 | return 0; | ||
| 4307 | } | ||
| 4308 | |||
| 4309 | gchar *get_command_output(const gchar *cmdline) | ||
| 4310 | { | ||
| 4311 | gchar *child_stdout; | ||
| 4312 | gint status; | ||
| 4313 | |||
| 4314 | g_return_val_if_fail(cmdline != NULL, NULL); | ||
| 4315 | |||
| 4316 | debug_print("get_command_output(): executing: %s\n", cmdline); | ||
| 4317 | |||
| 4318 | if (g_spawn_command_line_sync(cmdline, &child_stdout, NULL, &status, | ||
| 4319 | NULL) == FALSE) { | ||
| 4320 | g_warning("Can't execute command: %s\n", cmdline); | ||
| 4321 | return NULL; | ||
| 4322 | } | ||
| 4323 | |||
| 4324 | return child_stdout; | ||
| 4325 | } | ||
| 4326 | |||
| 4327 | gint open_uri(const gchar *uri, const gchar *cmdline) | ||
| 4328 | { | ||
| 4329 | gchar buf[BUFFSIZE]; | ||
| 4330 | |||
| 4331 | g_return_val_if_fail(uri != NULL, -1); | ||
| 4332 | |||
| 4333 | #if defined(G_OS_WIN32) || defined(__APPLE__) | ||
| 4334 | if (!cmdline || cmdline[0] == '\0') | ||
| 4335 | return execute_open_file(uri, NULL); | ||
| 4336 | #endif | ||
| 4337 | |||
| 4338 | if (cmdline && str_find_format_times(cmdline, 's') == 1) | ||
| 4339 | g_snprintf(buf, sizeof(buf), cmdline, uri); | ||
| 4340 | else { | ||
| 4341 | if (cmdline) | ||
| 4342 | g_warning("Open URI command line is invalid " | ||
| 4343 | "(there must be only one '%%s'): %s", | ||
| 4344 | cmdline); | ||
| 4345 | g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, uri); | ||
| 4346 | } | ||
| 4347 | |||
| 4348 | execute_command_line(buf, TRUE); | ||
| 4349 | |||
| 4350 | return 0; | ||
| 4351 | } | ||
| 4352 | |||
| 4353 | gint play_sound(const gchar *file, gboolean async) | ||
| 4354 | { | ||
| 4355 | #ifdef G_OS_WIN32 | ||
| 4356 | wchar_t *wfile; | ||
| 4357 | DWORD flag = SND_FILENAME; | ||
| 4358 | |||
| 4359 | wfile = g_utf8_to_utf16(file, -1, NULL, NULL, NULL); | ||
| 4360 | if (wfile == NULL) | ||
| 4361 | return -1; | ||
| 4362 | if (async) | ||
| 4363 | flag |= SND_ASYNC; | ||
| 4364 | else | ||
| 4365 | flag |= SND_SYNC; | ||
| 4366 | PlaySoundW(wfile, NULL, flag); | ||
| 4367 | g_free(wfile); | ||
| 4368 | #endif | ||
| 4369 | return 0; | ||
| 4370 | } | ||
| 4371 | |||
| 4372 | stime_t remote_tzoffset_sec(const gchar *zone) | ||
| 4373 | { | ||
| 4374 | static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT"; | ||
| 4375 | gchar zone3[4]; | ||
| 4376 | gchar *p; | ||
| 4377 | gchar c; | ||
| 4378 | gint iustz; | ||
| 4379 | gint offset; | ||
| 4380 | time_t remoteoffset; | ||
| 4381 | |||
| 4382 | strncpy(zone3, zone, 3); | ||
| 4383 | zone3[3] = '\0'; | ||
| 4384 | remoteoffset = 0; | ||
| 4385 | |||
| 4386 | if (sscanf(zone, "%c%d", &c, &offset) == 2 && | ||
| 4387 | (c == '+' || c == '-')) { | ||
| 4388 | remoteoffset = ((offset / 100) * 60 + (offset % 100)) * 60; | ||
| 4389 | if (c == '-') | ||
| 4390 | remoteoffset = -remoteoffset; | ||
| 4391 | } else if (!strncmp(zone, "UT" , 2) || | ||
| 4392 | !strncmp(zone, "GMT", 2)) { | ||
| 4393 | remoteoffset = 0; | ||
| 4394 | } else if (strlen(zone3) == 3) { | ||
| 4395 | for (p = ustzstr; *p != '\0'; p += 3) { | ||
| 4396 | if (!g_ascii_strncasecmp(p, zone3, 3)) { | ||
| 4397 | iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8; | ||
| 4398 | remoteoffset = iustz * 3600; | ||
| 4399 | break; | ||
| 4400 | } | ||
| 4401 | } | ||
| 4402 | if (*p == '\0') | ||
| 4403 | return -1; | ||
| 4404 | } else if (strlen(zone3) == 1) { | ||
| 4405 | switch (zone[0]) { | ||
| 4406 | case 'Z': remoteoffset = 0; break; | ||
| 4407 | case 'A': remoteoffset = -1; break; | ||
| 4408 | case 'B': remoteoffset = -2; break; | ||
| 4409 | case 'C': remoteoffset = -3; break; | ||
| 4410 | case 'D': remoteoffset = -4; break; | ||
| 4411 | case 'E': remoteoffset = -5; break; | ||
| 4412 | case 'F': remoteoffset = -6; break; | ||
| 4413 | case 'G': remoteoffset = -7; break; | ||
| 4414 | case 'H': remoteoffset = -8; break; | ||
| 4415 | case 'I': remoteoffset = -9; break; | ||
| 4416 | case 'K': remoteoffset = -10; break; /* J is not used */ | ||
| 4417 | case 'L': remoteoffset = -11; break; | ||
| 4418 | case 'M': remoteoffset = -12; break; | ||
| 4419 | case 'N': remoteoffset = 1; break; | ||
| 4420 | case 'O': remoteoffset = 2; break; | ||
| 4421 | case 'P': remoteoffset = 3; break; | ||
| 4422 | case 'Q': remoteoffset = 4; break; | ||
| 4423 | case 'R': remoteoffset = 5; break; | ||
| 4424 | case 'S': remoteoffset = 6; break; | ||
| 4425 | case 'T': remoteoffset = 7; break; | ||
| 4426 | case 'U': remoteoffset = 8; break; | ||
| 4427 | case 'V': remoteoffset = 9; break; | ||
| 4428 | case 'W': remoteoffset = 10; break; | ||
| 4429 | case 'X': remoteoffset = 11; break; | ||
| 4430 | case 'Y': remoteoffset = 12; break; | ||
| 4431 | default: remoteoffset = 0; break; | ||
| 4432 | } | ||
| 4433 | remoteoffset = remoteoffset * 3600; | ||
| 4434 | } else | ||
| 4435 | return -1; | ||
| 4436 | |||
| 4437 | return remoteoffset; | ||
| 4438 | } | ||
| 4439 | |||
| 4440 | stime_t tzoffset_sec(stime_t *now) | ||
| 4441 | { | ||
| 4442 | time_t now_ = *now; | ||
| 4443 | struct tm gmt, *tmp, *lt; | ||
| 4444 | gint off; | ||
| 4445 | |||
| 4446 | tmp = gmtime(&now_); | ||
| 4447 | g_return_val_if_fail(tmp != NULL, -1); | ||
| 4448 | gmt = *tmp; | ||
| 4449 | lt = localtime(&now_); | ||
| 4450 | g_return_val_if_fail(lt != NULL, -1); | ||
| 4451 | |||
| 4452 | off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; | ||
| 4453 | |||
| 4454 | if (lt->tm_year < gmt.tm_year) | ||
| 4455 | off -= 24 * 60; | ||
| 4456 | else if (lt->tm_year > gmt.tm_year) | ||
| 4457 | off += 24 * 60; | ||
| 4458 | else if (lt->tm_yday < gmt.tm_yday) | ||
| 4459 | off -= 24 * 60; | ||
| 4460 | else if (lt->tm_yday > gmt.tm_yday) | ||
| 4461 | off += 24 * 60; | ||
| 4462 | |||
| 4463 | if (off >= 24 * 60) /* should be impossible */ | ||
| 4464 | off = 23 * 60 + 59; /* if not, insert silly value */ | ||
| 4465 | if (off <= -24 * 60) | ||
| 4466 | off = -(23 * 60 + 59); | ||
| 4467 | |||
| 4468 | return off * 60; | ||
| 4469 | } | ||
| 4470 | |||
| 4471 | /* calculate timezone offset (buf must not be less than 6 bytes) */ | ||
| 4472 | gchar *tzoffset_buf(gchar *buf, stime_t *now) | ||
| 4473 | { | ||
| 4474 | time_t now_ = *now; | ||
| 4475 | struct tm gmt, *tmp, *lt; | ||
| 4476 | gint off; | ||
| 4477 | gchar sign = '+'; | ||
| 4478 | |||
| 4479 | tmp = gmtime(&now_); | ||
| 4480 | g_return_val_if_fail(tmp != NULL, NULL); | ||
| 4481 | gmt = *tmp; | ||
| 4482 | lt = localtime(&now_); | ||
| 4483 | g_return_val_if_fail(lt != NULL, NULL); | ||
| 4484 | |||
| 4485 | off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; | ||
| 4486 | |||
| 4487 | if (lt->tm_year < gmt.tm_year) | ||
| 4488 | off -= 24 * 60; | ||
| 4489 | else if (lt->tm_year > gmt.tm_year) | ||
| 4490 | off += 24 * 60; | ||
| 4491 | else if (lt->tm_yday < gmt.tm_yday) | ||
| 4492 | off -= 24 * 60; | ||
| 4493 | else if (lt->tm_yday > gmt.tm_yday) | ||
| 4494 | off += 24 * 60; | ||
| 4495 | |||
| 4496 | if (off < 0) { | ||
| 4497 | sign = '-'; | ||
| 4498 | off = -off; | ||
| 4499 | } | ||
| 4500 | |||
| 4501 | if (off >= 24 * 60) /* should be impossible */ | ||
| 4502 | off = 23 * 60 + 59; /* if not, insert silly value */ | ||
| 4503 | |||
| 4504 | g_snprintf(buf, 6, "%c%02d%02d", sign, off / 60, off % 60); | ||
| 4505 | |||
| 4506 | return buf; | ||
| 4507 | } | ||
| 4508 | |||
| 4509 | gchar *tzoffset(stime_t *now) | ||
| 4510 | { | ||
| 4511 | static gchar offset_string[6]; | ||
| 4512 | |||
| 4513 | return tzoffset_buf(offset_string, now); | ||
| 4514 | } | ||
| 4515 | |||
| 4516 | void get_rfc822_date(gchar *buf, gint len) | ||
| 4517 | { | ||
| 4518 | struct tm *lt; | ||
| 4519 | time_t t; | ||
| 4520 | stime_t t_; | ||
| 4521 | gchar day[4], mon[4]; | ||
| 4522 | gint dd, hh, mm, ss, yyyy; | ||
| 4523 | gchar off[6]; | ||
| 4524 | |||
| 4525 | t_ = t = time(NULL); | ||
| 4526 | lt = localtime(&t); | ||
| 4527 | |||
| 4528 | sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n", | ||
| 4529 | day, mon, &dd, &hh, &mm, &ss, &yyyy); | ||
| 4530 | g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s", | ||
| 4531 | day, dd, mon, yyyy, hh, mm, ss, tzoffset_buf(off, &t_)); | ||
| 4532 | } | ||
| 4533 | |||
| 4534 | /* just a wrapper to suppress the warning of gcc about %c */ | ||
| 4535 | size_t my_strftime(gchar *s, size_t max, const gchar *format, | ||
| 4536 | const struct tm *tm) | ||
| 4537 | { | ||
| 4538 | return strftime(s, max, format, tm); | ||
| 4539 | } | ||
| 4540 | |||
| 4541 | /* UI hints */ | ||
| 4542 | |||
| 4543 | static UIUpdateFunc ui_update_func = NULL; | ||
| 4544 | |||
| 4545 | void set_ui_update_func(UIUpdateFunc func) | ||
| 4546 | { | ||
| 4547 | ui_update_func = func; | ||
| 4548 | } | ||
| 4549 | |||
| 4550 | void ui_update(void) | ||
| 4551 | { | ||
| 4552 | if (ui_update_func) | ||
| 4553 | ui_update_func(); | ||
| 4554 | } | ||
| 4555 | |||
| 4556 | static EventLoopFunc event_loop_func = NULL; | ||
| 4557 | |||
| 4558 | void set_event_loop_func(EventLoopFunc func) | ||
| 4559 | { | ||
| 4560 | event_loop_func = func; | ||
| 4561 | } | ||
| 4562 | |||
| 4563 | void event_loop_iterate(void) | ||
| 4564 | { | ||
| 4565 | if (event_loop_func) | ||
| 4566 | event_loop_func(); | ||
| 4567 | else | ||
| 4568 | g_main_context_iteration(NULL, TRUE); | ||
| 4569 | } | ||
| 4570 | |||
| 4571 | static ProgressFunc progress_func = NULL; | ||
| 4572 | |||
| 4573 | void set_progress_func(ProgressFunc func) | ||
| 4574 | { | ||
| 4575 | progress_func = func; | ||
| 4576 | } | ||
| 4577 | |||
| 4578 | void progress_show(gint cur, gint total) | ||
| 4579 | { | ||
| 4580 | if (progress_func) | ||
| 4581 | progress_func(cur, total); | ||
| 4582 | } | ||
| 4583 | |||
| 4584 | /* user input */ | ||
| 4585 | |||
| 4586 | static QueryPasswordFunc query_password_func = NULL; | ||
| 4587 | |||
| 4588 | void set_input_query_password_func(QueryPasswordFunc func) | ||
| 4589 | { | ||
| 4590 | query_password_func = func; | ||
| 4591 | } | ||
| 4592 | |||
| 4593 | gchar *input_query_password(const gchar *server, const gchar *user) | ||
| 4594 | { | ||
| 4595 | if (query_password_func) | ||
| 4596 | return query_password_func(server, user); | ||
| 4597 | else | ||
| 4598 | return NULL; | ||
| 4599 | } | ||
| 4600 | |||
| 4601 | /* logging */ | ||
| 4602 | |||
| 4603 | static FILE *log_fp = NULL; | ||
| 4604 | #if USE_THREADS | ||
| 4605 | G_LOCK_DEFINE_STATIC(log_fp); | ||
| 4606 | #define S_LOCK(name) G_LOCK(name) | ||
| 4607 | #define S_UNLOCK(name) G_UNLOCK(name) | ||
| 4608 | #else | ||
| 4609 | #define S_LOCK(name) | ||
| 4610 | #define S_UNLOCK(name) | ||
| 4611 | #endif | ||
| 4612 | |||
| 4613 | void set_log_file(const gchar *filename) | ||
| 4614 | { | ||
| 4615 | S_LOCK(log_fp); | ||
| 4616 | if (!log_fp) { | ||
| 4617 | log_fp = g_fopen(filename, "w"); | ||
| 4618 | if (!log_fp) | ||
| 4619 | FILE_OP_ERROR(filename, "fopen"); | ||
| 4620 | } | ||
| 4621 | S_UNLOCK(log_fp); | ||
| 4622 | } | ||
| 4623 | |||
| 4624 | void close_log_file(void) | ||
| 4625 | { | ||
| 4626 | S_LOCK(log_fp); | ||
| 4627 | if (log_fp) { | ||
| 4628 | fclose(log_fp); | ||
| 4629 | log_fp = NULL; | ||
| 4630 | } | ||
| 4631 | S_UNLOCK(log_fp); | ||
| 4632 | } | ||
| 4633 | |||
| 4634 | static guint log_verbosity_count = 0; | ||
| 4635 | |||
| 4636 | void set_log_verbosity(gboolean verbose) | ||
| 4637 | { | ||
| 4638 | if (verbose) | ||
| 4639 | log_verbosity_count++; | ||
| 4640 | else if (log_verbosity_count > 0) | ||
| 4641 | log_verbosity_count--; | ||
| 4642 | } | ||
| 4643 | |||
| 4644 | gboolean get_debug_mode(void) | ||
| 4645 | { | ||
| 4646 | return debug_mode; | ||
| 4647 | } | ||
| 4648 | |||
| 4649 | void set_debug_mode(gboolean enable) | ||
| 4650 | { | ||
| 4651 | debug_mode = enable; | ||
| 4652 | } | ||
| 4653 | |||
| 4654 | static void log_dummy_func(const gchar *str) | ||
| 4655 | { | ||
| 4656 | } | ||
| 4657 | |||
| 4658 | static void log_dummy_flush_func(void) | ||
| 4659 | { | ||
| 4660 | } | ||
| 4661 | |||
| 4662 | static LogFunc log_print_ui_func = log_dummy_func; | ||
| 4663 | static LogFunc log_message_ui_func = log_dummy_func; | ||
| 4664 | static LogFunc log_warning_ui_func = log_dummy_func; | ||
| 4665 | static LogFunc log_error_ui_func = log_dummy_func; | ||
| 4666 | static LogFlushFunc log_flush_ui_func = log_dummy_flush_func; | ||
| 4667 | |||
| 4668 | static LogFunc log_show_status_func = log_dummy_func; | ||
| 4669 | |||
| 4670 | void set_log_ui_func(LogFunc print_func, LogFunc message_func, | ||
| 4671 | LogFunc warning_func, LogFunc error_func) | ||
| 4672 | { | ||
| 4673 | log_print_ui_func = print_func; | ||
| 4674 | log_message_ui_func = message_func; | ||
| 4675 | log_warning_ui_func = warning_func; | ||
| 4676 | log_error_ui_func = error_func; | ||
| 4677 | } | ||
| 4678 | |||
| 4679 | void set_log_ui_func_full(LogFunc print_func, LogFunc message_func, | ||
| 4680 | LogFunc warning_func, LogFunc error_func, | ||
| 4681 | LogFlushFunc flush_func) | ||
| 4682 | { | ||
| 4683 | set_log_ui_func(print_func, message_func, warning_func, error_func); | ||
| 4684 | log_flush_ui_func = flush_func; | ||
| 4685 | } | ||
| 4686 | |||
| 4687 | void set_log_show_status_func(LogFunc status_func) | ||
| 4688 | { | ||
| 4689 | log_show_status_func = status_func; | ||
| 4690 | } | ||
| 4691 | |||
| 4692 | void debug_print(const gchar *format, ...) | ||
| 4693 | { | ||
| 4694 | va_list args; | ||
| 4695 | gchar buf[BUFFSIZE]; | ||
| 4696 | |||
| 4697 | if (!debug_mode) return; | ||
| 4698 | |||
| 4699 | va_start(args, format); | ||
| 4700 | g_vsnprintf(buf, sizeof(buf), format, args); | ||
| 4701 | va_end(args); | ||
| 4702 | |||
| 4703 | g_print("%s", buf); | ||
| 4704 | } | ||
| 4705 | |||
| 4706 | void status_print(const gchar *format, ...) | ||
| 4707 | { | ||
| 4708 | va_list args; | ||
| 4709 | gchar buf[BUFFSIZE]; | ||
| 4710 | |||
| 4711 | va_start(args, format); | ||
| 4712 | g_vsnprintf(buf, sizeof(buf), format, args); | ||
| 4713 | va_end(args); | ||
| 4714 | |||
| 4715 | log_show_status_func(buf); | ||
| 4716 | } | ||
| 4717 | |||
| 4718 | #define TIME_LEN 11 | ||
| 4719 | |||
| 4720 | void log_write(const gchar *str, const gchar *prefix) | ||
| 4721 | { | ||
| 4722 | S_LOCK(log_fp); | ||
| 4723 | |||
| 4724 | if (log_fp) { | ||
| 4725 | gchar buf[TIME_LEN + 1]; | ||
| 4726 | time_t t; | ||
| 4727 | |||
| 4728 | time(&t); | ||
| 4729 | strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); | ||
| 4730 | |||
| 4731 | fputs(buf, log_fp); | ||
| 4732 | if (prefix) | ||
| 4733 | fputs(prefix, log_fp); | ||
| 4734 | fputs(str, log_fp); | ||
| 4735 | fflush(log_fp); | ||
| 4736 | } | ||
| 4737 | |||
| 4738 | S_UNLOCK(log_fp); | ||
| 4739 | } | ||
| 4740 | |||
| 4741 | void log_print(const gchar *format, ...) | ||
| 4742 | { | ||
| 4743 | va_list args; | ||
| 4744 | gchar buf[BUFFSIZE + TIME_LEN]; | ||
| 4745 | time_t t; | ||
| 4746 | |||
| 4747 | time(&t); | ||
| 4748 | strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); | ||
| 4749 | |||
| 4750 | va_start(args, format); | ||
| 4751 | g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); | ||
| 4752 | va_end(args); | ||
| 4753 | |||
| 4754 | if (debug_mode) g_print("%s", buf); | ||
| 4755 | log_print_ui_func(buf); | ||
| 4756 | S_LOCK(log_fp); | ||
| 4757 | if (log_fp) { | ||
| 4758 | fputs(buf, log_fp); | ||
| 4759 | fflush(log_fp); | ||
| 4760 | } | ||
| 4761 | S_UNLOCK(log_fp); | ||
| 4762 | if (log_verbosity_count) | ||
| 4763 | log_show_status_func(buf + TIME_LEN); | ||
| 4764 | } | ||
| 4765 | |||
| 4766 | void log_message(const gchar *format, ...) | ||
| 4767 | { | ||
| 4768 | va_list args; | ||
| 4769 | gchar buf[BUFFSIZE + TIME_LEN]; | ||
| 4770 | time_t t; | ||
| 4771 | |||
| 4772 | time(&t); | ||
| 4773 | strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); | ||
| 4774 | |||
| 4775 | va_start(args, format); | ||
| 4776 | g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); | ||
| 4777 | va_end(args); | ||
| 4778 | |||
| 4779 | if (debug_mode) g_message("%s", buf + TIME_LEN); | ||
| 4780 | log_message_ui_func(buf + TIME_LEN); | ||
| 4781 | S_LOCK(log_fp); | ||
| 4782 | if (log_fp) { | ||
| 4783 | fwrite(buf, TIME_LEN, 1, log_fp); | ||
| 4784 | fputs("* message: ", log_fp); | ||
| 4785 | fputs(buf + TIME_LEN, log_fp); | ||
| 4786 | fflush(log_fp); | ||
| 4787 | } | ||
| 4788 | S_UNLOCK(log_fp); | ||
| 4789 | log_show_status_func(buf + TIME_LEN); | ||
| 4790 | } | ||
| 4791 | |||
| 4792 | void log_warning(const gchar *format, ...) | ||
| 4793 | { | ||
| 4794 | va_list args; | ||
| 4795 | gchar buf[BUFFSIZE + TIME_LEN]; | ||
| 4796 | time_t t; | ||
| 4797 | |||
| 4798 | time(&t); | ||
| 4799 | strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); | ||
| 4800 | |||
| 4801 | va_start(args, format); | ||
| 4802 | g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); | ||
| 4803 | va_end(args); | ||
| 4804 | |||
| 4805 | g_warning("%s", buf); | ||
| 4806 | log_warning_ui_func(buf + TIME_LEN); | ||
| 4807 | S_LOCK(log_fp); | ||
| 4808 | if (log_fp) { | ||
| 4809 | fwrite(buf, TIME_LEN, 1, log_fp); | ||
| 4810 | fputs("** warning: ", log_fp); | ||
| 4811 | fputs(buf + TIME_LEN, log_fp); | ||
| 4812 | fflush(log_fp); | ||
| 4813 | } | ||
| 4814 | S_UNLOCK(log_fp); | ||
| 4815 | } | ||
| 4816 | |||
| 4817 | void log_error(const gchar *format, ...) | ||
| 4818 | { | ||
| 4819 | va_list args; | ||
| 4820 | gchar buf[BUFFSIZE + TIME_LEN]; | ||
| 4821 | time_t t; | ||
| 4822 | |||
| 4823 | time(&t); | ||
| 4824 | strftime(buf, TIME_LEN + 1, "[%H:%M:%S] ", localtime(&t)); | ||
| 4825 | |||
| 4826 | va_start(args, format); | ||
| 4827 | g_vsnprintf(buf + TIME_LEN, BUFFSIZE, format, args); | ||
| 4828 | va_end(args); | ||
| 4829 | |||
| 4830 | g_warning("%s", buf); | ||
| 4831 | log_error_ui_func(buf + TIME_LEN); | ||
| 4832 | S_LOCK(log_fp); | ||
| 4833 | if (log_fp) { | ||
| 4834 | fwrite(buf, TIME_LEN, 1, log_fp); | ||
| 4835 | fputs("*** error: ", log_fp); | ||
| 4836 | fputs(buf + TIME_LEN, log_fp); | ||
| 4837 | fflush(log_fp); | ||
| 4838 | } | ||
| 4839 | S_UNLOCK(log_fp); | ||
| 4840 | } | ||
| 4841 | |||
| 4842 | void log_flush(void) | ||
| 4843 | { | ||
| 4844 | S_LOCK(log_fp); | ||
| 4845 | if (log_fp) | ||
| 4846 | fflush(log_fp); | ||
| 4847 | S_UNLOCK(log_fp); | ||
| 4848 | log_flush_ui_func(); | ||
| 4849 | } | ||
diff --git a/libsylph/utils.h b/libsylph/utils.h new file mode 100644 index 0000000..9ac65cf --- /dev/null +++ b/libsylph/utils.h | |||
| @@ -0,0 +1,599 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2017 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __UTILS_H__ | ||
| 21 | #define __UTILS_H__ | ||
| 22 | |||
| 23 | #ifdef HAVE_CONFIG_H | ||
| 24 | # include "config.h" | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #include <glib.h> | ||
| 28 | #include <stdio.h> | ||
| 29 | #include <string.h> | ||
| 30 | #include <stdlib.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | #include <sys/types.h> | ||
| 33 | #include <dirent.h> | ||
| 34 | #include <time.h> | ||
| 35 | #if HAVE_ALLOCA_H | ||
| 36 | # include <alloca.h> | ||
| 37 | #endif | ||
| 38 | |||
| 39 | /* Wrappers for C library function that take pathname arguments. */ | ||
| 40 | #if GLIB_CHECK_VERSION(2, 6, 0) | ||
| 41 | # include <glib/gstdio.h> | ||
| 42 | #else | ||
| 43 | |||
| 44 | #define g_open open | ||
| 45 | #define g_rename rename | ||
| 46 | #define g_mkdir mkdir | ||
| 47 | #define g_stat stat | ||
| 48 | #define g_lstat lstat | ||
| 49 | #define g_unlink unlink | ||
| 50 | #define g_remove remove | ||
| 51 | #define g_rmdir rmdir | ||
| 52 | #define g_fopen fopen | ||
| 53 | #define g_freopen freopen | ||
| 54 | |||
| 55 | #endif /* GLIB_CHECK_VERSION */ | ||
| 56 | |||
| 57 | #if !GLIB_CHECK_VERSION(2, 7, 0) | ||
| 58 | |||
| 59 | #ifdef G_OS_UNIX | ||
| 60 | #define g_chdir chdir | ||
| 61 | #define g_chmod chmod | ||
| 62 | #else | ||
| 63 | gint g_chdir (const gchar *path); | ||
| 64 | gint g_chmod (const gchar *path, | ||
| 65 | gint mode); | ||
| 66 | #endif /* G_OS_UNIX */ | ||
| 67 | |||
| 68 | #endif /* !GLIB_CHECK_VERSION */ | ||
| 69 | |||
| 70 | #ifdef G_OS_UNIX | ||
| 71 | #define syl_link link | ||
| 72 | #else | ||
| 73 | gint syl_link (const gchar *src, | ||
| 74 | const gchar *dest); | ||
| 75 | #endif | ||
| 76 | |||
| 77 | /* The AC_CHECK_SIZEOF() in configure fails for some machines. | ||
| 78 | * we provide some fallback values here */ | ||
| 79 | #if !SIZEOF_UNSIGNED_SHORT | ||
| 80 | #undef SIZEOF_UNSIGNED_SHORT | ||
| 81 | #define SIZEOF_UNSIGNED_SHORT 2 | ||
| 82 | #endif | ||
| 83 | #if !SIZEOF_UNSIGNED_INT | ||
| 84 | #undef SIZEOF_UNSIGNED_INT | ||
| 85 | #define SIZEOF_UNSIGNED_INT 4 | ||
| 86 | #endif | ||
| 87 | #if !SIZEOF_UNSIGNED_LONG | ||
| 88 | #undef SIZEOF_UNSIGNED_LONG | ||
| 89 | #define SIZEOF_UNSIGNED_LONG 4 | ||
| 90 | #endif | ||
| 91 | |||
| 92 | #ifndef HAVE_U32_TYPEDEF | ||
| 93 | #undef u32 /* maybe there is a macro with this name */ | ||
| 94 | typedef guint32 u32; | ||
| 95 | #define HAVE_U32_TYPEDEF | ||
| 96 | #endif | ||
| 97 | |||
| 98 | #if defined(G_OS_WIN32) && !defined(_WIN64) && defined(HAVE_64BIT_TIME_T) | ||
| 99 | /* for backward binary compatibility. Use only in struct definition and | ||
| 100 | pointer arguments. */ | ||
| 101 | typedef gint32 stime_t; | ||
| 102 | #else | ||
| 103 | typedef time_t stime_t; | ||
| 104 | #endif | ||
| 105 | |||
| 106 | #if !GLIB_CHECK_VERSION(2, 26, 0) | ||
| 107 | #if (defined (_MSC_VER) || defined (__MINGW32__)) && !defined(_WIN64) | ||
| 108 | typedef struct _stat32 GStatBuf; | ||
| 109 | #else | ||
| 110 | typedef struct stat GStatBuf; | ||
| 111 | #endif | ||
| 112 | #endif | ||
| 113 | |||
| 114 | #ifndef BIG_ENDIAN_HOST | ||
| 115 | #if (G_BYTE_ORDER == G_BIG_ENDIAN) | ||
| 116 | #define BIG_ENDIAN_HOST 1 | ||
| 117 | #endif | ||
| 118 | #endif | ||
| 119 | |||
| 120 | #define CHDIR_RETURN_IF_FAIL(dir) \ | ||
| 121 | { \ | ||
| 122 | if (change_dir(dir) < 0) return; \ | ||
| 123 | } | ||
| 124 | |||
| 125 | #define CHDIR_RETURN_VAL_IF_FAIL(dir, val) \ | ||
| 126 | { \ | ||
| 127 | if (change_dir(dir) < 0) return val; \ | ||
| 128 | } | ||
| 129 | |||
| 130 | #define Xalloca(ptr, size, iffail) \ | ||
| 131 | { \ | ||
| 132 | if ((ptr = alloca(size)) == NULL) { \ | ||
| 133 | g_warning("can't allocate memory\n"); \ | ||
| 134 | iffail; \ | ||
| 135 | } \ | ||
| 136 | } | ||
| 137 | |||
| 138 | #define Xstrdup_a(ptr, str, iffail) \ | ||
| 139 | { \ | ||
| 140 | gchar *__tmp; \ | ||
| 141 | \ | ||
| 142 | if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \ | ||
| 143 | g_warning("can't allocate memory\n"); \ | ||
| 144 | iffail; \ | ||
| 145 | } else \ | ||
| 146 | strcpy(__tmp, str); \ | ||
| 147 | \ | ||
| 148 | ptr = __tmp; \ | ||
| 149 | } | ||
| 150 | |||
| 151 | #define Xstrndup_a(ptr, str, len, iffail) \ | ||
| 152 | { \ | ||
| 153 | gchar *__tmp; \ | ||
| 154 | \ | ||
| 155 | if ((__tmp = alloca(len + 1)) == NULL) { \ | ||
| 156 | g_warning("can't allocate memory\n"); \ | ||
| 157 | iffail; \ | ||
| 158 | } else { \ | ||
| 159 | strncpy(__tmp, str, len); \ | ||
| 160 | __tmp[len] = '\0'; \ | ||
| 161 | } \ | ||
| 162 | \ | ||
| 163 | ptr = __tmp; \ | ||
| 164 | } | ||
| 165 | |||
| 166 | #define Xstrcat_a(ptr, str1, str2, iffail) \ | ||
| 167 | { \ | ||
| 168 | gchar *__tmp; \ | ||
| 169 | gint len1, len2; \ | ||
| 170 | \ | ||
| 171 | len1 = strlen(str1); \ | ||
| 172 | len2 = strlen(str2); \ | ||
| 173 | if ((__tmp = alloca(len1 + len2 + 1)) == NULL) { \ | ||
| 174 | g_warning("can't allocate memory\n"); \ | ||
| 175 | iffail; \ | ||
| 176 | } else { \ | ||
| 177 | memcpy(__tmp, str1, len1); \ | ||
| 178 | memcpy(__tmp + len1, str2, len2 + 1); \ | ||
| 179 | } \ | ||
| 180 | \ | ||
| 181 | ptr = __tmp; \ | ||
| 182 | } | ||
| 183 | |||
| 184 | #define AUTORELEASE_STR(str, iffail) \ | ||
| 185 | { \ | ||
| 186 | gchar *__str; \ | ||
| 187 | Xstrdup_a(__str, str, iffail); \ | ||
| 188 | g_free(str); \ | ||
| 189 | str = __str; \ | ||
| 190 | } | ||
| 191 | |||
| 192 | #define FILE_OP_ERROR(file, func) \ | ||
| 193 | { \ | ||
| 194 | fprintf(stderr, "%s: ", file); \ | ||
| 195 | fflush(stderr); \ | ||
| 196 | perror(func); \ | ||
| 197 | } | ||
| 198 | |||
| 199 | typedef void (*UIUpdateFunc) (void); | ||
| 200 | typedef void (*EventLoopFunc) (void); | ||
| 201 | typedef void (*ProgressFunc) (gint cur, | ||
| 202 | gint total); | ||
| 203 | typedef gchar * (*QueryPasswordFunc) (const gchar *server, | ||
| 204 | const gchar *user); | ||
| 205 | typedef void (*LogFunc) (const gchar *str); | ||
| 206 | typedef void (*LogFlushFunc) (void); | ||
| 207 | |||
| 208 | /* for macro expansion */ | ||
| 209 | #define Str(x) #x | ||
| 210 | #define Xstr(x) Str(x) | ||
| 211 | |||
| 212 | void list_free_strings (GList *list); | ||
| 213 | void slist_free_strings (GSList *list); | ||
| 214 | |||
| 215 | void hash_free_strings (GHashTable *table); | ||
| 216 | void hash_free_value_mem (GHashTable *table); | ||
| 217 | |||
| 218 | gint str_case_equal (gconstpointer v, | ||
| 219 | gconstpointer v2); | ||
| 220 | guint str_case_hash (gconstpointer key); | ||
| 221 | |||
| 222 | void ptr_array_free_strings (GPtrArray *array); | ||
| 223 | |||
| 224 | typedef gboolean (*StrFindFunc) (const gchar *haystack, | ||
| 225 | const gchar *needle); | ||
| 226 | |||
| 227 | gboolean str_find (const gchar *haystack, | ||
| 228 | const gchar *needle); | ||
| 229 | gboolean str_case_find (const gchar *haystack, | ||
| 230 | const gchar *needle); | ||
| 231 | gboolean str_find_equal (const gchar *haystack, | ||
| 232 | const gchar *needle); | ||
| 233 | gboolean str_case_find_equal (const gchar *haystack, | ||
| 234 | const gchar *needle); | ||
| 235 | |||
| 236 | /* number-string conversion */ | ||
| 237 | gint to_number (const gchar *nstr); | ||
| 238 | guint to_unumber (const gchar *nstr); | ||
| 239 | gchar *itos_buf (gchar *nstr, | ||
| 240 | gint n); | ||
| 241 | gchar *itos (gint n); | ||
| 242 | gchar *utos_buf (gchar *nstr, | ||
| 243 | guint n); | ||
| 244 | gchar *to_human_readable_buf (gchar *buf, | ||
| 245 | size_t bufsize, | ||
| 246 | gint64 size); | ||
| 247 | gchar *to_human_readable (gint64 size); | ||
| 248 | |||
| 249 | /* alternative string functions */ | ||
| 250 | gint strcmp2 (const gchar *s1, | ||
| 251 | const gchar *s2); | ||
| 252 | gint path_cmp (const gchar *s1, | ||
| 253 | const gchar *s2); | ||
| 254 | gboolean is_path_parent (const gchar *parent, | ||
| 255 | const gchar *child); | ||
| 256 | |||
| 257 | gchar *strretchomp (gchar *str); | ||
| 258 | gchar *strtailchomp (gchar *str, | ||
| 259 | gchar tail_char); | ||
| 260 | gchar *strcrchomp (gchar *str); | ||
| 261 | |||
| 262 | gchar *strcasestr (const gchar *haystack, | ||
| 263 | const gchar *needle); | ||
| 264 | gpointer my_memmem (gconstpointer haystack, | ||
| 265 | size_t haystacklen, | ||
| 266 | gconstpointer needle, | ||
| 267 | size_t needlelen); | ||
| 268 | |||
| 269 | gchar *strncpy2 (gchar *dest, | ||
| 270 | const gchar *src, | ||
| 271 | size_t n); | ||
| 272 | |||
| 273 | gboolean str_has_suffix_case (const gchar *str, | ||
| 274 | const gchar *suffix); | ||
| 275 | |||
| 276 | gint str_find_format_times (const gchar *haystack, | ||
| 277 | gchar ch); | ||
| 278 | |||
| 279 | gboolean is_next_nonascii (const gchar *s); | ||
| 280 | gint get_next_word_len (const gchar *s); | ||
| 281 | |||
| 282 | /* functions for string parsing */ | ||
| 283 | gint subject_compare (const gchar *s1, | ||
| 284 | const gchar *s2); | ||
| 285 | gint subject_compare_for_sort (const gchar *s1, | ||
| 286 | const gchar *s2); | ||
| 287 | void trim_subject_for_compare (gchar *str); | ||
| 288 | void trim_subject_for_sort (gchar *str); | ||
| 289 | void trim_subject (gchar *str); | ||
| 290 | void eliminate_parenthesis (gchar *str, | ||
| 291 | gchar op, | ||
| 292 | gchar cl); | ||
| 293 | void extract_parenthesis (gchar *str, | ||
| 294 | gchar op, | ||
| 295 | gchar cl); | ||
| 296 | void extract_parenthesis_with_escape (gchar *str, | ||
| 297 | gchar op, | ||
| 298 | gchar cl); | ||
| 299 | |||
| 300 | void extract_parenthesis_with_skip_quote (gchar *str, | ||
| 301 | gchar quote_chr, | ||
| 302 | gchar op, | ||
| 303 | gchar cl); | ||
| 304 | |||
| 305 | void eliminate_quote (gchar *str, | ||
| 306 | gchar quote_chr); | ||
| 307 | void extract_quote (gchar *str, | ||
| 308 | gchar quote_chr); | ||
| 309 | void extract_quote_with_escape (gchar *str, | ||
| 310 | gchar quote_chr); | ||
| 311 | void eliminate_address_comment (gchar *str); | ||
| 312 | gchar *strchr_with_skip_quote (const gchar *str, | ||
| 313 | gint quote_chr, | ||
| 314 | gint c); | ||
| 315 | gchar *strrchr_with_skip_quote (const gchar *str, | ||
| 316 | gint quote_chr, | ||
| 317 | gint c); | ||
| 318 | void extract_address (gchar *str); | ||
| 319 | void extract_list_id_str (gchar *str); | ||
| 320 | |||
| 321 | gchar *extract_addresses (const gchar *str); | ||
| 322 | |||
| 323 | gchar *normalize_address_field (const gchar *str); | ||
| 324 | |||
| 325 | gboolean address_equal (const gchar *addr1, | ||
| 326 | const gchar *addr2); | ||
| 327 | |||
| 328 | GSList *address_list_append_orig (GSList *addr_list, | ||
| 329 | const gchar *str); | ||
| 330 | GSList *address_list_append (GSList *addr_list, | ||
| 331 | const gchar *str); | ||
| 332 | GSList *references_list_prepend (GSList *msgid_list, | ||
| 333 | const gchar *str); | ||
| 334 | GSList *references_list_append (GSList *msgid_list, | ||
| 335 | const gchar *str); | ||
| 336 | GSList *newsgroup_list_append (GSList *group_list, | ||
| 337 | const gchar *str); | ||
| 338 | |||
| 339 | GList *add_history (GList *list, | ||
| 340 | const gchar *str); | ||
| 341 | |||
| 342 | /* modify string */ | ||
| 343 | void remove_return (gchar *str); | ||
| 344 | void remove_space (gchar *str); | ||
| 345 | void unfold_line (gchar *str); | ||
| 346 | void subst_char (gchar *str, | ||
| 347 | gchar orig, | ||
| 348 | gchar subst); | ||
| 349 | void subst_chars (gchar *str, | ||
| 350 | gchar *orig, | ||
| 351 | gchar subst); | ||
| 352 | void subst_null (gchar *str, | ||
| 353 | gint len, | ||
| 354 | gchar subst); | ||
| 355 | void subst_control (gchar *str, | ||
| 356 | gchar subst); | ||
| 357 | void subst_for_filename (gchar *str); | ||
| 358 | |||
| 359 | gchar *get_alt_filename (const gchar *filename, | ||
| 360 | gint count); | ||
| 361 | |||
| 362 | gboolean is_header_line (const gchar *str); | ||
| 363 | gboolean is_ascii_str (const gchar *str); | ||
| 364 | |||
| 365 | gint get_quote_level (const gchar *str); | ||
| 366 | gint check_line_length (const gchar *str, | ||
| 367 | gint max_chars, | ||
| 368 | gint *line); | ||
| 369 | |||
| 370 | gchar *strstr_with_skip_quote (const gchar *haystack, | ||
| 371 | const gchar *needle); | ||
| 372 | gchar *strcasestr_with_skip_quote (const gchar *haystack, | ||
| 373 | const gchar *needle); | ||
| 374 | gchar *strchr_parenthesis_close (const gchar *str, | ||
| 375 | gchar op, | ||
| 376 | gchar cl); | ||
| 377 | |||
| 378 | gchar **strsplit_parenthesis (const gchar *str, | ||
| 379 | gchar op, | ||
| 380 | gchar cl, | ||
| 381 | gint max_tokens); | ||
| 382 | gchar **strsplit_with_quote (const gchar *str, | ||
| 383 | const gchar *delim, | ||
| 384 | gint max_tokens); | ||
| 385 | gchar **strsplit_csv (const gchar *str, | ||
| 386 | gchar delim, | ||
| 387 | gint max_tokens); | ||
| 388 | |||
| 389 | gchar *strconcat_csv (gchar delim, | ||
| 390 | const gchar *field1, | ||
| 391 | ...) G_GNUC_MALLOC G_GNUC_NULL_TERMINATED; | ||
| 392 | |||
| 393 | gchar *get_abbrev_newsgroup_name (const gchar *group, | ||
| 394 | gint len); | ||
| 395 | gchar *trim_string (const gchar *str, | ||
| 396 | gint len); | ||
| 397 | gchar *trim_string_before (const gchar *str, | ||
| 398 | gint len); | ||
| 399 | |||
| 400 | GList *uri_list_extract_filenames (const gchar *uri_list); | ||
| 401 | gboolean is_uri_string (const gchar *str); | ||
| 402 | gchar *get_uri_path (const gchar *uri); | ||
| 403 | gint get_uri_len (const gchar *str); | ||
| 404 | void decode_uri (gchar *decoded_uri, | ||
| 405 | const gchar *encoded_uri); | ||
| 406 | void decode_xdigit_encoded_str (gchar *decoded, | ||
| 407 | const gchar *encoded); | ||
| 408 | gchar *encode_uri (const gchar *filename); | ||
| 409 | gchar *uriencode_for_filename (const gchar *filename); | ||
| 410 | gchar *uriencode_for_mailto (const gchar *mailto); | ||
| 411 | gint scan_mailto_url (const gchar *mailto, | ||
| 412 | gchar **to, | ||
| 413 | gchar **cc, | ||
| 414 | gchar **bcc, | ||
| 415 | gchar **subject, | ||
| 416 | gchar **inreplyto, | ||
| 417 | gchar **body); | ||
| 418 | |||
| 419 | void set_startup_dir (void); | ||
| 420 | void set_rc_dir (const gchar *dir); | ||
| 421 | |||
| 422 | /* return static strings */ | ||
| 423 | const gchar *get_startup_dir (void); | ||
| 424 | const gchar *get_home_dir (void); | ||
| 425 | const gchar *get_document_dir (void); | ||
| 426 | const gchar *get_rc_dir (void); | ||
| 427 | const gchar *get_old_rc_dir (void); | ||
| 428 | const gchar *get_mail_base_dir (void); | ||
| 429 | const gchar *get_news_cache_dir (void); | ||
| 430 | const gchar *get_imap_cache_dir (void); | ||
| 431 | const gchar *get_mime_tmp_dir (void); | ||
| 432 | const gchar *get_template_dir (void); | ||
| 433 | const gchar *get_tmp_dir (void); | ||
| 434 | gchar *get_tmp_file (void); | ||
| 435 | const gchar *get_domain_name (void); | ||
| 436 | |||
| 437 | /* file / directory handling */ | ||
| 438 | off_t get_file_size (const gchar *file); | ||
| 439 | off_t get_file_size_as_crlf (const gchar *file); | ||
| 440 | off_t get_left_file_size (FILE *fp); | ||
| 441 | |||
| 442 | gint get_last_empty_line_size (FILE *fp, | ||
| 443 | off_t size); | ||
| 444 | |||
| 445 | gboolean file_exist (const gchar *file, | ||
| 446 | gboolean allow_fifo); | ||
| 447 | gboolean is_dir_exist (const gchar *dir); | ||
| 448 | gboolean is_file_entry_exist (const gchar *file); | ||
| 449 | gboolean dirent_is_regular_file (struct dirent *d); | ||
| 450 | gboolean dirent_is_directory (struct dirent *d); | ||
| 451 | |||
| 452 | #define is_file_exist(file) file_exist(file, FALSE) | ||
| 453 | #define is_file_or_fifo_exist(file) file_exist(file, TRUE) | ||
| 454 | |||
| 455 | gint change_dir (const gchar *dir); | ||
| 456 | gint make_dir (const gchar *dir); | ||
| 457 | gint make_dir_hier (const gchar *dir); | ||
| 458 | gint remove_all_files (const gchar *dir); | ||
| 459 | gint remove_numbered_files (const gchar *dir, | ||
| 460 | guint first, | ||
| 461 | guint last); | ||
| 462 | gint remove_all_numbered_files (const gchar *dir); | ||
| 463 | gint remove_expired_files (const gchar *dir, | ||
| 464 | guint hours); | ||
| 465 | gint remove_dir_recursive (const gchar *dir); | ||
| 466 | gint rename_force (const gchar *oldpath, | ||
| 467 | const gchar *newpath); | ||
| 468 | gint copy_file (const gchar *src, | ||
| 469 | const gchar *dest, | ||
| 470 | gboolean keep_backup); | ||
| 471 | gint copy_dir (const gchar *src, | ||
| 472 | const gchar *dest); | ||
| 473 | gint move_file (const gchar *src, | ||
| 474 | const gchar *dest, | ||
| 475 | gboolean overwrite); | ||
| 476 | |||
| 477 | gint append_file_part (FILE *fp, | ||
| 478 | off_t offset, | ||
| 479 | size_t length, | ||
| 480 | FILE *dest_fp); | ||
| 481 | gint copy_file_part (FILE *fp, | ||
| 482 | off_t offset, | ||
| 483 | size_t length, | ||
| 484 | const gchar *dest); | ||
| 485 | |||
| 486 | gint copy_file_stream (FILE *fp, | ||
| 487 | FILE *dest_fp); | ||
| 488 | |||
| 489 | gchar *canonicalize_str (const gchar *str); | ||
| 490 | gint canonicalize_file (const gchar *src, | ||
| 491 | const gchar *dest); | ||
| 492 | gint canonicalize_file_replace (const gchar *file); | ||
| 493 | FILE *canonicalize_file_stream (FILE *fp, | ||
| 494 | gint *length); | ||
| 495 | gint uncanonicalize_file (const gchar *src, | ||
| 496 | const gchar *dest); | ||
| 497 | gint uncanonicalize_file_replace(const gchar *file); | ||
| 498 | |||
| 499 | gchar *normalize_newlines (const gchar *str); | ||
| 500 | gchar *strchomp_all (const gchar *str); | ||
| 501 | |||
| 502 | FILE *get_outgoing_rfc2822_file (FILE *fp); | ||
| 503 | gchar *get_outgoing_rfc2822_str (FILE *fp); | ||
| 504 | gchar *generate_mime_boundary (const gchar *prefix); | ||
| 505 | |||
| 506 | gint change_file_mode_rw (FILE *fp, | ||
| 507 | const gchar *file); | ||
| 508 | FILE *my_tmpfile (void); | ||
| 509 | FILE *str_open_as_stream (const gchar *str); | ||
| 510 | gint str_write_to_file (const gchar *str, | ||
| 511 | const gchar *file); | ||
| 512 | gchar *file_read_to_str (const gchar *file); | ||
| 513 | gchar *file_read_stream_to_str (FILE *fp); | ||
| 514 | |||
| 515 | /* process execution */ | ||
| 516 | gint execute_async (gchar *const argv[]); | ||
| 517 | gint execute_sync (gchar *const argv[]); | ||
| 518 | gint execute_command_line (const gchar *cmdline, | ||
| 519 | gboolean async); | ||
| 520 | gint execute_command_line_async_wait | ||
| 521 | (const gchar *cmdline); | ||
| 522 | |||
| 523 | gint execute_open_file (const gchar *file, | ||
| 524 | const gchar *content_type); | ||
| 525 | gint execute_print_file (const gchar *file); | ||
| 526 | gchar *get_command_output (const gchar *cmdline); | ||
| 527 | |||
| 528 | /* open URI with external browser */ | ||
| 529 | gint open_uri (const gchar *uri, | ||
| 530 | const gchar *cmdline); | ||
| 531 | |||
| 532 | /* play sound */ | ||
| 533 | gint play_sound (const gchar *file, | ||
| 534 | gboolean async); | ||
| 535 | |||
| 536 | /* time functions */ | ||
| 537 | stime_t remote_tzoffset_sec (const gchar *zone); | ||
| 538 | stime_t tzoffset_sec (stime_t *now); | ||
| 539 | gchar *tzoffset_buf (gchar *buf, | ||
| 540 | stime_t *now); | ||
| 541 | gchar *tzoffset (stime_t *now); | ||
| 542 | void get_rfc822_date (gchar *buf, | ||
| 543 | gint len); | ||
| 544 | |||
| 545 | size_t my_strftime (gchar *s, | ||
| 546 | size_t max, | ||
| 547 | const gchar *format, | ||
| 548 | const struct tm *tm); | ||
| 549 | |||
| 550 | /* UI hints */ | ||
| 551 | void set_ui_update_func (UIUpdateFunc func); | ||
| 552 | void ui_update (void); | ||
| 553 | |||
| 554 | void set_event_loop_func (EventLoopFunc func); | ||
| 555 | void event_loop_iterate (void); | ||
| 556 | |||
| 557 | void set_progress_func (ProgressFunc func); | ||
| 558 | void progress_show (gint cur, | ||
| 559 | gint total); | ||
| 560 | |||
| 561 | /* user input */ | ||
| 562 | void set_input_query_password_func (QueryPasswordFunc func); | ||
| 563 | |||
| 564 | gchar *input_query_password (const gchar *server, | ||
| 565 | const gchar *user); | ||
| 566 | |||
| 567 | /* logging */ | ||
| 568 | void set_log_file (const gchar *filename); | ||
| 569 | void close_log_file (void); | ||
| 570 | void set_log_verbosity (gboolean verbose); | ||
| 571 | gboolean get_debug_mode (void); | ||
| 572 | void set_debug_mode (gboolean enable); | ||
| 573 | |||
| 574 | void set_log_ui_func (LogFunc print_func, | ||
| 575 | LogFunc message_func, | ||
| 576 | LogFunc warning_func, | ||
| 577 | LogFunc error_func); | ||
| 578 | void set_log_ui_func_full (LogFunc print_func, | ||
| 579 | LogFunc message_func, | ||
| 580 | LogFunc warning_func, | ||
| 581 | LogFunc error_func, | ||
| 582 | LogFlushFunc flush_func); | ||
| 583 | |||
| 584 | void set_log_show_status_func (LogFunc status_func); | ||
| 585 | |||
| 586 | void debug_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 587 | void status_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 588 | |||
| 589 | void log_write (const gchar *str, | ||
| 590 | const gchar *prefix); | ||
| 591 | |||
| 592 | void log_print (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 593 | void log_message (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 594 | void log_warning (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 595 | void log_error (const gchar *format, ...) G_GNUC_PRINTF(1, 2); | ||
| 596 | |||
| 597 | void log_flush (void); | ||
| 598 | |||
| 599 | #endif /* __UTILS_H__ */ | ||
diff --git a/libsylph/uuencode.c b/libsylph/uuencode.c new file mode 100644 index 0000000..090e3d3 --- /dev/null +++ b/libsylph/uuencode.c | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <ctype.h> | ||
| 21 | |||
| 22 | #define UUDECODE(c) (c=='`' ? 0 : c - ' ') | ||
| 23 | #define N64(i) (i & ~63) | ||
| 24 | |||
| 25 | const char uudigit[64] = | ||
| 26 | { | ||
| 27 | '`', '!', '"', '#', '$', '%', '&', '\'', | ||
| 28 | '(', ')', '*', '+', ',', '-', '.', '/', | ||
| 29 | '0', '1', '2', '3', '4', '5', '6', '7', | ||
| 30 | '8', '9', ':', ';', '<', '=', '>', '?', | ||
| 31 | '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', | ||
| 32 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', | ||
| 33 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | ||
| 34 | 'X', 'Y', 'Z', '[', '\\', ']', '^', '_' | ||
| 35 | }; | ||
| 36 | |||
| 37 | int touufrombits(unsigned char *out, const unsigned char *in, int inlen) | ||
| 38 | { | ||
| 39 | int len; | ||
| 40 | |||
| 41 | if (inlen > 45) return -1; | ||
| 42 | len = (inlen * 4 + 2) / 3 + 1; | ||
| 43 | *out++ = uudigit[inlen]; | ||
| 44 | |||
| 45 | for (; inlen >= 3; inlen -= 3) { | ||
| 46 | *out++ = uudigit[in[0] >> 2]; | ||
| 47 | *out++ = uudigit[((in[0] << 4) & 0x30) | (in[1] >> 4)]; | ||
| 48 | *out++ = uudigit[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; | ||
| 49 | *out++ = uudigit[in[2] & 0x3f]; | ||
| 50 | in += 3; | ||
| 51 | } | ||
| 52 | |||
| 53 | if (inlen > 0) { | ||
| 54 | *out++ = uudigit[(in[0] >> 2)]; | ||
| 55 | if (inlen == 1) { | ||
| 56 | *out++ = uudigit[((in[0] << 4) & 0x30)]; | ||
| 57 | } else { | ||
| 58 | *out++ = uudigit[(((in[0] << 4) & 0x30) | (in[1] >> 4))] ; | ||
| 59 | *out++ = uudigit[((in[1] << 2) & 0x3c)]; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | *out = '\0'; | ||
| 63 | |||
| 64 | return len; | ||
| 65 | } | ||
| 66 | |||
| 67 | int fromuutobits(char *out, const char *in) | ||
| 68 | { | ||
| 69 | int len, outlen, inlen; | ||
| 70 | register unsigned char digit1, digit2; | ||
| 71 | |||
| 72 | outlen = UUDECODE(in[0]); | ||
| 73 | in += 1; | ||
| 74 | if(outlen < 0 || outlen > 45) | ||
| 75 | return -2; | ||
| 76 | if(outlen == 0) | ||
| 77 | return 0; | ||
| 78 | inlen = (outlen * 4 + 2) / 3; | ||
| 79 | len = 0; | ||
| 80 | |||
| 81 | for( ; inlen>0; inlen-=4) { | ||
| 82 | digit1 = UUDECODE(in[0]); | ||
| 83 | if (N64(digit1)) return -1; | ||
| 84 | digit2 = UUDECODE(in[1]); | ||
| 85 | if (N64(digit2)) return -1; | ||
| 86 | out[len++] = (digit1 << 2) | (digit2 >> 4); | ||
| 87 | if (inlen > 2) { | ||
| 88 | digit1 = UUDECODE(in[2]); | ||
| 89 | if (N64(digit1)) return -1; | ||
| 90 | out[len++] = (digit2 << 4) | (digit1 >> 2); | ||
| 91 | if (inlen > 3) { | ||
| 92 | digit2 = UUDECODE(in[3]); | ||
| 93 | if (N64(digit2)) return -1; | ||
| 94 | out[len++] = (digit1 << 6) | digit2; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | in += 4; | ||
| 98 | } | ||
| 99 | |||
| 100 | return len == outlen ? len : -3; | ||
| 101 | } | ||
diff --git a/libsylph/uuencode.h b/libsylph/uuencode.h new file mode 100644 index 0000000..15a1800 --- /dev/null +++ b/libsylph/uuencode.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | int touufrombits(unsigned char *, const unsigned char *, int); | ||
| 21 | int fromuutobits(char *, const char *); | ||
| 22 | |||
| 23 | #define X_UUENCODE_END_LINE '`' | ||
| 24 | #define UUENCODE_END_LINE ' ' | ||
diff --git a/libsylph/virtual.c b/libsylph/virtual.c new file mode 100644 index 0000000..ead73ee --- /dev/null +++ b/libsylph/virtual.c | |||
| @@ -0,0 +1,603 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2010 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifdef HAVE_CONFIG_H | ||
| 21 | # include "config.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "defs.h" | ||
| 25 | |||
| 26 | #include <glib.h> | ||
| 27 | #include <glib/gi18n.h> | ||
| 28 | #include <dirent.h> | ||
| 29 | #include <sys/stat.h> | ||
| 30 | #include <time.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | #include <string.h> | ||
| 33 | #include <errno.h> | ||
| 34 | |||
| 35 | #undef MEASURE_TIME | ||
| 36 | |||
| 37 | #include "folder.h" | ||
| 38 | #include "virtual.h" | ||
| 39 | #include "mh.h" | ||
| 40 | #include "procmsg.h" | ||
| 41 | #include "procheader.h" | ||
| 42 | #include "filter.h" | ||
| 43 | #include "utils.h" | ||
| 44 | |||
| 45 | typedef struct _VirtualSearchInfo VirtualSearchInfo; | ||
| 46 | typedef struct _SearchCacheInfo SearchCacheInfo; | ||
| 47 | |||
| 48 | struct _VirtualSearchInfo { | ||
| 49 | FilterRule *rule; | ||
| 50 | GSList *mlist; | ||
| 51 | GHashTable *search_cache_table; | ||
| 52 | FILE *fp; | ||
| 53 | gboolean requires_full_headers; | ||
| 54 | gboolean exclude_trash; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct _SearchCacheInfo { | ||
| 58 | FolderItem *folder; | ||
| 59 | guint msgnum; | ||
| 60 | off_t size; | ||
| 61 | time_t mtime; | ||
| 62 | MsgFlags flags; | ||
| 63 | }; | ||
| 64 | |||
| 65 | enum | ||
| 66 | { | ||
| 67 | SCACHE_NOT_EXIST = 0, | ||
| 68 | SCACHE_MATCHED = 1, | ||
| 69 | SCACHE_NOT_MATCHED = 2 | ||
| 70 | }; | ||
| 71 | |||
| 72 | static void virtual_folder_init (Folder *folder, | ||
| 73 | const gchar *name, | ||
| 74 | const gchar *path); | ||
| 75 | |||
| 76 | static GHashTable *virtual_read_search_cache | ||
| 77 | (FolderItem *item); | ||
| 78 | static void virtual_write_search_cache (FILE *fp, | ||
| 79 | FolderItem *item, | ||
| 80 | MsgInfo *msginfo, | ||
| 81 | gint matched); | ||
| 82 | |||
| 83 | static GSList *virtual_search_folder (VirtualSearchInfo *info, | ||
| 84 | FolderItem *item); | ||
| 85 | static gboolean virtual_search_recursive_func | ||
| 86 | (GNode *node, | ||
| 87 | gpointer data); | ||
| 88 | |||
| 89 | static Folder *virtual_folder_new (const gchar *name, | ||
| 90 | const gchar *path); | ||
| 91 | static void virtual_folder_destroy (Folder *folder); | ||
| 92 | |||
| 93 | static GSList *virtual_get_msg_list (Folder *folder, | ||
| 94 | FolderItem *item, | ||
| 95 | gboolean use_cache); | ||
| 96 | static gchar *virtual_fetch_msg (Folder *folder, | ||
| 97 | FolderItem *item, | ||
| 98 | gint num); | ||
| 99 | static MsgInfo *virtual_get_msginfo (Folder *folder, | ||
| 100 | FolderItem *item, | ||
| 101 | gint num); | ||
| 102 | static gint virtual_close (Folder *folder, | ||
| 103 | FolderItem *item); | ||
| 104 | |||
| 105 | static gint virtual_scan_folder (Folder *folder, | ||
| 106 | FolderItem *item); | ||
| 107 | |||
| 108 | static gint virtual_rename_folder (Folder *folder, | ||
| 109 | FolderItem *item, | ||
| 110 | const gchar *name); | ||
| 111 | static gint virtual_move_folder (Folder *folder, | ||
| 112 | FolderItem *item, | ||
| 113 | FolderItem *new_parent); | ||
| 114 | static gint virtual_remove_folder (Folder *folder, | ||
| 115 | FolderItem *item); | ||
| 116 | |||
| 117 | static FolderClass virtual_class = | ||
| 118 | { | ||
| 119 | F_VIRTUAL, | ||
| 120 | |||
| 121 | virtual_folder_new, | ||
| 122 | virtual_folder_destroy, | ||
| 123 | |||
| 124 | NULL, | ||
| 125 | NULL, | ||
| 126 | |||
| 127 | virtual_get_msg_list, | ||
| 128 | NULL, | ||
| 129 | virtual_fetch_msg, | ||
| 130 | virtual_get_msginfo, | ||
| 131 | NULL, | ||
| 132 | NULL, | ||
| 133 | NULL, | ||
| 134 | NULL, | ||
| 135 | NULL, | ||
| 136 | NULL, | ||
| 137 | NULL, | ||
| 138 | NULL, | ||
| 139 | NULL, | ||
| 140 | NULL, | ||
| 141 | NULL, | ||
| 142 | NULL, | ||
| 143 | virtual_close, | ||
| 144 | virtual_scan_folder, | ||
| 145 | |||
| 146 | NULL, | ||
| 147 | virtual_rename_folder, | ||
| 148 | virtual_move_folder, | ||
| 149 | virtual_remove_folder, | ||
| 150 | }; | ||
| 151 | |||
| 152 | |||
| 153 | FolderClass *virtual_get_class(void) | ||
| 154 | { | ||
| 155 | return &virtual_class; | ||
| 156 | } | ||
| 157 | |||
| 158 | static Folder *virtual_folder_new(const gchar *name, const gchar *path) | ||
| 159 | { | ||
| 160 | Folder *folder; | ||
| 161 | |||
| 162 | folder = (Folder *)g_new0(VirtualFolder, 1); | ||
| 163 | virtual_folder_init(folder, name, path); | ||
| 164 | |||
| 165 | return folder; | ||
| 166 | } | ||
| 167 | |||
| 168 | static void virtual_folder_destroy(Folder *folder) | ||
| 169 | { | ||
| 170 | folder_local_folder_destroy(LOCAL_FOLDER(folder)); | ||
| 171 | } | ||
| 172 | |||
| 173 | static void virtual_folder_init(Folder *folder, const gchar *name, | ||
| 174 | const gchar *path) | ||
| 175 | { | ||
| 176 | folder->klass = virtual_get_class(); | ||
| 177 | folder_local_folder_init(folder, name, path); | ||
| 178 | } | ||
| 179 | |||
| 180 | guint sinfo_hash(gconstpointer key) | ||
| 181 | { | ||
| 182 | const SearchCacheInfo *sinfo = key; | ||
| 183 | guint h; | ||
| 184 | |||
| 185 | h = (guint)sinfo->folder; | ||
| 186 | h ^= sinfo->msgnum; | ||
| 187 | h ^= (guint)sinfo->size; | ||
| 188 | h ^= (guint)sinfo->mtime; | ||
| 189 | /* h ^= (guint)sinfo->flags.tmp_flags; */ | ||
| 190 | h ^= (guint)sinfo->flags.perm_flags; | ||
| 191 | |||
| 192 | /* g_print("path: %s, n = %u, hash = %u\n", | ||
| 193 | sinfo->folder->path, sinfo->msgnum, h); */ | ||
| 194 | |||
| 195 | return h; | ||
| 196 | } | ||
| 197 | |||
| 198 | gint sinfo_equal(gconstpointer v, gconstpointer v2) | ||
| 199 | { | ||
| 200 | const SearchCacheInfo *s1 = v; | ||
| 201 | const SearchCacheInfo *s2 = v2; | ||
| 202 | |||
| 203 | return (s1->folder == s2->folder && s1->msgnum == s2->msgnum && | ||
| 204 | s1->size == s2->size && s1->mtime == s2->mtime && | ||
| 205 | /* s1->flags.tmp_flags == s2->flags.tmp_flags && */ | ||
| 206 | s1->flags.perm_flags == s2->flags.perm_flags); | ||
| 207 | } | ||
| 208 | |||
| 209 | #define READ_CACHE_DATA_INT(n, fp) \ | ||
| 210 | { \ | ||
| 211 | guint32 idata; \ | ||
| 212 | \ | ||
| 213 | if (fread(&idata, sizeof(idata), 1, fp) != 1) { \ | ||
| 214 | g_warning("Cache data is corrupted\n"); \ | ||
| 215 | fclose(fp); \ | ||
| 216 | return table; \ | ||
| 217 | } else \ | ||
| 218 | n = idata; \ | ||
| 219 | } | ||
| 220 | |||
| 221 | static GHashTable *virtual_read_search_cache(FolderItem *item) | ||
| 222 | { | ||
| 223 | GHashTable *table; | ||
| 224 | gchar *path, *file; | ||
| 225 | FILE *fp; | ||
| 226 | gchar *id; | ||
| 227 | gint count = 0; | ||
| 228 | |||
| 229 | g_return_val_if_fail(item != NULL, NULL); | ||
| 230 | |||
| 231 | path = folder_item_get_path(item); | ||
| 232 | file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL); | ||
| 233 | debug_print("reading search cache: %s\n", file); | ||
| 234 | fp = procmsg_open_data_file(file, SEARCH_CACHE_VERSION, DATA_READ, | ||
| 235 | NULL, 0); | ||
| 236 | g_free(file); | ||
| 237 | g_free(path); | ||
| 238 | if (!fp) | ||
| 239 | return NULL; | ||
| 240 | |||
| 241 | table = g_hash_table_new(sinfo_hash, sinfo_equal); | ||
| 242 | |||
| 243 | while (procmsg_read_cache_data_str(fp, &id) == 0) { | ||
| 244 | FolderItem *folder; | ||
| 245 | guint32 msgnum; | ||
| 246 | off_t size; | ||
| 247 | time_t mtime; | ||
| 248 | MsgFlags flags; | ||
| 249 | gint matched; | ||
| 250 | SearchCacheInfo *sinfo; | ||
| 251 | |||
| 252 | folder = folder_find_item_from_identifier(id); | ||
| 253 | g_free(id); | ||
| 254 | |||
| 255 | while (fread(&msgnum, sizeof(msgnum), 1, fp) == 1) { | ||
| 256 | if (msgnum == 0) | ||
| 257 | break; | ||
| 258 | |||
| 259 | READ_CACHE_DATA_INT(size, fp); | ||
| 260 | READ_CACHE_DATA_INT(mtime, fp); | ||
| 261 | READ_CACHE_DATA_INT(flags.tmp_flags, fp); | ||
| 262 | READ_CACHE_DATA_INT(flags.perm_flags, fp); | ||
| 263 | READ_CACHE_DATA_INT(matched, fp); | ||
| 264 | |||
| 265 | if (folder) { | ||
| 266 | sinfo = g_new(SearchCacheInfo, 1); | ||
| 267 | sinfo->folder = folder; | ||
| 268 | sinfo->msgnum = msgnum; | ||
| 269 | sinfo->size = size; | ||
| 270 | sinfo->mtime = mtime; | ||
| 271 | sinfo->flags = flags; | ||
| 272 | g_hash_table_insert(table, sinfo, | ||
| 273 | GINT_TO_POINTER(matched)); | ||
| 274 | ++count; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | debug_print("%d cache items read.\n", count); | ||
| 280 | |||
| 281 | fclose(fp); | ||
| 282 | return table; | ||
| 283 | } | ||
| 284 | |||
| 285 | static void virtual_write_search_cache(FILE *fp, FolderItem *item, | ||
| 286 | MsgInfo *msginfo, gint matched) | ||
| 287 | { | ||
| 288 | if (!item && !msginfo) { | ||
| 289 | WRITE_CACHE_DATA_INT(0, fp); | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | |||
| 293 | if (item) { | ||
| 294 | gchar *id; | ||
| 295 | |||
| 296 | id = folder_item_get_identifier(item); | ||
| 297 | if (id) { | ||
| 298 | WRITE_CACHE_DATA(id, fp); | ||
| 299 | g_free(id); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if (msginfo) { | ||
| 304 | WRITE_CACHE_DATA_INT(msginfo->msgnum, fp); | ||
| 305 | WRITE_CACHE_DATA_INT(msginfo->size, fp); | ||
| 306 | WRITE_CACHE_DATA_INT(msginfo->mtime, fp); | ||
| 307 | WRITE_CACHE_DATA_INT | ||
| 308 | ((msginfo->flags.tmp_flags & MSG_CACHED_FLAG_MASK), fp); | ||
| 309 | WRITE_CACHE_DATA_INT(msginfo->flags.perm_flags, fp); | ||
| 310 | WRITE_CACHE_DATA_INT(matched, fp); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | static void search_cache_free_func(gpointer key, gpointer value, gpointer data) | ||
| 315 | { | ||
| 316 | g_free(key); | ||
| 317 | } | ||
| 318 | |||
| 319 | static void virtual_search_cache_free(GHashTable *table) | ||
| 320 | { | ||
| 321 | if (table) { | ||
| 322 | g_hash_table_foreach(table, search_cache_free_func, NULL); | ||
| 323 | g_hash_table_destroy(table); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | static GSList *virtual_search_folder(VirtualSearchInfo *info, FolderItem *item) | ||
| 328 | { | ||
| 329 | GSList *match_list = NULL; | ||
| 330 | GSList *mlist; | ||
| 331 | GSList *cur; | ||
| 332 | FilterInfo fltinfo; | ||
| 333 | gint count = 1, total, ncachehit = 0; | ||
| 334 | GTimeVal tv_prev, tv_cur; | ||
| 335 | |||
| 336 | g_return_val_if_fail(info != NULL, NULL); | ||
| 337 | g_return_val_if_fail(info->rule != NULL, NULL); | ||
| 338 | g_return_val_if_fail(item != NULL, NULL); | ||
| 339 | g_return_val_if_fail(item->path != NULL, NULL); | ||
| 340 | |||
| 341 | /* prevent circular reference */ | ||
| 342 | if (item->stype == F_VIRTUAL) | ||
| 343 | return NULL; | ||
| 344 | |||
| 345 | g_get_current_time(&tv_prev); | ||
| 346 | status_print(_("Searching %s ..."), item->path); | ||
| 347 | |||
| 348 | mlist = folder_item_get_msg_list(item, TRUE); | ||
| 349 | total = g_slist_length(mlist); | ||
| 350 | |||
| 351 | memset(&fltinfo, 0, sizeof(FilterInfo)); | ||
| 352 | |||
| 353 | debug_print("start query search: %s\n", item->path); | ||
| 354 | |||
| 355 | virtual_write_search_cache(info->fp, item, NULL, 0); | ||
| 356 | |||
| 357 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 358 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 359 | GSList *hlist; | ||
| 360 | |||
| 361 | g_get_current_time(&tv_cur); | ||
| 362 | if (tv_cur.tv_sec > tv_prev.tv_sec || | ||
| 363 | tv_cur.tv_usec - tv_prev.tv_usec > | ||
| 364 | PROGRESS_UPDATE_INTERVAL * 1000) { | ||
| 365 | status_print(_("Searching %s (%d / %d)..."), | ||
| 366 | item->path, count, total); | ||
| 367 | tv_prev = tv_cur; | ||
| 368 | } | ||
| 369 | ++count; | ||
| 370 | |||
| 371 | if (info->search_cache_table) { | ||
| 372 | gint matched; | ||
| 373 | SearchCacheInfo sinfo; | ||
| 374 | |||
| 375 | sinfo.folder = item; | ||
| 376 | sinfo.msgnum = msginfo->msgnum; | ||
| 377 | sinfo.size = msginfo->size; | ||
| 378 | sinfo.mtime = msginfo->mtime; | ||
| 379 | sinfo.flags = msginfo->flags; | ||
| 380 | |||
| 381 | matched = (gint)g_hash_table_lookup | ||
| 382 | (info->search_cache_table, &sinfo); | ||
| 383 | if (matched == SCACHE_MATCHED) { | ||
| 384 | match_list = g_slist_prepend | ||
| 385 | (match_list, msginfo); | ||
| 386 | cur->data = NULL; | ||
| 387 | virtual_write_search_cache(info->fp, NULL, | ||
| 388 | msginfo, matched); | ||
| 389 | ++ncachehit; | ||
| 390 | continue; | ||
| 391 | } else if (matched == SCACHE_NOT_MATCHED) { | ||
| 392 | virtual_write_search_cache(info->fp, NULL, | ||
| 393 | msginfo, matched); | ||
| 394 | ++ncachehit; | ||
| 395 | continue; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | fltinfo.flags = msginfo->flags; | ||
| 400 | if (info->requires_full_headers) { | ||
| 401 | gchar *file; | ||
| 402 | |||
| 403 | file = procmsg_get_message_file(msginfo); | ||
| 404 | hlist = procheader_get_header_list_from_file(file); | ||
| 405 | g_free(file); | ||
| 406 | } else | ||
| 407 | hlist = procheader_get_header_list_from_msginfo | ||
| 408 | (msginfo); | ||
| 409 | if (!hlist) | ||
| 410 | continue; | ||
| 411 | |||
| 412 | if (filter_match_rule(info->rule, msginfo, hlist, &fltinfo)) { | ||
| 413 | match_list = g_slist_prepend(match_list, msginfo); | ||
| 414 | cur->data = NULL; | ||
| 415 | virtual_write_search_cache(info->fp, NULL, msginfo, | ||
| 416 | SCACHE_MATCHED); | ||
| 417 | } else { | ||
| 418 | virtual_write_search_cache(info->fp, NULL, msginfo, | ||
| 419 | SCACHE_NOT_MATCHED); | ||
| 420 | } | ||
| 421 | |||
| 422 | procheader_header_list_destroy(hlist); | ||
| 423 | } | ||
| 424 | |||
| 425 | debug_print("%d cache hits (%d total)\n", ncachehit, total); | ||
| 426 | |||
| 427 | virtual_write_search_cache(info->fp, NULL, NULL, 0); | ||
| 428 | procmsg_msg_list_free(mlist); | ||
| 429 | |||
| 430 | return g_slist_reverse(match_list); | ||
| 431 | } | ||
| 432 | |||
| 433 | static gboolean virtual_search_recursive_func(GNode *node, gpointer data) | ||
| 434 | { | ||
| 435 | VirtualSearchInfo *info = (VirtualSearchInfo *)data; | ||
| 436 | FolderItem *item; | ||
| 437 | GSList *mlist; | ||
| 438 | |||
| 439 | g_return_val_if_fail(node->data != NULL, FALSE); | ||
| 440 | |||
| 441 | item = FOLDER_ITEM(node->data); | ||
| 442 | |||
| 443 | if (!item->path) | ||
| 444 | return FALSE; | ||
| 445 | if (info->exclude_trash && item->stype == F_TRASH) | ||
| 446 | return FALSE; | ||
| 447 | |||
| 448 | mlist = virtual_search_folder(info, item); | ||
| 449 | info->mlist = g_slist_concat(info->mlist, mlist); | ||
| 450 | |||
| 451 | return FALSE; | ||
| 452 | } | ||
| 453 | |||
| 454 | static GSList *virtual_get_msg_list(Folder *folder, FolderItem *item, | ||
| 455 | gboolean use_cache) | ||
| 456 | { | ||
| 457 | GSList *mlist = NULL; | ||
| 458 | GSList *flist; | ||
| 459 | GSList *cur; | ||
| 460 | FilterRule *rule; | ||
| 461 | gchar *path; | ||
| 462 | gchar *rule_file; | ||
| 463 | gchar *cache_file; | ||
| 464 | FolderItem *target; | ||
| 465 | gint new = 0, unread = 0, total = 0; | ||
| 466 | VirtualSearchInfo info; | ||
| 467 | |||
| 468 | g_return_val_if_fail(item != NULL, NULL); | ||
| 469 | g_return_val_if_fail(item->stype == F_VIRTUAL, NULL); | ||
| 470 | |||
| 471 | path = folder_item_get_path(item); | ||
| 472 | rule_file = g_strconcat(path, G_DIR_SEPARATOR_S, "filter.xml", NULL); | ||
| 473 | flist = filter_read_file(rule_file); | ||
| 474 | g_free(rule_file); | ||
| 475 | |||
| 476 | g_free(path); | ||
| 477 | |||
| 478 | if (!flist) { | ||
| 479 | g_warning("filter rule not found\n"); | ||
| 480 | return NULL; | ||
| 481 | } | ||
| 482 | |||
| 483 | rule = (FilterRule *)flist->data; | ||
| 484 | target = folder_find_item_from_identifier(rule->target_folder); | ||
| 485 | |||
| 486 | if (!target || target == item) { | ||
| 487 | g_warning("invalid target folder\n"); | ||
| 488 | goto finish; | ||
| 489 | } | ||
| 490 | |||
| 491 | info.rule = rule; | ||
| 492 | info.mlist = NULL; | ||
| 493 | if (use_cache) | ||
| 494 | info.search_cache_table = virtual_read_search_cache(item); | ||
| 495 | else | ||
| 496 | info.search_cache_table = NULL; | ||
| 497 | |||
| 498 | path = folder_item_get_path(item); | ||
| 499 | cache_file = g_strconcat(path, G_DIR_SEPARATOR_S, SEARCH_CACHE, NULL); | ||
| 500 | info.fp = procmsg_open_data_file(cache_file, SEARCH_CACHE_VERSION, | ||
| 501 | DATA_WRITE, NULL, 0); | ||
| 502 | g_free(cache_file); | ||
| 503 | g_free(path); | ||
| 504 | if (!info.fp) | ||
| 505 | goto finish; | ||
| 506 | |||
| 507 | info.requires_full_headers = | ||
| 508 | filter_rule_requires_full_headers(rule); | ||
| 509 | |||
| 510 | if (rule->recursive) { | ||
| 511 | if (target->stype == F_TRASH) | ||
| 512 | info.exclude_trash = FALSE; | ||
| 513 | else | ||
| 514 | info.exclude_trash = TRUE; | ||
| 515 | } else | ||
| 516 | info.exclude_trash = FALSE; | ||
| 517 | |||
| 518 | if (rule->recursive) { | ||
| 519 | g_node_traverse(target->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, | ||
| 520 | virtual_search_recursive_func, &info); | ||
| 521 | mlist = info.mlist; | ||
| 522 | } else | ||
| 523 | mlist = virtual_search_folder(&info, target); | ||
| 524 | |||
| 525 | fclose(info.fp); | ||
| 526 | virtual_search_cache_free(info.search_cache_table); | ||
| 527 | |||
| 528 | for (cur = mlist; cur != NULL; cur = cur->next) { | ||
| 529 | MsgInfo *msginfo = (MsgInfo *)cur->data; | ||
| 530 | |||
| 531 | if (MSG_IS_NEW(msginfo->flags)) | ||
| 532 | ++new; | ||
| 533 | if (MSG_IS_UNREAD(msginfo->flags)) | ||
| 534 | ++unread; | ||
| 535 | ++total; | ||
| 536 | } | ||
| 537 | |||
| 538 | item->new = new; | ||
| 539 | item->unread = unread; | ||
| 540 | item->total = total; | ||
| 541 | item->updated = TRUE; | ||
| 542 | |||
| 543 | finish: | ||
| 544 | filter_rule_list_free(flist); | ||
| 545 | return mlist; | ||
| 546 | } | ||
| 547 | |||
| 548 | static gchar *virtual_fetch_msg(Folder *folder, FolderItem *item, gint num) | ||
| 549 | { | ||
| 550 | return NULL; | ||
| 551 | } | ||
| 552 | |||
| 553 | static MsgInfo *virtual_get_msginfo(Folder *folder, FolderItem *item, gint num) | ||
| 554 | { | ||
| 555 | return NULL; | ||
| 556 | } | ||
| 557 | |||
| 558 | static gint virtual_close(Folder *folder, FolderItem *item) | ||
| 559 | { | ||
| 560 | return 0; | ||
| 561 | } | ||
| 562 | |||
| 563 | static gint virtual_scan_folder(Folder *folder, FolderItem *item) | ||
| 564 | { | ||
| 565 | return 0; | ||
| 566 | } | ||
| 567 | |||
| 568 | static gint virtual_rename_folder(Folder *folder, FolderItem *item, | ||
| 569 | const gchar *name) | ||
| 570 | { | ||
| 571 | g_return_val_if_fail(item != NULL, -1); | ||
| 572 | g_return_val_if_fail(item->stype == F_VIRTUAL, -1); | ||
| 573 | |||
| 574 | return mh_get_class()->rename_folder(folder, item, name); | ||
| 575 | } | ||
| 576 | |||
| 577 | static gint virtual_move_folder(Folder *folder, FolderItem *item, | ||
| 578 | FolderItem *new_parent) | ||
| 579 | { | ||
| 580 | g_return_val_if_fail(item != NULL, -1); | ||
| 581 | g_return_val_if_fail(item->stype == F_VIRTUAL, -1); | ||
| 582 | |||
| 583 | return mh_get_class()->move_folder(folder, item, new_parent); | ||
| 584 | } | ||
| 585 | |||
| 586 | static gint virtual_remove_folder(Folder *folder, FolderItem *item) | ||
| 587 | { | ||
| 588 | gchar *path; | ||
| 589 | |||
| 590 | g_return_val_if_fail(item != NULL, -1); | ||
| 591 | g_return_val_if_fail(item->stype == F_VIRTUAL, -1); | ||
| 592 | |||
| 593 | path = folder_item_get_path(item); | ||
| 594 | if (remove_dir_recursive(path) < 0) { | ||
| 595 | g_warning("can't remove directory '%s'\n", path); | ||
| 596 | g_free(path); | ||
| 597 | return -1; | ||
| 598 | } | ||
| 599 | |||
| 600 | g_free(path); | ||
| 601 | folder_item_remove(item); | ||
| 602 | return 0; | ||
| 603 | } | ||
diff --git a/libsylph/virtual.h b/libsylph/virtual.h new file mode 100644 index 0000000..69647ee --- /dev/null +++ b/libsylph/virtual.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2007 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __VIRTUAL_H__ | ||
| 21 | #define __VIRTUAL_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | |||
| 25 | #include "folder.h" | ||
| 26 | |||
| 27 | typedef struct _VirtualFolder VirtualFolder; | ||
| 28 | |||
| 29 | #define VIRTUAL_FOLDER(obj) ((VirtualFolder *)obj) | ||
| 30 | |||
| 31 | struct _VirtualFolder | ||
| 32 | { | ||
| 33 | LocalFolder lfolder; | ||
| 34 | }; | ||
| 35 | |||
| 36 | FolderClass *virtual_get_class (void); | ||
| 37 | |||
| 38 | #endif /* __VIRTUAL_H__ */ | ||
diff --git a/libsylph/xml.c b/libsylph/xml.c new file mode 100644 index 0000000..e641e84 --- /dev/null +++ b/libsylph/xml.c | |||
| @@ -0,0 +1,688 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <glib.h> | ||
| 21 | #include <stdio.h> | ||
| 22 | #include <string.h> | ||
| 23 | #include <ctype.h> | ||
| 24 | |||
| 25 | #include "xml.h" | ||
| 26 | #include "utils.h" | ||
| 27 | #include "codeconv.h" | ||
| 28 | |||
| 29 | #define SPARSE_MEMORY | ||
| 30 | /* if this is defined all attr.names and tag.names are stored | ||
| 31 | * in a hash table */ | ||
| 32 | #if defined(SPARSE_MEMORY) | ||
| 33 | #include "stringtable.h" | ||
| 34 | |||
| 35 | static StringTable *xml_string_table; | ||
| 36 | |||
| 37 | static void xml_string_table_create(void) | ||
| 38 | { | ||
| 39 | if (xml_string_table == NULL) | ||
| 40 | xml_string_table = string_table_new(); | ||
| 41 | } | ||
| 42 | #define XML_STRING_ADD(str) \ | ||
| 43 | string_table_insert_string(xml_string_table, (str)) | ||
| 44 | #define XML_STRING_FREE(str) \ | ||
| 45 | string_table_free_string(xml_string_table, (str)) | ||
| 46 | |||
| 47 | #define XML_STRING_TABLE_CREATE() \ | ||
| 48 | xml_string_table_create() | ||
| 49 | |||
| 50 | #else /* !SPARSE_MEMORY */ | ||
| 51 | |||
| 52 | #define XML_STRING_ADD(str) \ | ||
| 53 | g_strdup(str) | ||
| 54 | #define XML_STRING_FREE(str) \ | ||
| 55 | g_free(str) | ||
| 56 | |||
| 57 | #define XML_STRING_TABLE_CREATE() | ||
| 58 | |||
| 59 | #endif /* SPARSE_MEMORY */ | ||
| 60 | |||
| 61 | static void xml_free_tag (XMLTag *tag); | ||
| 62 | static gint xml_get_parenthesis (XMLFile *file, | ||
| 63 | gchar *buf, | ||
| 64 | gint len); | ||
| 65 | |||
| 66 | XMLFile *xml_open_file(const gchar *path) | ||
| 67 | { | ||
| 68 | XMLFile *newfile; | ||
| 69 | |||
| 70 | g_return_val_if_fail(path != NULL, NULL); | ||
| 71 | |||
| 72 | XML_STRING_TABLE_CREATE(); | ||
| 73 | |||
| 74 | newfile = g_new(XMLFile, 1); | ||
| 75 | |||
| 76 | newfile->fp = g_fopen(path, "rb"); | ||
| 77 | if (!newfile->fp) { | ||
| 78 | g_free(newfile); | ||
| 79 | return NULL; | ||
| 80 | } | ||
| 81 | |||
| 82 | newfile->buf = g_string_new(NULL); | ||
| 83 | newfile->bufp = newfile->buf->str; | ||
| 84 | |||
| 85 | newfile->dtd = NULL; | ||
| 86 | newfile->encoding = NULL; | ||
| 87 | newfile->tag_stack = NULL; | ||
| 88 | newfile->level = 0; | ||
| 89 | newfile->is_empty_element = FALSE; | ||
| 90 | |||
| 91 | return newfile; | ||
| 92 | } | ||
| 93 | |||
| 94 | void xml_close_file(XMLFile *file) | ||
| 95 | { | ||
| 96 | g_return_if_fail(file != NULL); | ||
| 97 | |||
| 98 | if (file->fp) fclose(file->fp); | ||
| 99 | |||
| 100 | g_string_free(file->buf, TRUE); | ||
| 101 | |||
| 102 | g_free(file->dtd); | ||
| 103 | g_free(file->encoding); | ||
| 104 | |||
| 105 | while (file->tag_stack != NULL) | ||
| 106 | xml_pop_tag(file); | ||
| 107 | |||
| 108 | g_free(file); | ||
| 109 | } | ||
| 110 | |||
| 111 | static GNode *xml_build_tree(XMLFile *file, GNode *parent, guint level) | ||
| 112 | { | ||
| 113 | GNode *node = NULL; | ||
| 114 | XMLNode *xmlnode; | ||
| 115 | XMLTag *tag; | ||
| 116 | |||
| 117 | while (xml_parse_next_tag(file) == 0) { | ||
| 118 | if (file->level < level) break; | ||
| 119 | if (file->level == level) { | ||
| 120 | g_warning("xml_build_tree(): Parse error\n"); | ||
| 121 | break; | ||
| 122 | } | ||
| 123 | |||
| 124 | tag = xml_get_current_tag(file); | ||
| 125 | if (!tag) break; | ||
| 126 | xmlnode = xml_node_new(xml_copy_tag(tag), NULL); | ||
| 127 | xmlnode->element = xml_get_element(file); | ||
| 128 | if (!parent) | ||
| 129 | node = g_node_new(xmlnode); | ||
| 130 | else | ||
| 131 | node = g_node_append_data(parent, xmlnode); | ||
| 132 | |||
| 133 | xml_build_tree(file, node, file->level); | ||
| 134 | if (file->level == 0) break; | ||
| 135 | } | ||
| 136 | |||
| 137 | return node; | ||
| 138 | } | ||
| 139 | |||
| 140 | GNode *xml_parse_file(const gchar *path) | ||
| 141 | { | ||
| 142 | XMLFile *file; | ||
| 143 | GNode *node; | ||
| 144 | |||
| 145 | file = xml_open_file(path); | ||
| 146 | g_return_val_if_fail(file != NULL, NULL); | ||
| 147 | |||
| 148 | xml_get_dtd(file); | ||
| 149 | |||
| 150 | node = xml_build_tree(file, NULL, file->level); | ||
| 151 | |||
| 152 | xml_close_file(file); | ||
| 153 | |||
| 154 | #if defined(SPARSE_MEMORY) | ||
| 155 | if (get_debug_mode()) | ||
| 156 | string_table_get_stats(xml_string_table); | ||
| 157 | #endif | ||
| 158 | |||
| 159 | return node; | ||
| 160 | } | ||
| 161 | |||
| 162 | gint xml_get_dtd(XMLFile *file) | ||
| 163 | { | ||
| 164 | gchar buf[XMLBUFSIZE]; | ||
| 165 | gchar *bufp = buf; | ||
| 166 | |||
| 167 | if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) return -1; | ||
| 168 | |||
| 169 | if ((*bufp++ == '?') && | ||
| 170 | (bufp = strcasestr(bufp, "xml")) && | ||
| 171 | (bufp = strcasestr(bufp + 3, "version")) && | ||
| 172 | (bufp = strchr(bufp + 7, '?'))) { | ||
| 173 | file->dtd = g_strdup(buf); | ||
| 174 | if ((bufp = strcasestr(buf, "encoding=\""))) { | ||
| 175 | bufp += 9; | ||
| 176 | extract_quote(bufp, '"'); | ||
| 177 | file->encoding = g_strdup(bufp); | ||
| 178 | } else | ||
| 179 | file->encoding = g_strdup(CS_INTERNAL); | ||
| 180 | } else { | ||
| 181 | g_warning("Can't get xml dtd\n"); | ||
| 182 | return -1; | ||
| 183 | } | ||
| 184 | |||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | gint xml_parse_next_tag(XMLFile *file) | ||
| 189 | { | ||
| 190 | gchar buf[XMLBUFSIZE]; | ||
| 191 | gchar *bufp = buf; | ||
| 192 | gchar *tag_str; | ||
| 193 | XMLTag *tag; | ||
| 194 | gint len; | ||
| 195 | |||
| 196 | if (file->is_empty_element == TRUE) { | ||
| 197 | file->is_empty_element = FALSE; | ||
| 198 | xml_pop_tag(file); | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | if (xml_get_parenthesis(file, buf, sizeof(buf)) < 0) { | ||
| 203 | g_warning("xml_parse_next_tag(): Can't parse next tag\n"); | ||
| 204 | return -1; | ||
| 205 | } | ||
| 206 | |||
| 207 | /* end-tag */ | ||
| 208 | if (buf[0] == '/') { | ||
| 209 | if (strcmp(xml_get_current_tag(file)->tag, buf + 1) != 0) { | ||
| 210 | g_warning("xml_parse_next_tag(): Tag name mismatch: %s\n", buf); | ||
| 211 | return -1; | ||
| 212 | } | ||
| 213 | xml_pop_tag(file); | ||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | |||
| 217 | tag = xml_tag_new(NULL); | ||
| 218 | xml_push_tag(file, tag); | ||
| 219 | |||
| 220 | len = strlen(buf); | ||
| 221 | if (len > 0 && buf[len - 1] == '/') { | ||
| 222 | file->is_empty_element = TRUE; | ||
| 223 | buf[len - 1] = '\0'; | ||
| 224 | g_strchomp(buf); | ||
| 225 | } | ||
| 226 | if (strlen(buf) == 0) { | ||
| 227 | g_warning("xml_parse_next_tag(): Tag name is empty\n"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | while (*bufp != '\0' && !g_ascii_isspace(*bufp)) bufp++; | ||
| 232 | if (*bufp == '\0') { | ||
| 233 | tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); | ||
| 234 | if (tag_str) { | ||
| 235 | tag->tag = XML_STRING_ADD(tag_str); | ||
| 236 | g_free(tag_str); | ||
| 237 | } else | ||
| 238 | tag->tag = XML_STRING_ADD(buf); | ||
| 239 | return 0; | ||
| 240 | } else { | ||
| 241 | *bufp++ = '\0'; | ||
| 242 | tag_str = conv_codeset_strdup(buf, file->encoding, CS_INTERNAL); | ||
| 243 | if (tag_str) { | ||
| 244 | tag->tag = XML_STRING_ADD(tag_str); | ||
| 245 | g_free(tag_str); | ||
| 246 | } else | ||
| 247 | tag->tag = XML_STRING_ADD(buf); | ||
| 248 | } | ||
| 249 | |||
| 250 | /* parse attributes ( name=value ) */ | ||
| 251 | while (*bufp) { | ||
| 252 | XMLAttr *attr; | ||
| 253 | gchar *attr_name; | ||
| 254 | gchar *attr_value; | ||
| 255 | gchar *utf8_attr_name; | ||
| 256 | gchar *utf8_attr_value; | ||
| 257 | gchar *p; | ||
| 258 | gchar quote; | ||
| 259 | |||
| 260 | while (g_ascii_isspace(*bufp)) bufp++; | ||
| 261 | attr_name = bufp; | ||
| 262 | if ((p = strchr(attr_name, '=')) == NULL) { | ||
| 263 | g_warning("xml_parse_next_tag(): Syntax error in tag\n"); | ||
| 264 | return -1; | ||
| 265 | } | ||
| 266 | bufp = p; | ||
| 267 | *bufp++ = '\0'; | ||
| 268 | while (g_ascii_isspace(*bufp)) bufp++; | ||
| 269 | |||
| 270 | if (*bufp != '"' && *bufp != '\'') { | ||
| 271 | g_warning("xml_parse_next_tag(): Syntax error in tag\n"); | ||
| 272 | return -1; | ||
| 273 | } | ||
| 274 | quote = *bufp; | ||
| 275 | bufp++; | ||
| 276 | attr_value = bufp; | ||
| 277 | if ((p = strchr(attr_value, quote)) == NULL) { | ||
| 278 | g_warning("xml_parse_next_tag(): Syntax error in tag\n"); | ||
| 279 | return -1; | ||
| 280 | } | ||
| 281 | bufp = p; | ||
| 282 | *bufp++ = '\0'; | ||
| 283 | |||
| 284 | g_strchomp(attr_name); | ||
| 285 | xml_unescape_str(attr_value); | ||
| 286 | utf8_attr_name = conv_codeset_strdup | ||
| 287 | (attr_name, file->encoding, CS_INTERNAL); | ||
| 288 | utf8_attr_value = conv_codeset_strdup | ||
| 289 | (attr_value, file->encoding, CS_INTERNAL); | ||
| 290 | if (!utf8_attr_name) | ||
| 291 | utf8_attr_name = g_strdup(attr_name); | ||
| 292 | if (!utf8_attr_value) | ||
| 293 | utf8_attr_value = g_strdup(attr_value); | ||
| 294 | |||
| 295 | attr = xml_attr_new(utf8_attr_name, utf8_attr_value); | ||
| 296 | xml_tag_add_attr(tag, attr); | ||
| 297 | |||
| 298 | g_free(utf8_attr_value); | ||
| 299 | g_free(utf8_attr_name); | ||
| 300 | } | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | void xml_push_tag(XMLFile *file, XMLTag *tag) | ||
| 306 | { | ||
| 307 | g_return_if_fail(tag != NULL); | ||
| 308 | |||
| 309 | file->tag_stack = g_list_prepend(file->tag_stack, tag); | ||
| 310 | file->level++; | ||
| 311 | } | ||
| 312 | |||
| 313 | void xml_pop_tag(XMLFile *file) | ||
| 314 | { | ||
| 315 | XMLTag *tag; | ||
| 316 | |||
| 317 | if (!file->tag_stack) return; | ||
| 318 | |||
| 319 | tag = (XMLTag *)file->tag_stack->data; | ||
| 320 | |||
| 321 | xml_free_tag(tag); | ||
| 322 | file->tag_stack = g_list_remove(file->tag_stack, tag); | ||
| 323 | file->level--; | ||
| 324 | } | ||
| 325 | |||
| 326 | XMLTag *xml_get_current_tag(XMLFile *file) | ||
| 327 | { | ||
| 328 | if (file->tag_stack) | ||
| 329 | return (XMLTag *)file->tag_stack->data; | ||
| 330 | else | ||
| 331 | return NULL; | ||
| 332 | } | ||
| 333 | |||
| 334 | GList *xml_get_current_tag_attr(XMLFile *file) | ||
| 335 | { | ||
| 336 | XMLTag *tag; | ||
| 337 | |||
| 338 | tag = xml_get_current_tag(file); | ||
| 339 | if (!tag) return NULL; | ||
| 340 | |||
| 341 | return tag->attr; | ||
| 342 | } | ||
| 343 | |||
| 344 | gchar *xml_get_element(XMLFile *file) | ||
| 345 | { | ||
| 346 | gchar *str; | ||
| 347 | gchar *new_str; | ||
| 348 | gchar *end; | ||
| 349 | |||
| 350 | while ((end = strchr(file->bufp, '<')) == NULL) | ||
| 351 | if (xml_read_line(file) < 0) return NULL; | ||
| 352 | |||
| 353 | if (end == file->bufp) | ||
| 354 | return NULL; | ||
| 355 | |||
| 356 | str = g_strndup(file->bufp, end - file->bufp); | ||
| 357 | /* this is not XML1.0 strict */ | ||
| 358 | g_strstrip(str); | ||
| 359 | xml_unescape_str(str); | ||
| 360 | |||
| 361 | file->bufp = end; | ||
| 362 | xml_truncate_buf(file); | ||
| 363 | |||
| 364 | if (str[0] == '\0') { | ||
| 365 | g_free(str); | ||
| 366 | return NULL; | ||
| 367 | } | ||
| 368 | |||
| 369 | new_str = conv_codeset_strdup(str, file->encoding, CS_INTERNAL); | ||
| 370 | if (!new_str) | ||
| 371 | new_str = g_strdup(str); | ||
| 372 | g_free(str); | ||
| 373 | |||
| 374 | return new_str; | ||
| 375 | } | ||
| 376 | |||
| 377 | gint xml_read_line(XMLFile *file) | ||
| 378 | { | ||
| 379 | gchar buf[XMLBUFSIZE]; | ||
| 380 | gint index; | ||
| 381 | |||
| 382 | if (fgets(buf, sizeof(buf), file->fp) == NULL) | ||
| 383 | return -1; | ||
| 384 | |||
| 385 | index = file->bufp - file->buf->str; | ||
| 386 | |||
| 387 | g_string_append(file->buf, buf); | ||
| 388 | |||
| 389 | file->bufp = file->buf->str + index; | ||
| 390 | |||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | void xml_truncate_buf(XMLFile *file) | ||
| 395 | { | ||
| 396 | gint len; | ||
| 397 | |||
| 398 | len = file->bufp - file->buf->str; | ||
| 399 | if (len > 0) { | ||
| 400 | g_string_erase(file->buf, 0, len); | ||
| 401 | file->bufp = file->buf->str; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | gboolean xml_compare_tag(XMLFile *file, const gchar *name) | ||
| 406 | { | ||
| 407 | XMLTag *tag; | ||
| 408 | |||
| 409 | tag = xml_get_current_tag(file); | ||
| 410 | |||
| 411 | if (tag && strcmp(tag->tag, name) == 0) | ||
| 412 | return TRUE; | ||
| 413 | else | ||
| 414 | return FALSE; | ||
| 415 | } | ||
| 416 | |||
| 417 | XMLNode *xml_node_new(XMLTag *tag, const gchar *text) | ||
| 418 | { | ||
| 419 | XMLNode *node; | ||
| 420 | |||
| 421 | node = g_new(XMLNode, 1); | ||
| 422 | node->tag = tag; | ||
| 423 | node->element = g_strdup(text); | ||
| 424 | |||
| 425 | return node; | ||
| 426 | } | ||
| 427 | |||
| 428 | XMLTag *xml_tag_new(const gchar *tag) | ||
| 429 | { | ||
| 430 | XMLTag *new_tag; | ||
| 431 | |||
| 432 | new_tag = g_new(XMLTag, 1); | ||
| 433 | if (tag) | ||
| 434 | new_tag->tag = XML_STRING_ADD(tag); | ||
| 435 | else | ||
| 436 | new_tag->tag = NULL; | ||
| 437 | new_tag->attr = NULL; | ||
| 438 | |||
| 439 | return new_tag; | ||
| 440 | } | ||
| 441 | |||
| 442 | XMLAttr *xml_attr_new(const gchar *name, const gchar *value) | ||
| 443 | { | ||
| 444 | XMLAttr *new_attr; | ||
| 445 | |||
| 446 | new_attr = g_new(XMLAttr, 1); | ||
| 447 | new_attr->name = XML_STRING_ADD(name); | ||
| 448 | new_attr->value = g_strdup(value); | ||
| 449 | |||
| 450 | return new_attr; | ||
| 451 | } | ||
| 452 | |||
| 453 | void xml_tag_add_attr(XMLTag *tag, XMLAttr *attr) | ||
| 454 | { | ||
| 455 | tag->attr = g_list_append(tag->attr, attr); | ||
| 456 | } | ||
| 457 | |||
| 458 | XMLTag *xml_copy_tag(XMLTag *tag) | ||
| 459 | { | ||
| 460 | XMLTag *new_tag; | ||
| 461 | XMLAttr *attr; | ||
| 462 | GList *list; | ||
| 463 | |||
| 464 | new_tag = xml_tag_new(tag->tag); | ||
| 465 | for (list = tag->attr; list != NULL; list = list->next) { | ||
| 466 | attr = xml_copy_attr((XMLAttr *)list->data); | ||
| 467 | xml_tag_add_attr(new_tag, attr); | ||
| 468 | } | ||
| 469 | |||
| 470 | return new_tag; | ||
| 471 | } | ||
| 472 | |||
| 473 | XMLAttr *xml_copy_attr(XMLAttr *attr) | ||
| 474 | { | ||
| 475 | return xml_attr_new(attr->name, attr->value); | ||
| 476 | } | ||
| 477 | |||
| 478 | gint xml_unescape_str(gchar *str) | ||
| 479 | { | ||
| 480 | gchar *start; | ||
| 481 | gchar *end; | ||
| 482 | gchar *p = str; | ||
| 483 | gchar ch; | ||
| 484 | gint len; | ||
| 485 | |||
| 486 | while ((start = strchr(p, '&')) != NULL) { | ||
| 487 | if ((end = strchr(start + 1, ';')) == NULL) { | ||
| 488 | g_warning("Unescaped `&' appeared\n"); | ||
| 489 | p = start + 1; | ||
| 490 | continue; | ||
| 491 | } | ||
| 492 | len = end - start + 1; | ||
| 493 | if (len < 3) { | ||
| 494 | p = end + 1; | ||
| 495 | continue; | ||
| 496 | } | ||
| 497 | |||
| 498 | if (!strncmp(start, "<", 4)) | ||
| 499 | ch = '<'; | ||
| 500 | else if (!strncmp(start, ">", 4)) | ||
| 501 | ch = '>'; | ||
| 502 | else if (!strncmp(start, "&", 5)) | ||
| 503 | ch = '&'; | ||
| 504 | else if (!strncmp(start, "'", 6)) | ||
| 505 | ch = '\''; | ||
| 506 | else if (!strncmp(start, """, 6)) | ||
| 507 | ch = '\"'; | ||
| 508 | else { | ||
| 509 | p = end + 1; | ||
| 510 | continue; | ||
| 511 | } | ||
| 512 | |||
| 513 | *start = ch; | ||
| 514 | memmove(start + 1, end + 1, strlen(end + 1) + 1); | ||
| 515 | p = start + 1; | ||
| 516 | } | ||
| 517 | |||
| 518 | return 0; | ||
| 519 | } | ||
| 520 | |||
| 521 | gchar *xml_escape_str(const gchar *str) | ||
| 522 | { | ||
| 523 | GString *estr; | ||
| 524 | const gchar *p; | ||
| 525 | |||
| 526 | if (!str) | ||
| 527 | return NULL; | ||
| 528 | |||
| 529 | estr = g_string_sized_new(strlen(str)); | ||
| 530 | for (p = str; *p != '\0'; p++) { | ||
| 531 | switch (*p) { | ||
| 532 | case '<': | ||
| 533 | g_string_append(estr, "<"); | ||
| 534 | break; | ||
| 535 | case '>': | ||
| 536 | g_string_append(estr, ">"); | ||
| 537 | break; | ||
| 538 | case '&': | ||
| 539 | g_string_append(estr, "&"); | ||
| 540 | break; | ||
| 541 | case '\'': | ||
| 542 | g_string_append(estr, "'"); | ||
| 543 | break; | ||
| 544 | case '\"': | ||
| 545 | g_string_append(estr, """); | ||
| 546 | break; | ||
| 547 | default: | ||
| 548 | g_string_append_c(estr, *p); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | return g_string_free(estr, FALSE); | ||
| 554 | } | ||
| 555 | |||
| 556 | gint xml_file_put_escape_str(FILE *fp, const gchar *str) | ||
| 557 | { | ||
| 558 | const gchar *p; | ||
| 559 | |||
| 560 | g_return_val_if_fail(fp != NULL, -1); | ||
| 561 | |||
| 562 | if (!str) return 0; | ||
| 563 | |||
| 564 | for (p = str; *p != '\0'; p++) { | ||
| 565 | switch (*p) { | ||
| 566 | case '<': | ||
| 567 | fputs("<", fp); | ||
| 568 | break; | ||
| 569 | case '>': | ||
| 570 | fputs(">", fp); | ||
| 571 | break; | ||
| 572 | case '&': | ||
| 573 | fputs("&", fp); | ||
| 574 | break; | ||
| 575 | case '\'': | ||
| 576 | fputs("'", fp); | ||
| 577 | break; | ||
| 578 | case '\"': | ||
| 579 | fputs(""", fp); | ||
| 580 | break; | ||
| 581 | default: | ||
| 582 | fputc(*p, fp); | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | return 0; | ||
| 587 | } | ||
| 588 | |||
| 589 | gint xml_file_put_xml_decl(FILE *fp) | ||
| 590 | { | ||
| 591 | g_return_val_if_fail(fp != NULL, -1); | ||
| 592 | |||
| 593 | fprintf(fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", CS_INTERNAL); | ||
| 594 | return 0; | ||
| 595 | } | ||
| 596 | |||
| 597 | gint xml_file_put_node(FILE *fp, XMLNode *node) | ||
| 598 | { | ||
| 599 | GList *cur; | ||
| 600 | |||
| 601 | g_return_val_if_fail(fp != NULL, -1); | ||
| 602 | g_return_val_if_fail(node != NULL, -1); | ||
| 603 | |||
| 604 | fprintf(fp, "<%s", node->tag->tag); | ||
| 605 | |||
| 606 | for (cur = node->tag->attr; cur != NULL; cur = cur->next) { | ||
| 607 | XMLAttr *attr = (XMLAttr *)cur->data; | ||
| 608 | fprintf(fp, " %s=\"", attr->name); | ||
| 609 | xml_file_put_escape_str(fp, attr->value); | ||
| 610 | fputs("\"", fp); | ||
| 611 | } | ||
| 612 | |||
| 613 | if (node->element) { | ||
| 614 | fputs(">", fp); | ||
| 615 | xml_file_put_escape_str(fp, node->element); | ||
| 616 | fprintf(fp, "</%s>\n", node->tag->tag); | ||
| 617 | } else { | ||
| 618 | fputs(" />\n", fp); | ||
| 619 | } | ||
| 620 | |||
| 621 | return 0; | ||
| 622 | } | ||
| 623 | |||
| 624 | void xml_free_node(XMLNode *node) | ||
| 625 | { | ||
| 626 | if (!node) return; | ||
| 627 | |||
| 628 | xml_free_tag(node->tag); | ||
| 629 | g_free(node->element); | ||
| 630 | g_free(node); | ||
| 631 | } | ||
| 632 | |||
| 633 | static gboolean xml_free_func(GNode *node, gpointer data) | ||
| 634 | { | ||
| 635 | XMLNode *xmlnode = node->data; | ||
| 636 | |||
| 637 | xml_free_node(xmlnode); | ||
| 638 | return FALSE; | ||
| 639 | } | ||
| 640 | |||
| 641 | void xml_free_tree(GNode *node) | ||
| 642 | { | ||
| 643 | g_return_if_fail(node != NULL); | ||
| 644 | |||
| 645 | g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_free_func, | ||
| 646 | NULL); | ||
| 647 | |||
| 648 | g_node_destroy(node); | ||
| 649 | } | ||
| 650 | |||
| 651 | static void xml_free_tag(XMLTag *tag) | ||
| 652 | { | ||
| 653 | if (!tag) return; | ||
| 654 | |||
| 655 | XML_STRING_FREE(tag->tag); | ||
| 656 | while (tag->attr != NULL) { | ||
| 657 | XMLAttr *attr = (XMLAttr *)tag->attr->data; | ||
| 658 | XML_STRING_FREE(attr->name); | ||
| 659 | g_free(attr->value); | ||
| 660 | g_free(attr); | ||
| 661 | tag->attr = g_list_remove(tag->attr, tag->attr->data); | ||
| 662 | } | ||
| 663 | g_free(tag); | ||
| 664 | } | ||
| 665 | |||
| 666 | static gint xml_get_parenthesis(XMLFile *file, gchar *buf, gint len) | ||
| 667 | { | ||
| 668 | gchar *start; | ||
| 669 | gchar *end; | ||
| 670 | |||
| 671 | buf[0] = '\0'; | ||
| 672 | |||
| 673 | while ((start = strchr(file->bufp, '<')) == NULL) | ||
| 674 | if (xml_read_line(file) < 0) return -1; | ||
| 675 | |||
| 676 | start++; | ||
| 677 | file->bufp = start; | ||
| 678 | |||
| 679 | while ((end = strchr(file->bufp, '>')) == NULL) | ||
| 680 | if (xml_read_line(file) < 0) return -1; | ||
| 681 | |||
| 682 | strncpy2(buf, file->bufp, MIN(end - file->bufp + 1, len)); | ||
| 683 | g_strstrip(buf); | ||
| 684 | file->bufp = end + 1; | ||
| 685 | xml_truncate_buf(file); | ||
| 686 | |||
| 687 | return 0; | ||
| 688 | } | ||
diff --git a/libsylph/xml.h b/libsylph/xml.h new file mode 100644 index 0000000..766281b --- /dev/null +++ b/libsylph/xml.h | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | /* | ||
| 2 | * LibSylph -- E-Mail client library | ||
| 3 | * Copyright (C) 1999-2005 Hiroyuki Yamamoto | ||
| 4 | * | ||
| 5 | * This library is free software; you can redistribute it and/or | ||
| 6 | * modify it under the terms of the GNU Lesser General Public | ||
| 7 | * License as published by the Free Software Foundation; either | ||
| 8 | * version 2.1 of the License, or (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This library is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * Lesser General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU Lesser General Public | ||
| 16 | * License along with this library; if not, write to the Free Software | ||
| 17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #ifndef __XML_H__ | ||
| 21 | #define __XML_H__ | ||
| 22 | |||
| 23 | #include <glib.h> | ||
| 24 | #include <stdio.h> | ||
| 25 | |||
| 26 | #define XMLBUFSIZE 8192 | ||
| 27 | |||
| 28 | typedef struct _XMLAttr XMLAttr; | ||
| 29 | typedef struct _XMLTag XMLTag; | ||
| 30 | typedef struct _XMLNode XMLNode; | ||
| 31 | typedef struct _XMLFile XMLFile; | ||
| 32 | |||
| 33 | struct _XMLAttr | ||
| 34 | { | ||
| 35 | gchar *name; | ||
| 36 | gchar *value; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct _XMLTag | ||
| 40 | { | ||
| 41 | gchar *tag; | ||
| 42 | GList *attr; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct _XMLNode | ||
| 46 | { | ||
| 47 | XMLTag *tag; | ||
| 48 | gchar *element; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct _XMLFile | ||
| 52 | { | ||
| 53 | FILE *fp; | ||
| 54 | |||
| 55 | GString *buf; | ||
| 56 | gchar *bufp; | ||
| 57 | |||
| 58 | gchar *dtd; | ||
| 59 | gchar *encoding; | ||
| 60 | |||
| 61 | GList *tag_stack; | ||
| 62 | guint level; | ||
| 63 | |||
| 64 | gboolean is_empty_element; | ||
| 65 | }; | ||
| 66 | |||
| 67 | XMLFile *xml_open_file (const gchar *path); | ||
| 68 | void xml_close_file (XMLFile *file); | ||
| 69 | GNode *xml_parse_file (const gchar *path); | ||
| 70 | |||
| 71 | gint xml_get_dtd (XMLFile *file); | ||
| 72 | gint xml_parse_next_tag (XMLFile *file); | ||
| 73 | void xml_push_tag (XMLFile *file, | ||
| 74 | XMLTag *tag); | ||
| 75 | void xml_pop_tag (XMLFile *file); | ||
| 76 | |||
| 77 | XMLTag *xml_get_current_tag (XMLFile *file); | ||
| 78 | GList *xml_get_current_tag_attr(XMLFile *file); | ||
| 79 | gchar *xml_get_element (XMLFile *file); | ||
| 80 | |||
| 81 | gint xml_read_line (XMLFile *file); | ||
| 82 | void xml_truncate_buf (XMLFile *file); | ||
| 83 | gboolean xml_compare_tag (XMLFile *file, | ||
| 84 | const gchar *name); | ||
| 85 | |||
| 86 | XMLNode *xml_node_new (XMLTag *tag, | ||
| 87 | const gchar *text); | ||
| 88 | XMLTag *xml_tag_new (const gchar *tag); | ||
| 89 | XMLAttr *xml_attr_new (const gchar *name, | ||
| 90 | const gchar *value); | ||
| 91 | void xml_tag_add_attr (XMLTag *tag, | ||
| 92 | XMLAttr *attr); | ||
| 93 | |||
| 94 | XMLTag *xml_copy_tag (XMLTag *tag); | ||
| 95 | XMLAttr *xml_copy_attr (XMLAttr *attr); | ||
| 96 | |||
| 97 | gint xml_unescape_str (gchar *str); | ||
| 98 | gchar *xml_escape_str (const gchar *str); | ||
| 99 | gint xml_file_put_escape_str (FILE *fp, | ||
| 100 | const gchar *str); | ||
| 101 | |||
| 102 | gint xml_file_put_xml_decl (FILE *fp); | ||
| 103 | gint xml_file_put_node (FILE *fp, | ||
| 104 | XMLNode *node); | ||
| 105 | |||
| 106 | void xml_free_node (XMLNode *node); | ||
| 107 | void xml_free_tree (GNode *node); | ||
| 108 | |||
| 109 | #endif /* __XML_H__ */ | ||
